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