• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
107 
108 private:
109     /**
110      *  Called when determining the output config to request to webp.
111      *  If the image does not have alpha, there is no need to premultiply.
112      *  If the caller wants unpremultiplied colors, that is respected.
113      */
shouldPremultiply() const114     bool shouldPremultiply() const {
115         return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors();
116     }
117 
118     bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
119 
120     SkAutoTDelete<SkStream> fInputStream;
121     int fOrigWidth;
122     int fOrigHeight;
123     int fHasAlpha;
124 
125     typedef SkImageDecoder INHERITED;
126 };
127 
128 //////////////////////////////////////////////////////////////////////////
129 
130 #ifdef TIME_DECODE
131 
132 #include "SkTime.h"
133 
134 class AutoTimeMillis {
135 public:
AutoTimeMillis(const char label[])136     AutoTimeMillis(const char label[]) :
137         fLabel(label) {
138         if (nullptr == fLabel) {
139             fLabel = "";
140         }
141         fNow = SkTime::GetMSecs();
142     }
~AutoTimeMillis()143     ~AutoTimeMillis() {
144         SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
145     }
146 private:
147     const char* fLabel;
148     SkMSec fNow;
149 };
150 
151 #endif
152 
153 ///////////////////////////////////////////////////////////////////////////////
154 
155 // This guy exists just to aid in debugging, as it allows debuggers to just
156 // set a break-point in one place to see all error exists.
print_webp_error(const SkBitmap & bm,const char msg[])157 static void print_webp_error(const SkBitmap& bm, const char msg[]) {
158     SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
159 }
160 
return_failure(const SkBitmap & bm,const char msg[])161 static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) {
162     print_webp_error(bm, msg);
163     return SkImageDecoder::kFailure; // must always return kFailure
164 }
165 
166 ///////////////////////////////////////////////////////////////////////////////
167 
webp_decode_mode(const SkBitmap * decodedBitmap,bool premultiply)168 static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) {
169     WEBP_CSP_MODE mode = MODE_LAST;
170     const SkColorType ct = decodedBitmap->colorType();
171 
172     if (ct == kN32_SkColorType) {
173         #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
174             mode = premultiply ? MODE_bgrA : MODE_BGRA;
175         #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
176             mode = premultiply ? MODE_rgbA : MODE_RGBA;
177         #else
178             #error "Skia uses BGRA or RGBA byte order"
179         #endif
180     } else if (ct == kARGB_4444_SkColorType) {
181         mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444;
182     } else if (ct == kRGB_565_SkColorType) {
183         mode = MODE_RGB_565;
184     }
185     SkASSERT(MODE_LAST != mode);
186     return mode;
187 }
188 
189 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively
190 // and decodes this block to appropriate color-space as per config object.
webp_idecode(SkStream * stream,WebPDecoderConfig * config)191 static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
192     WebPIDecoder* idec = WebPIDecode(nullptr, 0, config);
193     if (nullptr == idec) {
194         WebPFreeDecBuffer(&config->output);
195         return false;
196     }
197 
198     if (!stream->rewind()) {
199         SkDebugf("Failed to rewind webp stream!");
200         return false;
201     }
202     const size_t readBufferSize = stream->hasLength() ?
203             SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ;
204     SkAutoTMalloc<unsigned char> srcStorage(readBufferSize);
205     unsigned char* input = srcStorage.get();
206     if (nullptr == input) {
207         WebPIDelete(idec);
208         WebPFreeDecBuffer(&config->output);
209         return false;
210     }
211 
212     bool success = true;
213     VP8StatusCode status = VP8_STATUS_SUSPENDED;
214     do {
215         const size_t bytesRead = stream->read(input, readBufferSize);
216         if (0 == bytesRead) {
217             success = false;
218             break;
219         }
220 
221         status = WebPIAppend(idec, input, bytesRead);
222         if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) {
223             success = false;
224             break;
225         }
226     } while (VP8_STATUS_OK != status);
227     srcStorage.free();
228     WebPIDelete(idec);
229     WebPFreeDecBuffer(&config->output);
230 
231     return success;
232 }
233 
webp_get_config_resize(WebPDecoderConfig * config,SkBitmap * decodedBitmap,int width,int height,bool premultiply)234 static bool webp_get_config_resize(WebPDecoderConfig* config,
235                                    SkBitmap* decodedBitmap,
236                                    int width, int height, bool premultiply) {
237     WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply);
238     if (MODE_LAST == mode) {
239         return false;
240     }
241 
242     if (0 == WebPInitDecoderConfig(config)) {
243         return false;
244     }
245 
246     config->output.colorspace = mode;
247     config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
248     config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes();
249     config->output.u.RGBA.size = decodedBitmap->getSize();
250     config->output.is_external_memory = 1;
251 
252     if (width != decodedBitmap->width() || height != decodedBitmap->height()) {
253         config->options.use_scaling = 1;
254         config->options.scaled_width = decodedBitmap->width();
255         config->options.scaled_height = decodedBitmap->height();
256     }
257 
258     return true;
259 }
260 
setDecodeConfig(SkBitmap * decodedBitmap,int width,int height)261 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) {
262     SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha));
263 
264     // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
265     if (fHasAlpha) {
266         if (colorType != kARGB_4444_SkColorType) {
267             colorType = kN32_SkColorType;
268         }
269     } else {
270         if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) {
271             colorType = kN32_SkColorType;
272         }
273     }
274 
275     SkAlphaType alphaType = kOpaque_SkAlphaType;
276     if (SkToBool(fHasAlpha)) {
277         if (this->getRequireUnpremultipliedColors()) {
278             alphaType = kUnpremul_SkAlphaType;
279         } else {
280             alphaType = kPremul_SkAlphaType;
281         }
282     }
283     return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
284 }
285 
onDecode(SkStream * stream,SkBitmap * decodedBitmap,Mode mode)286 SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
287                                                     Mode mode) {
288 #ifdef TIME_DECODE
289     AutoTimeMillis atm("WEBP Decode");
290 #endif
291 
292     int origWidth, origHeight, hasAlpha;
293     if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
294         return kFailure;
295     }
296     this->fHasAlpha = hasAlpha;
297 
298     const int sampleSize = this->getSampleSize();
299     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
300     if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
301                          sampler.scaledHeight())) {
302         return kFailure;
303     }
304 
305     // If only bounds are requested, done
306     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
307         return kSuccess;
308     }
309 
310     if (!this->allocPixelRef(decodedBitmap, nullptr)) {
311         return return_failure(*decodedBitmap, "allocPixelRef");
312     }
313 
314     SkAutoLockPixels alp(*decodedBitmap);
315 
316     WebPDecoderConfig config;
317     if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
318                                 this->shouldPremultiply())) {
319         return kFailure;
320     }
321 
322     // Decode the WebP image data stream using WebP incremental decoding.
323     return webp_idecode(stream, &config) ? kSuccess : kFailure;
324 }
325 
326 ///////////////////////////////////////////////////////////////////////////////
327 
328 #include "SkUnPreMultiply.h"
329 
330 typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
331                                  const SkPMColor* SK_RESTRICT ctable);
332 
ARGB_8888_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)333 static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
334                              const SkPMColor*) {
335   const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
336   for (int i = 0; i < width; ++i) {
337       const uint32_t c = *src++;
338       rgb[0] = SkGetPackedR32(c);
339       rgb[1] = SkGetPackedG32(c);
340       rgb[2] = SkGetPackedB32(c);
341       rgb += 3;
342   }
343 }
344 
ARGB_8888_To_RGBA(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)345 static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
346                               const SkPMColor*) {
347   const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
348   const SkUnPreMultiply::Scale* SK_RESTRICT table =
349       SkUnPreMultiply::GetScaleTable();
350   for (int i = 0; i < width; ++i) {
351       const uint32_t c = *src++;
352       uint8_t a = SkGetPackedA32(c);
353       uint8_t r = SkGetPackedR32(c);
354       uint8_t g = SkGetPackedG32(c);
355       uint8_t b = SkGetPackedB32(c);
356       if (0 != a && 255 != a) {
357         SkUnPreMultiply::Scale scale = table[a];
358         r = SkUnPreMultiply::ApplyScale(scale, r);
359         g = SkUnPreMultiply::ApplyScale(scale, g);
360         b = SkUnPreMultiply::ApplyScale(scale, b);
361       }
362       rgb[0] = r;
363       rgb[1] = g;
364       rgb[2] = b;
365       rgb[3] = a;
366       rgb += 4;
367   }
368 }
369 
RGB_565_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)370 static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
371                            const SkPMColor*) {
372   const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
373   for (int i = 0; i < width; ++i) {
374       const uint16_t c = *src++;
375       rgb[0] = SkPacked16ToR32(c);
376       rgb[1] = SkPacked16ToG32(c);
377       rgb[2] = SkPacked16ToB32(c);
378       rgb += 3;
379   }
380 }
381 
ARGB_4444_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)382 static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
383                              const SkPMColor*) {
384   const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
385   for (int i = 0; i < width; ++i) {
386       const SkPMColor16 c = *src++;
387       rgb[0] = SkPacked4444ToR32(c);
388       rgb[1] = SkPacked4444ToG32(c);
389       rgb[2] = SkPacked4444ToB32(c);
390       rgb += 3;
391   }
392 }
393 
ARGB_4444_To_RGBA(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)394 static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
395                               const SkPMColor*) {
396   const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
397   const SkUnPreMultiply::Scale* SK_RESTRICT table =
398       SkUnPreMultiply::GetScaleTable();
399   for (int i = 0; i < width; ++i) {
400       const SkPMColor16 c = *src++;
401       uint8_t a = SkPacked4444ToA32(c);
402       uint8_t r = SkPacked4444ToR32(c);
403       uint8_t g = SkPacked4444ToG32(c);
404       uint8_t b = SkPacked4444ToB32(c);
405       if (0 != a && 255 != a) {
406         SkUnPreMultiply::Scale scale = table[a];
407         r = SkUnPreMultiply::ApplyScale(scale, r);
408         g = SkUnPreMultiply::ApplyScale(scale, g);
409         b = SkUnPreMultiply::ApplyScale(scale, b);
410       }
411       rgb[0] = r;
412       rgb[1] = g;
413       rgb[2] = b;
414       rgb[3] = a;
415       rgb += 4;
416   }
417 }
418 
Index8_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor * SK_RESTRICT ctable)419 static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
420                           const SkPMColor* SK_RESTRICT ctable) {
421   const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
422   for (int i = 0; i < width; ++i) {
423       const uint32_t c = ctable[*src++];
424       rgb[0] = SkGetPackedR32(c);
425       rgb[1] = SkGetPackedG32(c);
426       rgb[2] = SkGetPackedB32(c);
427       rgb += 3;
428   }
429 }
430 
ChooseImporter(SkColorType ct,bool hasAlpha,int * bpp)431 static ScanlineImporter ChooseImporter(SkColorType ct, bool  hasAlpha, int*  bpp) {
432     switch (ct) {
433         case kN32_SkColorType:
434             if (hasAlpha) {
435                 *bpp = 4;
436                 return ARGB_8888_To_RGBA;
437             } else {
438                 *bpp = 3;
439                 return ARGB_8888_To_RGB;
440             }
441         case kARGB_4444_SkColorType:
442             if (hasAlpha) {
443                 *bpp = 4;
444                 return ARGB_4444_To_RGBA;
445             } else {
446                 *bpp = 3;
447                 return ARGB_4444_To_RGB;
448             }
449         case kRGB_565_SkColorType:
450             *bpp = 3;
451             return RGB_565_To_RGB;
452         case kIndex_8_SkColorType:
453             *bpp = 3;
454             return Index8_To_RGB;
455         default:
456             return nullptr;
457     }
458 }
459 
stream_writer(const uint8_t * data,size_t data_size,const WebPPicture * const picture)460 static int stream_writer(const uint8_t* data, size_t data_size,
461                          const WebPPicture* const picture) {
462   SkWStream* const stream = (SkWStream*)picture->custom_ptr;
463   return stream->write(data, data_size) ? 1 : 0;
464 }
465 
466 class SkWEBPImageEncoder : public SkImageEncoder {
467 protected:
468     bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
469 
470 private:
471     typedef SkImageEncoder INHERITED;
472 };
473 
onEncode(SkWStream * stream,const SkBitmap & bm,int quality)474 bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
475                                   int quality) {
476     const bool hasAlpha = !bm.isOpaque();
477     int bpp = -1;
478     const ScanlineImporter scanline_import = ChooseImporter(bm.colorType(), hasAlpha, &bpp);
479     if (nullptr == scanline_import) {
480         return false;
481     }
482     if (-1 == bpp) {
483         return false;
484     }
485 
486     SkAutoLockPixels alp(bm);
487     if (nullptr == bm.getPixels()) {
488         return false;
489     }
490 
491     WebPConfig webp_config;
492     if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) {
493         return false;
494     }
495 
496     WebPPicture pic;
497     WebPPictureInit(&pic);
498     pic.width = bm.width();
499     pic.height = bm.height();
500     pic.writer = stream_writer;
501     pic.custom_ptr = (void*)stream;
502 
503     const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
504     const uint8_t* src = (uint8_t*)bm.getPixels();
505     const int rgbStride = pic.width * bpp;
506 
507     // Import (for each scanline) the bit-map image (in appropriate color-space)
508     // to RGB color space.
509     uint8_t* rgb = new uint8_t[rgbStride * pic.height];
510     for (int y = 0; y < pic.height; ++y) {
511         scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride,
512                         pic.width, colors);
513     }
514 
515     bool ok;
516     if (bpp == 3) {
517         ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride));
518     } else {
519         ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride));
520     }
521     delete[] rgb;
522 
523     ok = ok && WebPEncode(&webp_config, &pic);
524     WebPPictureFree(&pic);
525 
526     return ok;
527 }
528 
529 
530 ///////////////////////////////////////////////////////////////////////////////
531 DEFINE_DECODER_CREATOR(WEBPImageDecoder);
532 DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
533 ///////////////////////////////////////////////////////////////////////////////
534 
sk_libwebp_dfactory(SkStreamRewindable * stream)535 static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) {
536     int width, height, hasAlpha;
537     if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
538         return nullptr;
539     }
540 
541     // Magic matches, call decoder
542     return new SkWEBPImageDecoder;
543 }
544 
get_format_webp(SkStreamRewindable * stream)545 static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) {
546     int width, height, hasAlpha;
547     if (webp_parse_header(stream, &width, &height, &hasAlpha)) {
548         return SkImageDecoder::kWEBP_Format;
549     }
550     return SkImageDecoder::kUnknown_Format;
551 }
552 
sk_libwebp_efactory(SkImageEncoder::Type t)553 static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
554     return (SkImageEncoder::kWEBP_Type == t) ? new SkWEBPImageEncoder : nullptr;
555 }
556 
557 static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory);
558 static SkImageDecoder_FormatReg gFormatReg(get_format_webp);
559 static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory);
560