OLD | NEW |
---|---|
(Empty) | |
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 #include "SkTScopedPtr.h" | |
25 | |
26 // A WebP decoder only, on top of (subset of) libwebp | |
27 // For more information on WebP image format, and libwebp library, see: | |
28 // http://code.google.com/speed/webp/ | |
29 // http://www.webmproject.org/code/#libwebp_webp_image_decoder_library | |
30 // http://review.webmproject.org/gitweb?p=libwebp.git | |
31 | |
32 #include <stdio.h> | |
33 extern "C" { | |
34 // If moving libwebp out of skia source tree, path for webp headers must be | |
35 // updated accordingly. Here, we enforce using local copy in webp sub-directory. | |
36 #include "webp/decode.h" | |
37 #include "webp/encode.h" | |
38 } | |
39 | |
40 #ifdef ANDROID | |
41 #include <cutils/properties.h> | |
42 | |
43 // Key to lookup the size of memory buffer set in system property | |
44 static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; | |
45 #endif | |
46 | |
47 // this enables timing code to report milliseconds for a decode | |
48 //#define TIME_DECODE | |
49 | |
50 ////////////////////////////////////////////////////////////////////////// | |
51 ////////////////////////////////////////////////////////////////////////// | |
52 | |
53 // Define VP8 I/O on top of Skia stream | |
54 | |
55 ////////////////////////////////////////////////////////////////////////// | |
56 ////////////////////////////////////////////////////////////////////////// | |
57 | |
robertphillips
2013/03/13 13:55:14
k prefix? g prefix?
| |
58 static const size_t WEBP_VP8_HEADER_SIZE = 64; | |
59 static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); | |
60 | |
61 // Parse headers of RIFF container, and check for valid Webp (VP8) content. | |
62 static bool webp_parse_header(SkStream* stream, int* width, int* height, int* al pha) { | |
63 unsigned char buffer[WEBP_VP8_HEADER_SIZE]; | |
64 const uint32_t contentSize = stream->getLength(); | |
65 const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE); | |
66 const uint32_t read_bytes = | |
67 (contentSize < WEBP_VP8_HEADER_SIZE) ? contentSize : WEBP_VP8_HEADER _SIZE; | |
68 if (len != read_bytes) { | |
69 return false; // can't read enough | |
70 } | |
71 | |
72 WebPBitstreamFeatures features; | |
73 VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &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 Sk64 size; | |
84 size.setMul(*width, *height); | |
85 if (size.isNeg() || !size.is32()) { | |
86 return false; | |
87 } | |
88 // now check that if we are 4-bytes per pixel, we also don't overflow | |
89 if (size.get32() > (0x7FFFFFFF >> 2)) { | |
90 return false; | |
91 } | |
92 } | |
93 return true; | |
94 } | |
95 | |
96 class SkWEBPImageDecoder: public SkImageDecoder { | |
97 public: | |
98 SkWEBPImageDecoder() { | |
99 fInputStream = NULL; | |
100 fOrigWidth = 0; | |
101 fOrigHeight = 0; | |
102 fHasAlpha = 0; | |
103 } | |
104 virtual ~SkWEBPImageDecoder() { | |
105 SkSafeUnref(fInputStream); | |
106 } | |
107 | |
108 virtual Format getFormat() const SK_OVERRIDE { | |
109 return kWEBP_Format; | |
110 } | |
111 | |
112 protected: | |
113 virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_ OVERRIDE; | |
114 virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRI DE; | |
115 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; | |
116 | |
117 private: | |
robertphillips
2013/03/13 13:55:14
members first?
| |
118 bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); | |
119 SkStream* fInputStream; | |
120 int fOrigWidth; | |
121 int fOrigHeight; | |
robertphillips
2013/03/13 13:55:14
should fHasAlpha be a bool?
| |
122 int fHasAlpha; | |
123 | |
124 typedef SkImageDecoder INHERITED; | |
125 }; | |
126 | |
127 ////////////////////////////////////////////////////////////////////////// | |
128 | |
129 #ifdef TIME_DECODE | |
130 | |
131 #include "SkTime.h" | |
132 | |
133 class AutoTimeMillis { | |
134 public: | |
135 AutoTimeMillis(const char label[]) : | |
136 fLabel(label) { | |
137 if (NULL == fLabel) { | |
138 fLabel = ""; | |
139 } | |
140 fNow = SkTime::GetMSecs(); | |
141 } | |
142 ~AutoTimeMillis() { | |
143 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); | |
144 } | |
145 private: | |
146 const char* fLabel; | |
147 SkMSec fNow; | |
148 }; | |
149 | |
150 #endif | |
151 | |
152 /////////////////////////////////////////////////////////////////////////////// | |
153 | |
154 // This guy exists just to aid in debugging, as it allows debuggers to just | |
155 // set a break-point in one place to see all error exists. | |
156 static bool return_false(const SkBitmap& bm, const char msg[]) { | |
157 SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); | |
158 return false; // must always return false | |
159 } | |
160 | |
robertphillips
2013/03/13 13:55:14
bool hasAlpha?
| |
161 static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, int hasAlph a) { | |
162 WEBP_CSP_MODE mode = MODE_LAST; | |
163 SkBitmap::Config config = decodedBitmap->config(); | |
164 // For images that have alpha, choose appropriate color mode (MODE_rgbA, | |
165 // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency | |
166 // factor (alpha). | |
robertphillips
2013/03/13 13:55:14
constant on LHS
| |
167 if (config == SkBitmap::kARGB_8888_Config) { | |
168 mode = hasAlpha ? MODE_rgbA : MODE_RGBA; | |
169 } else if (config == SkBitmap::kARGB_4444_Config) { | |
170 mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444; | |
171 } else if (config == SkBitmap::kRGB_565_Config) { | |
172 mode = MODE_RGB_565; | |
173 } | |
174 SkASSERT(MODE_LAST != mode); | |
175 return mode; | |
176 } | |
177 | |
178 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively | |
179 // and decodes this block to appropriate color-space as per config object. | |
180 static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { | |
181 WebPIDecoder* idec = WebPIDecode(NULL, 0, config); | |
182 if (NULL == idec) { | |
183 WebPFreeDecBuffer(&config->output); | |
184 return false; | |
185 } | |
186 | |
187 stream->rewind(); | |
188 const uint32_t contentSize = stream->getLength(); | |
189 const uint32_t readBufferSize = (contentSize < WEBP_IDECODE_BUFFER_SZ) ? | |
robertphillips
2013/03/13 13:55:14
remove spaces?
| |
190 contentSize : WEBP_IDECODE_BUFFER_SZ; | |
191 SkAutoMalloc srcStorage(readBufferSize); | |
192 unsigned char* input = (uint8_t*)srcStorage.get(); | |
193 if (NULL == input) { | |
194 WebPIDelete(idec); | |
195 WebPFreeDecBuffer(&config->output); | |
196 return false; | |
197 } | |
198 | |
199 uint32_t bytesRemaining = contentSize; | |
200 while (bytesRemaining > 0) { | |
201 const uint32_t bytesToRead = (bytesRemaining < WEBP_IDECODE_BUFFER_SZ) ? | |
202 bytesRemaining : WEBP_IDECODE_BUFFER_SZ; | |
203 const size_t bytesRead = stream->read(input, bytesToRead); | |
204 if (0 == bytesRead) { | |
205 break; | |
206 } | |
207 | |
208 VP8StatusCode status = WebPIAppend(idec, input, bytesRead); | |
209 if (VP8_STATUS_OK == status || VP8_STATUS_SUSPENDED == status) { | |
210 bytesRemaining -= bytesRead; | |
211 } else { | |
212 break; | |
213 } | |
214 } | |
215 srcStorage.free(); | |
216 WebPIDelete(idec); | |
217 WebPFreeDecBuffer(&config->output); | |
218 | |
robertphillips
2013/03/13 13:55:14
return bytesRemaining <= 0;?
| |
219 if (bytesRemaining > 0) { | |
220 return false; | |
221 } else { | |
222 return true; | |
223 } | |
224 } | |
225 | |
226 static bool webp_get_config_resize(WebPDecoderConfig* config, | |
227 SkBitmap* decodedBitmap, | |
228 int width, int height, int hasAlpha) { | |
229 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha); | |
230 if (MODE_LAST == mode) { | |
231 return false; | |
232 } | |
233 | |
234 if (0 == WebPInitDecoderConfig(config)) { | |
235 return false; | |
236 } | |
237 | |
238 config->output.colorspace = mode; | |
239 config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); | |
240 config->output.u.RGBA.stride = decodedBitmap->rowBytes(); | |
241 config->output.u.RGBA.size = decodedBitmap->getSize(); | |
242 config->output.is_external_memory = 1; | |
243 | |
244 if (width != decodedBitmap->width() || height != decodedBitmap->height()) { | |
245 config->options.use_scaling = 1; | |
246 config->options.scaled_width = decodedBitmap->width(); | |
247 config->options.scaled_height = decodedBitmap->height(); | |
248 } | |
249 | |
250 return true; | |
251 } | |
252 | |
253 static bool webp_get_config_resize_crop(WebPDecoderConfig* config, | |
254 SkBitmap* decodedBitmap, | |
robertphillips
2013/03/13 13:55:14
bool hasAlpha?
| |
255 const SkIRect& region, int hasAlpha) { | |
256 | |
257 if (!webp_get_config_resize(config, decodedBitmap, region.width(), | |
258 region.height(), hasAlpha)) { | |
259 return false; | |
260 } | |
261 | |
262 config->options.use_cropping = 1; | |
263 config->options.crop_left = region.fLeft; | |
264 config->options.crop_top = region.fTop; | |
265 config->options.crop_width = region.width(); | |
266 config->options.crop_height = region.height(); | |
267 | |
268 return true; | |
269 } | |
270 | |
271 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, | |
272 int width, int height) { | |
273 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, fHasAlpha); | |
274 | |
275 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. | |
276 if (fHasAlpha) { | |
277 if (config != SkBitmap::kARGB_4444_Config) { | |
278 config = SkBitmap::kARGB_8888_Config; | |
279 } | |
280 } else { | |
281 if (config != SkBitmap::kRGB_565_Config && | |
282 config != SkBitmap::kARGB_4444_Config) { | |
283 config = SkBitmap::kARGB_8888_Config; | |
284 } | |
285 } | |
286 | |
287 if (!this->chooseFromOneChoice(config, width, height)) { | |
288 return false; | |
289 } | |
290 | |
291 decodedBitmap->setConfig(config, width, height, 0); | |
292 | |
293 decodedBitmap->setIsOpaque(!fHasAlpha); | |
294 | |
295 return true; | |
296 } | |
297 | |
298 bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, | |
299 int *width, int *height) { | |
300 int origWidth, origHeight, hasAlpha; | |
301 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { | |
302 return false; | |
303 } | |
304 | |
305 stream->rewind(); | |
306 *width = origWidth; | |
307 *height = origHeight; | |
308 | |
309 SkRefCnt_SafeAssign(this->fInputStream, stream); | |
310 this->fOrigWidth = origWidth; | |
311 this->fOrigHeight = origHeight; | |
312 this->fHasAlpha = hasAlpha; | |
313 | |
314 return true; | |
315 } | |
316 | |
317 static bool is_config_compatible(const SkBitmap& bitmap) { | |
318 SkBitmap::Config config = bitmap.config(); | |
319 return config == SkBitmap::kARGB_4444_Config || | |
320 config == SkBitmap::kRGB_565_Config || | |
321 config == SkBitmap::kARGB_8888_Config; | |
322 } | |
323 | |
324 bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, | |
325 const SkIRect& region) { | |
326 SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight); | |
327 | |
328 if (!rect.intersect(region)) { | |
329 // If the requested region is entirely outsides the image, return false | |
330 return false; | |
331 } | |
332 | |
333 const int sampleSize = this->getSampleSize(); | |
334 SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); | |
335 const int width = sampler.scaledWidth(); | |
336 const int height = sampler.scaledHeight(); | |
337 | |
338 // The image can be decoded directly to decodedBitmap if | |
339 // 1. the region is within the image range | |
340 // 2. bitmap's config is compatible | |
341 // 3. bitmap's size is same as the required region (after sampled) | |
342 bool directDecode = (rect == region) && | |
343 (decodedBitmap->isNull() || | |
344 (is_config_compatible(*decodedBitmap) && | |
345 (decodedBitmap->width() == width) && | |
346 (decodedBitmap->height() == height))); | |
347 SkTScopedPtr<SkBitmap> adb; | |
348 SkBitmap *bitmap = decodedBitmap; | |
349 | |
350 if (!directDecode) { | |
351 // allocates a temp bitmap | |
352 bitmap = new SkBitmap; | |
353 adb.reset(bitmap); | |
354 } | |
355 | |
356 if (bitmap->isNull()) { | |
357 if (!setDecodeConfig(bitmap, width, height)) { | |
358 return false; | |
359 } | |
360 // alloc from native heap if it is a temp bitmap. (prevent GC) | |
361 bool allocResult = (bitmap == decodedBitmap) | |
362 ? allocPixelRef(bitmap, NULL) | |
363 : bitmap->allocPixels(); | |
364 if (!allocResult) { | |
365 return return_false(*decodedBitmap, "allocPixelRef"); | |
366 } | |
367 } else { | |
368 // This is also called in setDecodeConfig in above block. | |
369 // i.e., when bitmap->isNull() is true. | |
370 if (!chooseFromOneChoice(bitmap->config(), width, height)) { | |
371 return false; | |
372 } | |
373 } | |
374 | |
375 SkAutoLockPixels alp(*bitmap); | |
376 WebPDecoderConfig config; | |
377 if (!webp_get_config_resize_crop(&config, bitmap, rect, fHasAlpha)) { | |
378 return false; | |
379 } | |
380 | |
381 // Decode the WebP image data stream using WebP incremental decoding for | |
382 // the specified cropped image-region. | |
383 if (!webp_idecode(this->fInputStream, &config)) { | |
384 return false; | |
385 } | |
386 | |
387 if (!directDecode) { | |
388 cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), | |
389 region.width(), region.height(), rect.x(), rect.y()); | |
390 } | |
391 return true; | |
392 } | |
393 | |
394 bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, | |
395 Mode mode) { | |
396 #ifdef TIME_DECODE | |
397 AutoTimeMillis atm("WEBP Decode"); | |
398 #endif | |
399 | |
400 int origWidth, origHeight, hasAlpha; | |
401 if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { | |
402 return false; | |
403 } | |
robertphillips
2013/03/13 13:55:14
remove this->?
| |
404 this->fHasAlpha = hasAlpha; | |
405 | |
406 const int sampleSize = this->getSampleSize(); | |
407 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); | |
408 | |
409 // If only bounds are requested, done | |
410 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | |
robertphillips
2013/03/13 13:55:14
just "return setDecodeConfig"?
| |
411 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), | |
412 sampler.scaledHeight())) { | |
413 return false; | |
414 } | |
415 return true; | |
416 } | |
417 | |
418 // No Bitmap reuse supported for this format | |
419 if (!decodedBitmap->isNull()) { | |
420 return false; | |
421 } | |
422 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), | |
423 sampler.scaledHeight())) { | |
424 return false; | |
425 } | |
426 | |
427 if (!this->allocPixelRef(decodedBitmap, NULL)) { | |
428 return return_false(*decodedBitmap, "allocPixelRef"); | |
429 } | |
430 | |
431 SkAutoLockPixels alp(*decodedBitmap); | |
432 | |
433 WebPDecoderConfig config; | |
434 if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, | |
435 hasAlpha)) { | |
436 return false; | |
437 } | |
438 | |
439 // Decode the WebP image data stream using WebP incremental decoding. | |
440 return webp_idecode(stream, &config); | |
441 } | |
442 | |
443 /////////////////////////////////////////////////////////////////////////////// | |
444 | |
445 typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, | |
446 const SkPMColor* SK_RESTRICT ctable); | |
447 | |
448 static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | |
449 const SkPMColor*) { | |
450 const uint32_t* SK_RESTRICT src = (const uint32_t*)in; | |
451 for (int i = 0; i < width; ++i) { | |
452 const uint32_t c = *src++; | |
453 rgb[0] = SkGetPackedR32(c); | |
454 rgb[1] = SkGetPackedG32(c); | |
455 rgb[2] = SkGetPackedB32(c); | |
456 rgb += 3; | |
457 } | |
458 } | |
459 | |
460 static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | |
461 const SkPMColor*) { | |
462 const uint16_t* SK_RESTRICT src = (const uint16_t*)in; | |
463 for (int i = 0; i < width; ++i) { | |
464 const uint16_t c = *src++; | |
465 rgb[0] = SkPacked16ToR32(c); | |
466 rgb[1] = SkPacked16ToG32(c); | |
467 rgb[2] = SkPacked16ToB32(c); | |
468 rgb += 3; | |
469 } | |
470 } | |
471 | |
472 static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | |
473 const SkPMColor*) { | |
474 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; | |
475 for (int i = 0; i < width; ++i) { | |
476 const SkPMColor16 c = *src++; | |
477 rgb[0] = SkPacked4444ToR32(c); | |
478 rgb[1] = SkPacked4444ToG32(c); | |
479 rgb[2] = SkPacked4444ToB32(c); | |
480 rgb += 3; | |
481 } | |
482 } | |
483 | |
484 static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | |
485 const SkPMColor* SK_RESTRICT ctable) { | |
486 const uint8_t* SK_RESTRICT src = (const uint8_t*)in; | |
487 for (int i = 0; i < width; ++i) { | |
488 const uint32_t c = ctable[*src++]; | |
489 rgb[0] = SkGetPackedR32(c); | |
490 rgb[1] = SkGetPackedG32(c); | |
491 rgb[2] = SkGetPackedB32(c); | |
492 rgb += 3; | |
493 } | |
494 } | |
495 | |
496 static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) { | |
497 switch (config) { | |
498 case SkBitmap::kARGB_8888_Config: | |
499 return ARGB_8888_To_RGB; | |
500 case SkBitmap::kRGB_565_Config: | |
501 return RGB_565_To_RGB; | |
502 case SkBitmap::kARGB_4444_Config: | |
503 return ARGB_4444_To_RGB; | |
504 case SkBitmap::kIndex8_Config: | |
505 return Index8_To_RGB; | |
506 default: | |
507 return NULL; | |
508 } | |
509 } | |
510 | |
robertphillips
2013/03/13 13:55:14
Should this return a bool?
| |
511 static int stream_writer(const uint8_t* data, size_t data_size, | |
512 const WebPPicture* const picture) { | |
513 SkWStream* const stream = (SkWStream*)picture->custom_ptr; | |
514 return stream->write(data, data_size) ? 1 : 0; | |
515 } | |
516 | |
517 class SkWEBPImageEncoder : public SkImageEncoder { | |
518 protected: | |
519 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK _OVERRIDE; | |
520 | |
521 private: | |
522 typedef SkImageEncoder INHERITED; | |
523 }; | |
524 | |
525 bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, | |
526 int quality) { | |
527 const SkBitmap::Config config = bm.getConfig(); | |
robertphillips
2013/03/13 13:55:14
scanlineImport?
| |
528 const ScanlineImporter scanline_import = ChooseImporter(config); | |
529 if (NULL == scanline_import) { | |
530 return false; | |
531 } | |
532 | |
533 SkAutoLockPixels alp(bm); | |
534 SkAutoLockColors ctLocker; | |
535 if (NULL == bm.getPixels()) { | |
536 return false; | |
537 } | |
538 | |
robertphillips
2013/03/13 13:55:14
webpConfig?
| |
539 WebPConfig webp_config; | |
540 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) { | |
541 return false; | |
542 } | |
543 | |
544 WebPPicture pic; | |
545 WebPPictureInit(&pic); | |
546 pic.width = bm.width(); | |
547 pic.height = bm.height(); | |
548 pic.writer = stream_writer; | |
549 pic.custom_ptr = (void*)stream; | |
550 | |
551 const SkPMColor* colors = ctLocker.lockColors(bm); | |
552 const uint8_t* src = (uint8_t*)bm.getPixels(); | |
553 const int rgbStride = pic.width * 3; | |
554 | |
555 // Import (for each scanline) the bit-map image (in appropriate color-space) | |
556 // to RGB color space. | |
557 uint8_t* rgb = new uint8_t[rgbStride * pic.height]; | |
558 for (int y = 0; y < pic.height; ++y) { | |
559 scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, | |
560 pic.width, colors); | |
561 } | |
562 | |
563 bool ok = WebPPictureImportRGB(&pic, rgb, rgbStride); | |
564 delete[] rgb; | |
565 | |
566 ok = ok && WebPEncode(&webp_config, &pic); | |
567 WebPPictureFree(&pic); | |
568 | |
569 return ok; | |
570 } | |
571 | |
572 | |
573 /////////////////////////////////////////////////////////////////////////////// | |
574 DEFINE_DECODER_CREATOR(WEBPImageDecoder); | |
575 DEFINE_ENCODER_CREATOR(WEBPImageEncoder); | |
576 /////////////////////////////////////////////////////////////////////////////// | |
577 | |
578 #include "SkTRegistry.h" | |
579 | |
580 static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) { | |
581 int width, height, hasAlpha; | |
582 if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { | |
583 return NULL; | |
584 } | |
585 | |
586 // Magic matches, call decoder | |
587 return SkNEW(SkWEBPImageDecoder); | |
588 } | |
589 | |
590 static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { | |
591 return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NUL L; | |
592 } | |
593 | |
594 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory); | |
595 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efact ory); | |
OLD | NEW |