SkImageDecoder_libjpeg.cpp revision 59b740028752e741e27ce7e7652a59ae56b5450f
1 2/* 3 * Copyright 2007 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkImageDecoder.h" 11#include "SkImageEncoder.h" 12#include "SkJpegUtility.h" 13#include "SkColorPriv.h" 14#include "SkDither.h" 15#include "SkScaledBitmapSampler.h" 16#include "SkStream.h" 17#include "SkTemplates.h" 18#include "SkUtils.h" 19 20#include <stdio.h> 21extern "C" { 22 #include "jpeglib.h" 23 #include "jerror.h" 24} 25 26// this enables timing code to report milliseconds for an encode 27//#define TIME_ENCODE 28//#define TIME_DECODE 29 30// this enables our rgb->yuv code, which is faster than libjpeg on ARM 31// disable for the moment, as we have some glitches when width != multiple of 4 32#define WE_CONVERT_TO_YUV 33 34////////////////////////////////////////////////////////////////////////// 35////////////////////////////////////////////////////////////////////////// 36 37class SkJPEGImageDecoder : public SkImageDecoder { 38public: 39 virtual Format getFormat() const { 40 return kJPEG_Format; 41 } 42 43protected: 44 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 45}; 46 47////////////////////////////////////////////////////////////////////////// 48 49#include "SkTime.h" 50 51class AutoTimeMillis { 52public: 53 AutoTimeMillis(const char label[]) : fLabel(label) { 54 if (!fLabel) { 55 fLabel = ""; 56 } 57 fNow = SkTime::GetMSecs(); 58 } 59 ~AutoTimeMillis() { 60 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); 61 } 62private: 63 const char* fLabel; 64 SkMSec fNow; 65}; 66 67/* Automatically clean up after throwing an exception */ 68class JPEGAutoClean { 69public: 70 JPEGAutoClean(): cinfo_ptr(NULL) {} 71 ~JPEGAutoClean() { 72 if (cinfo_ptr) { 73 jpeg_destroy_decompress(cinfo_ptr); 74 } 75 } 76 void set(jpeg_decompress_struct* info) { 77 cinfo_ptr = info; 78 } 79private: 80 jpeg_decompress_struct* cinfo_ptr; 81}; 82 83#ifdef SK_BUILD_FOR_ANDROID 84 85/* For non-ndk builds we could look at the system's jpeg memory cap and use it 86 * if it is set. However, for now we will use the NDK compliant hardcoded values 87 */ 88//#include <cutils/properties.h> 89//static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap"; 90 91static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) { 92#ifdef ANDROID_LARGE_MEMORY_DEVICE 93 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024; 94#else 95 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024; 96#endif 97} 98#endif 99 100 101/////////////////////////////////////////////////////////////////////////////// 102 103/* If we need to better match the request, we might examine the image and 104 output dimensions, and determine if the downsampling jpeg provided is 105 not sufficient. If so, we can recompute a modified sampleSize value to 106 make up the difference. 107 108 To skip this additional scaling, just set sampleSize = 1; below. 109 */ 110static int recompute_sampleSize(int sampleSize, 111 const jpeg_decompress_struct& cinfo) { 112 return sampleSize * cinfo.output_width / cinfo.image_width; 113} 114 115static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) { 116 /* These are initialized to 0, so if they have non-zero values, we assume 117 they are "valid" (i.e. have been computed by libjpeg) 118 */ 119 return cinfo.output_width != 0 && cinfo.output_height != 0; 120} 121 122static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, 123 int count) { 124 for (int i = 0; i < count; i++) { 125 JSAMPLE* rowptr = (JSAMPLE*)buffer; 126 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); 127 if (row_count != 1) { 128 return false; 129 } 130 } 131 return true; 132} 133 134// This guy exists just to aid in debugging, as it allows debuggers to just 135// set a break-point in one place to see all error exists. 136static bool return_false(const jpeg_decompress_struct& cinfo, 137 const SkBitmap& bm, const char msg[]) { 138#if 0 139 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code, 140 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg, 141 bm.width(), bm.height()); 142#endif 143 return false; // must always return false 144} 145 146// Convert a scanline of CMYK samples to RGBX in place. Note that this 147// method moves the "scanline" pointer in its processing 148static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) { 149 // At this point we've received CMYK pixels from libjpeg. We 150 // perform a crude conversion to RGB (based on the formulae 151 // from easyrgb.com): 152 // CMYK -> CMY 153 // C = ( C * (1 - K) + K ) // for each CMY component 154 // CMY -> RGB 155 // R = ( 1 - C ) * 255 // for each RGB component 156 // Unfortunately we are seeing inverted CMYK so all the original terms 157 // are 1-. This yields: 158 // CMYK -> CMY 159 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K 160 // The conversion from CMY->RGB remains the same 161 for (unsigned int x = 0; x < width; ++x, scanline += 4) { 162 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); 163 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); 164 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); 165 scanline[3] = 255; 166 } 167} 168 169bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 170#ifdef TIME_DECODE 171 AutoTimeMillis atm("JPEG Decode"); 172#endif 173 174 SkAutoMalloc srcStorage; 175 JPEGAutoClean autoClean; 176 177 jpeg_decompress_struct cinfo; 178 skjpeg_error_mgr sk_err; 179 skjpeg_source_mgr sk_stream(stream, this, false); 180 181 cinfo.err = jpeg_std_error(&sk_err); 182 sk_err.error_exit = skjpeg_error_exit; 183 184 // All objects need to be instantiated before this setjmp call so that 185 // they will be cleaned up properly if an error occurs. 186 if (setjmp(sk_err.fJmpBuf)) { 187 return return_false(cinfo, *bm, "setjmp"); 188 } 189 190 jpeg_create_decompress(&cinfo); 191 autoClean.set(&cinfo); 192 193#ifdef SK_BUILD_FOR_ANDROID 194 overwrite_mem_buffer_size(&cinfo); 195#endif 196 197 //jpeg_stdio_src(&cinfo, file); 198 cinfo.src = &sk_stream; 199 200 int status = jpeg_read_header(&cinfo, true); 201 if (status != JPEG_HEADER_OK) { 202 return return_false(cinfo, *bm, "read_header"); 203 } 204 205 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it 206 can) much faster that we, just use their num/denom api to approximate 207 the size. 208 */ 209 int sampleSize = this->getSampleSize(); 210 211 cinfo.dct_method = JDCT_IFAST; 212 cinfo.scale_num = 1; 213 cinfo.scale_denom = sampleSize; 214 215 /* this gives about 30% performance improvement. In theory it may 216 reduce the visual quality, in practice I'm not seeing a difference 217 */ 218 cinfo.do_fancy_upsampling = 0; 219 220 /* this gives another few percents */ 221 cinfo.do_block_smoothing = 0; 222 223 /* default format is RGB */ 224 if (cinfo.jpeg_color_space == JCS_CMYK) { 225 // libjpeg cannot convert from CMYK to RGB - here we set up 226 // so libjpeg will give us CMYK samples back and we will 227 // later manually convert them to RGB 228 cinfo.out_color_space = JCS_CMYK; 229 } else { 230 cinfo.out_color_space = JCS_RGB; 231 } 232 233 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false); 234 // only these make sense for jpegs 235 if (config != SkBitmap::kARGB_8888_Config && 236 config != SkBitmap::kARGB_4444_Config && 237 config != SkBitmap::kRGB_565_Config) { 238 config = SkBitmap::kARGB_8888_Config; 239 } 240 241#ifdef ANDROID_RGB 242 cinfo.dither_mode = JDITHER_NONE; 243 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) { 244 cinfo.out_color_space = JCS_RGBA_8888; 245 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) { 246 cinfo.out_color_space = JCS_RGB_565; 247 if (this->getDitherImage()) { 248 cinfo.dither_mode = JDITHER_ORDERED; 249 } 250 } 251#endif 252 253 if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) { 254 bm->setConfig(config, cinfo.image_width, cinfo.image_height); 255 bm->setIsOpaque(true); 256 return true; 257 } 258 259 /* image_width and image_height are the original dimensions, available 260 after jpeg_read_header(). To see the scaled dimensions, we have to call 261 jpeg_start_decompress(), and then read output_width and output_height. 262 */ 263 if (!jpeg_start_decompress(&cinfo)) { 264 /* If we failed here, we may still have enough information to return 265 to the caller if they just wanted (subsampled bounds). If sampleSize 266 was 1, then we would have already returned. Thus we just check if 267 we're in kDecodeBounds_Mode, and that we have valid output sizes. 268 269 One reason to fail here is that we have insufficient stream data 270 to complete the setup. However, output dimensions seem to get 271 computed very early, which is why this special check can pay off. 272 */ 273 if (SkImageDecoder::kDecodeBounds_Mode == mode && 274 valid_output_dimensions(cinfo)) { 275 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height, 276 recompute_sampleSize(sampleSize, cinfo)); 277 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight()); 278 bm->setIsOpaque(true); 279 return true; 280 } else { 281 return return_false(cinfo, *bm, "start_decompress"); 282 } 283 } 284 sampleSize = recompute_sampleSize(sampleSize, cinfo); 285 286 // should we allow the Chooser (if present) to pick a config for us??? 287 if (!this->chooseFromOneChoice(config, cinfo.output_width, 288 cinfo.output_height)) { 289 return return_false(cinfo, *bm, "chooseFromOneChoice"); 290 } 291 292#ifdef ANDROID_RGB 293 /* short-circuit the SkScaledBitmapSampler when possible, as this gives 294 a significant performance boost. 295 */ 296 if (sampleSize == 1 && 297 ((config == SkBitmap::kARGB_8888_Config && 298 cinfo.out_color_space == JCS_RGBA_8888) || 299 (config == SkBitmap::kRGB_565_Config && 300 cinfo.out_color_space == JCS_RGB_565))) 301 { 302 bm->setConfig(config, cinfo.output_width, cinfo.output_height); 303 bm->setIsOpaque(true); 304 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 305 return true; 306 } 307 if (!this->allocPixelRef(bm, NULL)) { 308 return return_false(cinfo, *bm, "allocPixelRef"); 309 } 310 SkAutoLockPixels alp(*bm); 311 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels(); 312 INT32 const bpr = bm->rowBytes(); 313 314 while (cinfo.output_scanline < cinfo.output_height) { 315 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); 316 // if row_count == 0, then we didn't get a scanline, so abort. 317 // if we supported partial images, we might return true in this case 318 if (0 == row_count) { 319 return return_false(cinfo, *bm, "read_scanlines"); 320 } 321 if (this->shouldCancelDecode()) { 322 return return_false(cinfo, *bm, "shouldCancelDecode"); 323 } 324 rowptr += bpr; 325 } 326 jpeg_finish_decompress(&cinfo); 327 return true; 328 } 329#endif 330 331 // check for supported formats 332 SkScaledBitmapSampler::SrcConfig sc; 333 if (JCS_CMYK == cinfo.out_color_space) { 334 // In this case we will manually convert the CMYK values to RGB 335 sc = SkScaledBitmapSampler::kRGBX; 336 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) { 337 sc = SkScaledBitmapSampler::kRGB; 338#ifdef ANDROID_RGB 339 } else if (JCS_RGBA_8888 == cinfo.out_color_space) { 340 sc = SkScaledBitmapSampler::kRGBX; 341 } else if (JCS_RGB_565 == cinfo.out_color_space) { 342 sc = SkScaledBitmapSampler::kRGB_565; 343#endif 344 } else if (1 == cinfo.out_color_components && 345 JCS_GRAYSCALE == cinfo.out_color_space) { 346 sc = SkScaledBitmapSampler::kGray; 347 } else { 348 return return_false(cinfo, *bm, "jpeg colorspace"); 349 } 350 351 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, 352 sampleSize); 353 354 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); 355 // jpegs are always opaque (i.e. have no per-pixel alpha) 356 bm->setIsOpaque(true); 357 358 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 359 return true; 360 } 361 if (!this->allocPixelRef(bm, NULL)) { 362 return return_false(cinfo, *bm, "allocPixelRef"); 363 } 364 365 SkAutoLockPixels alp(*bm); 366 if (!sampler.begin(bm, sc, this->getDitherImage())) { 367 return return_false(cinfo, *bm, "sampler.begin"); 368 } 369 370 // The CMYK work-around relies on 4 components per pixel here 371 uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4); 372 373 // Possibly skip initial rows [sampler.srcY0] 374 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { 375 return return_false(cinfo, *bm, "skip rows"); 376 } 377 378 // now loop through scanlines until y == bm->height() - 1 379 for (int y = 0;; y++) { 380 JSAMPLE* rowptr = (JSAMPLE*)srcRow; 381 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); 382 if (0 == row_count) { 383 return return_false(cinfo, *bm, "read_scanlines"); 384 } 385 if (this->shouldCancelDecode()) { 386 return return_false(cinfo, *bm, "shouldCancelDecode"); 387 } 388 389 if (JCS_CMYK == cinfo.out_color_space) { 390 convert_CMYK_to_RGB(srcRow, cinfo.output_width); 391 } 392 393 sampler.next(srcRow); 394 if (bm->height() - 1 == y) { 395 // we're done 396 break; 397 } 398 399 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) { 400 return return_false(cinfo, *bm, "skip rows"); 401 } 402 } 403 404 // we formally skip the rest, so we don't get a complaint from libjpeg 405 if (!skip_src_rows(&cinfo, srcRow, 406 cinfo.output_height - cinfo.output_scanline)) { 407 return return_false(cinfo, *bm, "skip rows"); 408 } 409 jpeg_finish_decompress(&cinfo); 410 411// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config()); 412 return true; 413} 414 415/////////////////////////////////////////////////////////////////////////////// 416 417#include "SkColorPriv.h" 418 419// taken from jcolor.c in libjpeg 420#if 0 // 16bit - precise but slow 421 #define CYR 19595 // 0.299 422 #define CYG 38470 // 0.587 423 #define CYB 7471 // 0.114 424 425 #define CUR -11059 // -0.16874 426 #define CUG -21709 // -0.33126 427 #define CUB 32768 // 0.5 428 429 #define CVR 32768 // 0.5 430 #define CVG -27439 // -0.41869 431 #define CVB -5329 // -0.08131 432 433 #define CSHIFT 16 434#else // 8bit - fast, slightly less precise 435 #define CYR 77 // 0.299 436 #define CYG 150 // 0.587 437 #define CYB 29 // 0.114 438 439 #define CUR -43 // -0.16874 440 #define CUG -85 // -0.33126 441 #define CUB 128 // 0.5 442 443 #define CVR 128 // 0.5 444 #define CVG -107 // -0.41869 445 #define CVB -21 // -0.08131 446 447 #define CSHIFT 8 448#endif 449 450static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { 451 int r = SkGetPackedR32(c); 452 int g = SkGetPackedG32(c); 453 int b = SkGetPackedB32(c); 454 455 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; 456 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; 457 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; 458 459 dst[0] = SkToU8(y); 460 dst[1] = SkToU8(u + 128); 461 dst[2] = SkToU8(v + 128); 462} 463 464static void rgb2yuv_4444(uint8_t dst[], U16CPU c) { 465 int r = SkGetPackedR4444(c); 466 int g = SkGetPackedG4444(c); 467 int b = SkGetPackedB4444(c); 468 469 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4); 470 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4); 471 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4); 472 473 dst[0] = SkToU8(y); 474 dst[1] = SkToU8(u + 128); 475 dst[2] = SkToU8(v + 128); 476} 477 478static void rgb2yuv_16(uint8_t dst[], U16CPU c) { 479 int r = SkGetPackedR16(c); 480 int g = SkGetPackedG16(c); 481 int b = SkGetPackedB16(c); 482 483 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2); 484 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2); 485 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2); 486 487 dst[0] = SkToU8(y); 488 dst[1] = SkToU8(u + 128); 489 dst[2] = SkToU8(v + 128); 490} 491 492/////////////////////////////////////////////////////////////////////////////// 493 494typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, 495 const void* SK_RESTRICT src, int width, 496 const SkPMColor* SK_RESTRICT ctable); 497 498static void Write_32_YUV(uint8_t* SK_RESTRICT dst, 499 const void* SK_RESTRICT srcRow, int width, 500 const SkPMColor*) { 501 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; 502 while (--width >= 0) { 503#ifdef WE_CONVERT_TO_YUV 504 rgb2yuv_32(dst, *src++); 505#else 506 uint32_t c = *src++; 507 dst[0] = SkGetPackedR32(c); 508 dst[1] = SkGetPackedG32(c); 509 dst[2] = SkGetPackedB32(c); 510#endif 511 dst += 3; 512 } 513} 514 515static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, 516 const void* SK_RESTRICT srcRow, int width, 517 const SkPMColor*) { 518 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; 519 while (--width >= 0) { 520#ifdef WE_CONVERT_TO_YUV 521 rgb2yuv_4444(dst, *src++); 522#else 523 SkPMColor16 c = *src++; 524 dst[0] = SkPacked4444ToR32(c); 525 dst[1] = SkPacked4444ToG32(c); 526 dst[2] = SkPacked4444ToB32(c); 527#endif 528 dst += 3; 529 } 530} 531 532static void Write_16_YUV(uint8_t* SK_RESTRICT dst, 533 const void* SK_RESTRICT srcRow, int width, 534 const SkPMColor*) { 535 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; 536 while (--width >= 0) { 537#ifdef WE_CONVERT_TO_YUV 538 rgb2yuv_16(dst, *src++); 539#else 540 uint16_t c = *src++; 541 dst[0] = SkPacked16ToR32(c); 542 dst[1] = SkPacked16ToG32(c); 543 dst[2] = SkPacked16ToB32(c); 544#endif 545 dst += 3; 546 } 547} 548 549static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, 550 const void* SK_RESTRICT srcRow, int width, 551 const SkPMColor* SK_RESTRICT ctable) { 552 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; 553 while (--width >= 0) { 554#ifdef WE_CONVERT_TO_YUV 555 rgb2yuv_32(dst, ctable[*src++]); 556#else 557 uint32_t c = ctable[*src++]; 558 dst[0] = SkGetPackedR32(c); 559 dst[1] = SkGetPackedG32(c); 560 dst[2] = SkGetPackedB32(c); 561#endif 562 dst += 3; 563 } 564} 565 566static WriteScanline ChooseWriter(const SkBitmap& bm) { 567 switch (bm.config()) { 568 case SkBitmap::kARGB_8888_Config: 569 return Write_32_YUV; 570 case SkBitmap::kRGB_565_Config: 571 return Write_16_YUV; 572 case SkBitmap::kARGB_4444_Config: 573 return Write_4444_YUV; 574 case SkBitmap::kIndex8_Config: 575 return Write_Index_YUV; 576 default: 577 return NULL; 578 } 579} 580 581class SkJPEGImageEncoder : public SkImageEncoder { 582protected: 583 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { 584#ifdef TIME_ENCODE 585 AutoTimeMillis atm("JPEG Encode"); 586#endif 587 588 const WriteScanline writer = ChooseWriter(bm); 589 if (NULL == writer) { 590 return false; 591 } 592 593 SkAutoLockPixels alp(bm); 594 if (NULL == bm.getPixels()) { 595 return false; 596 } 597 598 jpeg_compress_struct cinfo; 599 skjpeg_error_mgr sk_err; 600 skjpeg_destination_mgr sk_wstream(stream); 601 602 // allocate these before set call setjmp 603 SkAutoMalloc oneRow; 604 SkAutoLockColors ctLocker; 605 606 cinfo.err = jpeg_std_error(&sk_err); 607 sk_err.error_exit = skjpeg_error_exit; 608 if (setjmp(sk_err.fJmpBuf)) { 609 return false; 610 } 611 jpeg_create_compress(&cinfo); 612 613 cinfo.dest = &sk_wstream; 614 cinfo.image_width = bm.width(); 615 cinfo.image_height = bm.height(); 616 cinfo.input_components = 3; 617#ifdef WE_CONVERT_TO_YUV 618 cinfo.in_color_space = JCS_YCbCr; 619#else 620 cinfo.in_color_space = JCS_RGB; 621#endif 622 cinfo.input_gamma = 1; 623 624 jpeg_set_defaults(&cinfo); 625 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); 626 cinfo.dct_method = JDCT_IFAST; 627 628 jpeg_start_compress(&cinfo, TRUE); 629 630 const int width = bm.width(); 631 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3); 632 633 const SkPMColor* colors = ctLocker.lockColors(bm); 634 const void* srcRow = bm.getPixels(); 635 636 while (cinfo.next_scanline < cinfo.image_height) { 637 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ 638 639 writer(oneRowP, srcRow, width, colors); 640 row_pointer[0] = oneRowP; 641 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); 642 srcRow = (const void*)((const char*)srcRow + bm.rowBytes()); 643 } 644 645 jpeg_finish_compress(&cinfo); 646 jpeg_destroy_compress(&cinfo); 647 648 return true; 649 } 650}; 651 652/////////////////////////////////////////////////////////////////////////////// 653DEFINE_DECODER_CREATOR(JPEGImageDecoder); 654DEFINE_ENCODER_CREATOR(JPEGImageEncoder); 655/////////////////////////////////////////////////////////////////////////////// 656 657#include "SkTRegistry.h" 658 659SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) { 660 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF }; 661 static const size_t HEADER_SIZE = sizeof(gHeader); 662 663 char buffer[HEADER_SIZE]; 664 size_t len = stream->read(buffer, HEADER_SIZE); 665 666 if (len != HEADER_SIZE) { 667 return NULL; // can't read enough 668 } 669 if (memcmp(buffer, gHeader, HEADER_SIZE)) { 670 return NULL; 671 } 672 return SkNEW(SkJPEGImageDecoder); 673} 674 675static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { 676 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL; 677} 678 679 680static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory); 681static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory); 682 683