1 /*
2  * Copyright 2010, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkImageDecoder.h"
18 #include "SkImageEncoder.h"
19 #include "SkColorPriv.h"
20 #include "SkScaledBitmapSampler.h"
21 #include "SkStream.h"
22 #include "SkTemplates.h"
23 #include "SkUtils.h"
24 
25 // A WebP decoder only, on top of (subset of) libwebp
26 // For more information on WebP image format, and libwebp library, see:
27 //   http://code.google.com/speed/webp/
28 //   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
29 //   http://review.webmproject.org/gitweb?p=libwebp.git
30 
31 #include <stdio.h>
32 extern "C" {
33 // If moving libwebp out of skia source tree, path for webp headers must be
34 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
35 #include "webp/decode.h"
36 #include "webp/encode.h"
37 }
38 
39 // this enables timing code to report milliseconds for a decode
40 //#define TIME_DECODE
41 
42 //////////////////////////////////////////////////////////////////////////
43 //////////////////////////////////////////////////////////////////////////
44 
45 // Define VP8 I/O on top of Skia stream
46 
47 //////////////////////////////////////////////////////////////////////////
48 //////////////////////////////////////////////////////////////////////////
49 
50 static const size_t WEBP_VP8_HEADER_SIZE = 64;
51 static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
52 
53 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
webp_parse_header(SkStream * stream,int * width,int * height,int * alpha)54 static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) {
55     unsigned char buffer[WEBP_VP8_HEADER_SIZE];
56     size_t bytesToRead = WEBP_VP8_HEADER_SIZE;
57     size_t totalBytesRead = 0;
58     do {
59         unsigned char* dst = buffer + totalBytesRead;
60         const size_t bytesRead = stream->read(dst, bytesToRead);
61         if (0 == bytesRead) {
62             SkASSERT(stream->isAtEnd());
63             break;
64         }
65         bytesToRead -= bytesRead;
66         totalBytesRead += bytesRead;
67         SkASSERT(bytesToRead + totalBytesRead == WEBP_VP8_HEADER_SIZE);
68     } while (!stream->isAtEnd() && bytesToRead > 0);
69 
70     WebPBitstreamFeatures features;
71     VP8StatusCode status = WebPGetFeatures(buffer, totalBytesRead, &features);
72     if (VP8_STATUS_OK != status) {
73         return false; // Invalid WebP file.
74     }
75     *width = features.width;
76     *height = features.height;
77     *alpha = features.has_alpha;
78 
79     // sanity check for image size that's about to be decoded.
80     {
81         int64_t size = sk_64_mul(*width, *height);
82         if (!sk_64_isS32(size)) {
83             return false;
84         }
85         // now check that if we are 4-bytes per pixel, we also don't overflow
86         if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
87             return false;
88         }
89     }
90     return true;
91 }
92 
93 class SkWEBPImageDecoder: public SkImageDecoder {
94 public:
SkWEBPImageDecoder()95     SkWEBPImageDecoder() {
96         fOrigWidth = 0;
97         fOrigHeight = 0;
98         fHasAlpha = 0;
99     }
100 
getFormat() const101     Format getFormat() const override {
102         return kWEBP_Format;
103     }
104 
105 protected:
106     bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
107     bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
108     Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
109 
110 private:
111     /**
112      *  Called when determining the output config to request to webp.
113      *  If the image does not have alpha, there is no need to premultiply.
114      *  If the caller wants unpremultiplied colors, that is respected.
115      */
shouldPremultiply() const116     bool shouldPremultiply() const {
117         return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors();
118     }
119 
120     bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
121 
122     SkAutoTDelete<SkStream> fInputStream;
123     int fOrigWidth;
124     int fOrigHeight;
125     int fHasAlpha;
126 
127     typedef SkImageDecoder INHERITED;
128 };
129 
130 //////////////////////////////////////////////////////////////////////////
131 
132 #ifdef TIME_DECODE
133 
134 #include "SkTime.h"
135 
136 class AutoTimeMillis {
137 public:
AutoTimeMillis(const char label[])138     AutoTimeMillis(const char label[]) :
139         fLabel(label) {
140         if (NULL == fLabel) {
141             fLabel = "";
142         }
143         fNow = SkTime::GetMSecs();
144     }
~AutoTimeMillis()145     ~AutoTimeMillis() {
146         SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
147     }
148 private:
149     const char* fLabel;
150     SkMSec fNow;
151 };
152 
153 #endif
154 
155 ///////////////////////////////////////////////////////////////////////////////
156 
157 // This guy exists just to aid in debugging, as it allows debuggers to just
158 // set a break-point in one place to see all error exists.
print_webp_error(const SkBitmap & bm,const char msg[])159 static void print_webp_error(const SkBitmap& bm, const char msg[]) {
160     SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
161 }
162 
return_false(const SkBitmap & bm,const char msg[])163 static bool return_false(const SkBitmap& bm, const char msg[]) {
164     print_webp_error(bm, msg);
165     return false; // must always return false
166 }
167 
return_failure(const SkBitmap & bm,const char msg[])168 static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) {
169     print_webp_error(bm, msg);
170     return SkImageDecoder::kFailure; // must always return kFailure
171 }
172 
173 ///////////////////////////////////////////////////////////////////////////////
174 
webp_decode_mode(const SkBitmap * decodedBitmap,bool premultiply)175 static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) {
176     WEBP_CSP_MODE mode = MODE_LAST;
177     const SkColorType ct = decodedBitmap->colorType();
178 
179     if (ct == kN32_SkColorType) {
180         #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
181             mode = premultiply ? MODE_bgrA : MODE_BGRA;
182         #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
183             mode = premultiply ? MODE_rgbA : MODE_RGBA;
184         #else
185             #error "Skia uses BGRA or RGBA byte order"
186         #endif
187     } else if (ct == kARGB_4444_SkColorType) {
188         mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444;
189     } else if (ct == kRGB_565_SkColorType) {
190         mode = MODE_RGB_565;
191     }
192     SkASSERT(MODE_LAST != mode);
193     return mode;
194 }
195 
196 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively
197 // and decodes this block to appropriate color-space as per config object.
webp_idecode(SkStream * stream,WebPDecoderConfig * config)198 static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
199     WebPIDecoder* idec = WebPIDecode(NULL, 0, config);
200     if (NULL == idec) {
201         WebPFreeDecBuffer(&config->output);
202         return false;
203     }
204 
205     if (!stream->rewind()) {
206         SkDebugf("Failed to rewind webp stream!");
207         return false;
208     }
209     const size_t readBufferSize = stream->hasLength() ?
210             SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ;
211     SkAutoMalloc srcStorage(readBufferSize);
212     unsigned char* input = (uint8_t*)srcStorage.get();
213     if (NULL == input) {
214         WebPIDelete(idec);
215         WebPFreeDecBuffer(&config->output);
216         return false;
217     }
218 
219     bool success = true;
220     VP8StatusCode status = VP8_STATUS_SUSPENDED;
221     do {
222         const size_t bytesRead = stream->read(input, readBufferSize);
223         if (0 == bytesRead) {
224             success = false;
225             break;
226         }
227 
228         status = WebPIAppend(idec, input, bytesRead);
229         if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) {
230             success = false;
231             break;
232         }
233     } while (VP8_STATUS_OK != status);
234     srcStorage.free();
235     WebPIDelete(idec);
236     WebPFreeDecBuffer(&config->output);
237 
238     return success;
239 }
240 
webp_get_config_resize(WebPDecoderConfig * config,SkBitmap * decodedBitmap,int width,int height,bool premultiply)241 static bool webp_get_config_resize(WebPDecoderConfig* config,
242                                    SkBitmap* decodedBitmap,
243                                    int width, int height, bool premultiply) {
244     WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply);
245     if (MODE_LAST == mode) {
246         return false;
247     }
248 
249     if (0 == WebPInitDecoderConfig(config)) {
250         return false;
251     }
252 
253     config->output.colorspace = mode;
254     config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
255     config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes();
256     config->output.u.RGBA.size = decodedBitmap->getSize();
257     config->output.is_external_memory = 1;
258 
259     if (width != decodedBitmap->width() || height != decodedBitmap->height()) {
260         config->options.use_scaling = 1;
261         config->options.scaled_width = decodedBitmap->width();
262         config->options.scaled_height = decodedBitmap->height();
263     }
264 
265     return true;
266 }
267 
webp_get_config_resize_crop(WebPDecoderConfig * config,SkBitmap * decodedBitmap,const SkIRect & region,bool premultiply)268 static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
269                                         SkBitmap* decodedBitmap,
270                                         const SkIRect& region, bool premultiply) {
271 
272     if (!webp_get_config_resize(config, decodedBitmap, region.width(),
273                                 region.height(), premultiply)) {
274       return false;
275     }
276 
277     config->options.use_cropping = 1;
278     config->options.crop_left = region.fLeft;
279     config->options.crop_top = region.fTop;
280     config->options.crop_width = region.width();
281     config->options.crop_height = region.height();
282 
283     return true;
284 }
285 
setDecodeConfig(SkBitmap * decodedBitmap,int width,int height)286 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) {
287     SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha));
288 
289     // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
290     if (fHasAlpha) {
291         if (colorType != kARGB_4444_SkColorType) {
292             colorType = kN32_SkColorType;
293         }
294     } else {
295         if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) {
296             colorType = kN32_SkColorType;
297         }
298     }
299 
300     SkAlphaType alphaType = kOpaque_SkAlphaType;
301     if (SkToBool(fHasAlpha)) {
302         if (this->getRequireUnpremultipliedColors()) {
303             alphaType = kUnpremul_SkAlphaType;
304         } else {
305             alphaType = kPremul_SkAlphaType;
306         }
307     }
308     return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
309 }
310 
onBuildTileIndex(SkStreamRewindable * stream,int * width,int * height)311 bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream,
312                                           int *width, int *height) {
313     SkAutoTDelete<SkStreamRewindable> streamDeleter(stream);
314     int origWidth, origHeight, hasAlpha;
315     if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
316         return false;
317     }
318 
319     if (!stream->rewind()) {
320         SkDebugf("Failed to rewind webp stream!");
321         return false;
322     }
323 
324     *width = origWidth;
325     *height = origHeight;
326 
327     this->fInputStream.reset(streamDeleter.detach());
328     this->fOrigWidth = origWidth;
329     this->fOrigHeight = origHeight;
330     this->fHasAlpha = hasAlpha;
331 
332     return true;
333 }
334 
is_config_compatible(const SkBitmap & bitmap)335 static bool is_config_compatible(const SkBitmap& bitmap) {
336     const SkColorType ct = bitmap.colorType();
337     return ct == kARGB_4444_SkColorType || ct == kRGB_565_SkColorType || ct == kN32_SkColorType;
338 }
339 
onDecodeSubset(SkBitmap * decodedBitmap,const SkIRect & region)340 bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap,
341                                         const SkIRect& region) {
342     SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight);
343 
344     if (!rect.intersect(region)) {
345         // If the requested region is entirely outsides the image, return false
346         return false;
347     }
348 
349     const int sampleSize = this->getSampleSize();
350     SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
351     const int width = sampler.scaledWidth();
352     const int height = sampler.scaledHeight();
353 
354     // The image can be decoded directly to decodedBitmap if
355     //   1. the region is within the image range
356     //   2. bitmap's config is compatible
357     //   3. bitmap's size is same as the required region (after sampled)
358     bool directDecode = (rect == region) &&
359                         (decodedBitmap->isNull() ||
360                          (is_config_compatible(*decodedBitmap) &&
361                          (decodedBitmap->width() == width) &&
362                          (decodedBitmap->height() == height)));
363 
364     SkBitmap tmpBitmap;
365     SkBitmap *bitmap = decodedBitmap;
366 
367     if (!directDecode) {
368         bitmap = &tmpBitmap;
369     }
370 
371     if (bitmap->isNull()) {
372         if (!setDecodeConfig(bitmap, width, height)) {
373             return false;
374         }
375         // alloc from native heap if it is a temp bitmap. (prevent GC)
376         bool allocResult = (bitmap == decodedBitmap)
377                                ? allocPixelRef(bitmap, NULL)
378                                : bitmap->tryAllocPixels();
379         if (!allocResult) {
380             return return_false(*decodedBitmap, "allocPixelRef");
381         }
382     }
383 
384     SkAutoLockPixels alp(*bitmap);
385     WebPDecoderConfig config;
386     if (!webp_get_config_resize_crop(&config, bitmap, rect,
387                                      this->shouldPremultiply())) {
388         return false;
389     }
390 
391     // Decode the WebP image data stream using WebP incremental decoding for
392     // the specified cropped image-region.
393     if (!webp_idecode(this->fInputStream, &config)) {
394         return false;
395     }
396 
397     if (!directDecode) {
398         return cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
399                           region.width(), region.height(), rect.x(), rect.y());
400     }
401     return true;
402 }
403 
onDecode(SkStream * stream,SkBitmap * decodedBitmap,Mode mode)404 SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
405                                                     Mode mode) {
406 #ifdef TIME_DECODE
407     AutoTimeMillis atm("WEBP Decode");
408 #endif
409 
410     int origWidth, origHeight, hasAlpha;
411     if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
412         return kFailure;
413     }
414     this->fHasAlpha = hasAlpha;
415 
416     const int sampleSize = this->getSampleSize();
417     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
418     if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
419                          sampler.scaledHeight())) {
420         return kFailure;
421     }
422 
423     // If only bounds are requested, done
424     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
425         return kSuccess;
426     }
427 
428     if (!this->allocPixelRef(decodedBitmap, NULL)) {
429         return return_failure(*decodedBitmap, "allocPixelRef");
430     }
431 
432     SkAutoLockPixels alp(*decodedBitmap);
433 
434     WebPDecoderConfig config;
435     if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
436                                 this->shouldPremultiply())) {
437         return kFailure;
438     }
439 
440     // Decode the WebP image data stream using WebP incremental decoding.
441     return webp_idecode(stream, &config) ? kSuccess : kFailure;
442 }
443 
444 ///////////////////////////////////////////////////////////////////////////////
445 
446 #include "SkUnPreMultiply.h"
447 
448 typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
449                                  const SkPMColor* SK_RESTRICT ctable);
450 
ARGB_8888_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)451 static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
452                              const SkPMColor*) {
453   const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
454   for (int i = 0; i < width; ++i) {
455       const uint32_t c = *src++;
456       rgb[0] = SkGetPackedR32(c);
457       rgb[1] = SkGetPackedG32(c);
458       rgb[2] = SkGetPackedB32(c);
459       rgb += 3;
460   }
461 }
462 
ARGB_8888_To_RGBA(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)463 static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
464                               const SkPMColor*) {
465   const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
466   const SkUnPreMultiply::Scale* SK_RESTRICT table =
467       SkUnPreMultiply::GetScaleTable();
468   for (int i = 0; i < width; ++i) {
469       const uint32_t c = *src++;
470       uint8_t a = SkGetPackedA32(c);
471       uint8_t r = SkGetPackedR32(c);
472       uint8_t g = SkGetPackedG32(c);
473       uint8_t b = SkGetPackedB32(c);
474       if (0 != a && 255 != a) {
475         SkUnPreMultiply::Scale scale = table[a];
476         r = SkUnPreMultiply::ApplyScale(scale, r);
477         g = SkUnPreMultiply::ApplyScale(scale, g);
478         b = SkUnPreMultiply::ApplyScale(scale, b);
479       }
480       rgb[0] = r;
481       rgb[1] = g;
482       rgb[2] = b;
483       rgb[3] = a;
484       rgb += 4;
485   }
486 }
487 
RGB_565_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)488 static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
489                            const SkPMColor*) {
490   const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
491   for (int i = 0; i < width; ++i) {
492       const uint16_t c = *src++;
493       rgb[0] = SkPacked16ToR32(c);
494       rgb[1] = SkPacked16ToG32(c);
495       rgb[2] = SkPacked16ToB32(c);
496       rgb += 3;
497   }
498 }
499 
ARGB_4444_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)500 static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
501                              const SkPMColor*) {
502   const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
503   for (int i = 0; i < width; ++i) {
504       const SkPMColor16 c = *src++;
505       rgb[0] = SkPacked4444ToR32(c);
506       rgb[1] = SkPacked4444ToG32(c);
507       rgb[2] = SkPacked4444ToB32(c);
508       rgb += 3;
509   }
510 }
511 
ARGB_4444_To_RGBA(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)512 static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
513                               const SkPMColor*) {
514   const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
515   const SkUnPreMultiply::Scale* SK_RESTRICT table =
516       SkUnPreMultiply::GetScaleTable();
517   for (int i = 0; i < width; ++i) {
518       const SkPMColor16 c = *src++;
519       uint8_t a = SkPacked4444ToA32(c);
520       uint8_t r = SkPacked4444ToR32(c);
521       uint8_t g = SkPacked4444ToG32(c);
522       uint8_t b = SkPacked4444ToB32(c);
523       if (0 != a && 255 != a) {
524         SkUnPreMultiply::Scale scale = table[a];
525         r = SkUnPreMultiply::ApplyScale(scale, r);
526         g = SkUnPreMultiply::ApplyScale(scale, g);
527         b = SkUnPreMultiply::ApplyScale(scale, b);
528       }
529       rgb[0] = r;
530       rgb[1] = g;
531       rgb[2] = b;
532       rgb[3] = a;
533       rgb += 4;
534   }
535 }
536 
Index8_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor * SK_RESTRICT ctable)537 static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
538                           const SkPMColor* SK_RESTRICT ctable) {
539   const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
540   for (int i = 0; i < width; ++i) {
541       const uint32_t c = ctable[*src++];
542       rgb[0] = SkGetPackedR32(c);
543       rgb[1] = SkGetPackedG32(c);
544       rgb[2] = SkGetPackedB32(c);
545       rgb += 3;
546   }
547 }
548 
ChooseImporter(SkColorType ct,bool hasAlpha,int * bpp)549 static ScanlineImporter ChooseImporter(SkColorType ct, bool  hasAlpha, int*  bpp) {
550     switch (ct) {
551         case kN32_SkColorType:
552             if (hasAlpha) {
553                 *bpp = 4;
554                 return ARGB_8888_To_RGBA;
555             } else {
556                 *bpp = 3;
557                 return ARGB_8888_To_RGB;
558             }
559         case kARGB_4444_SkColorType:
560             if (hasAlpha) {
561                 *bpp = 4;
562                 return ARGB_4444_To_RGBA;
563             } else {
564                 *bpp = 3;
565                 return ARGB_4444_To_RGB;
566             }
567         case kRGB_565_SkColorType:
568             *bpp = 3;
569             return RGB_565_To_RGB;
570         case kIndex_8_SkColorType:
571             *bpp = 3;
572             return Index8_To_RGB;
573         default:
574             return NULL;
575     }
576 }
577 
stream_writer(const uint8_t * data,size_t data_size,const WebPPicture * const picture)578 static int stream_writer(const uint8_t* data, size_t data_size,
579                          const WebPPicture* const picture) {
580   SkWStream* const stream = (SkWStream*)picture->custom_ptr;
581   return stream->write(data, data_size) ? 1 : 0;
582 }
583 
584 class SkWEBPImageEncoder : public SkImageEncoder {
585 protected:
586     bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
587 
588 private:
589     typedef SkImageEncoder INHERITED;
590 };
591 
onEncode(SkWStream * stream,const SkBitmap & bm,int quality)592 bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
593                                   int quality) {
594     const bool hasAlpha = !bm.isOpaque();
595     int bpp = -1;
596     const ScanlineImporter scanline_import = ChooseImporter(bm.colorType(), hasAlpha, &bpp);
597     if (NULL == scanline_import) {
598         return false;
599     }
600     if (-1 == bpp) {
601         return false;
602     }
603 
604     SkAutoLockPixels alp(bm);
605     if (NULL == bm.getPixels()) {
606         return false;
607     }
608 
609     WebPConfig webp_config;
610     if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) {
611         return false;
612     }
613 
614     WebPPicture pic;
615     WebPPictureInit(&pic);
616     pic.width = bm.width();
617     pic.height = bm.height();
618     pic.writer = stream_writer;
619     pic.custom_ptr = (void*)stream;
620 
621     const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
622     const uint8_t* src = (uint8_t*)bm.getPixels();
623     const int rgbStride = pic.width * bpp;
624 
625     // Import (for each scanline) the bit-map image (in appropriate color-space)
626     // to RGB color space.
627     uint8_t* rgb = new uint8_t[rgbStride * pic.height];
628     for (int y = 0; y < pic.height; ++y) {
629         scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride,
630                         pic.width, colors);
631     }
632 
633     bool ok;
634     if (bpp == 3) {
635         ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride));
636     } else {
637         ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride));
638     }
639     delete[] rgb;
640 
641     ok = ok && WebPEncode(&webp_config, &pic);
642     WebPPictureFree(&pic);
643 
644     return ok;
645 }
646 
647 
648 ///////////////////////////////////////////////////////////////////////////////
649 DEFINE_DECODER_CREATOR(WEBPImageDecoder);
650 DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
651 ///////////////////////////////////////////////////////////////////////////////
652 
sk_libwebp_dfactory(SkStreamRewindable * stream)653 static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) {
654     int width, height, hasAlpha;
655     if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
656         return NULL;
657     }
658 
659     // Magic matches, call decoder
660     return SkNEW(SkWEBPImageDecoder);
661 }
662 
get_format_webp(SkStreamRewindable * stream)663 static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) {
664     int width, height, hasAlpha;
665     if (webp_parse_header(stream, &width, &height, &hasAlpha)) {
666         return SkImageDecoder::kWEBP_Format;
667     }
668     return SkImageDecoder::kUnknown_Format;
669 }
670 
sk_libwebp_efactory(SkImageEncoder::Type t)671 static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
672       return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
673 }
674 
675 static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory);
676 static SkImageDecoder_FormatReg gFormatReg(get_format_webp);
677 static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory);
678