Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2007 The Android Open Source Project | 2 * Copyright 2016 Google Inc. |
|
scroggo
2016/04/18 20:04:09
We generally leave copyright notices unchanged aft
msarett
2016/04/18 21:12:13
Done.
| |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 | 8 |
| 9 #include "SkImageEncoder.h" | 9 #include "SkImageEncoder.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| 11 #include "SkDither.h" | 11 #include "SkDither.h" |
| 12 #include "SkStream.h" | 12 #include "SkStream.h" |
| 13 #include "SkTemplates.h" | 13 #include "SkTemplates.h" |
| 14 #include "SkTime.h" | |
| 15 #include "SkUtils.h" | 14 #include "SkUtils.h" |
| 16 #include "SkRTConf.h" | 15 #include "SkRTConf.h" |
| 17 #include "SkRect.h" | 16 #include "SkRect.h" |
| 18 #include "SkCanvas.h" | 17 #include "SkCanvas.h" |
| 19 | 18 |
| 20 | |
| 21 #include <stdio.h> | 19 #include <stdio.h> |
| 22 #include "SkJPEGWriteUtility.h" | 20 #include "SkJPEGWriteUtility.h" |
| 23 extern "C" { | 21 extern "C" { |
| 24 #include "jpeglib.h" | 22 #include "jpeglib.h" |
| 25 #include "jerror.h" | 23 #include "jerror.h" |
| 26 } | 24 } |
| 27 | 25 |
| 28 // These enable timing code that report milliseconds for an encoding | |
| 29 //#define TIME_ENCODE | |
| 30 | |
| 31 // this enables our rgb->yuv code, which is faster than libjpeg on ARM | |
| 32 #define WE_CONVERT_TO_YUV | |
| 33 | |
| 34 /////////////////////////////////////////////////////////////////////////////// | |
| 35 | |
| 36 #include "SkColorPriv.h" | |
| 37 | |
| 38 // taken from jcolor.c in libjpeg | |
| 39 #if 0 // 16bit - precise but slow | |
| 40 #define CYR 19595 // 0.299 | |
| 41 #define CYG 38470 // 0.587 | |
| 42 #define CYB 7471 // 0.114 | |
| 43 | |
| 44 #define CUR -11059 // -0.16874 | |
| 45 #define CUG -21709 // -0.33126 | |
| 46 #define CUB 32768 // 0.5 | |
| 47 | |
| 48 #define CVR 32768 // 0.5 | |
| 49 #define CVG -27439 // -0.41869 | |
| 50 #define CVB -5329 // -0.08131 | |
| 51 | |
| 52 #define CSHIFT 16 | |
| 53 #else // 8bit - fast, slightly less precise | |
| 54 #define CYR 77 // 0.299 | |
| 55 #define CYG 150 // 0.587 | |
| 56 #define CYB 29 // 0.114 | |
| 57 | |
| 58 #define CUR -43 // -0.16874 | |
| 59 #define CUG -85 // -0.33126 | |
| 60 #define CUB 128 // 0.5 | |
| 61 | |
| 62 #define CVR 128 // 0.5 | |
| 63 #define CVG -107 // -0.41869 | |
| 64 #define CVB -21 // -0.08131 | |
| 65 | |
| 66 #define CSHIFT 8 | |
| 67 #endif | |
| 68 | |
| 69 static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { | |
| 70 int r = SkGetPackedR32(c); | |
| 71 int g = SkGetPackedG32(c); | |
| 72 int b = SkGetPackedB32(c); | |
| 73 | |
| 74 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; | |
| 75 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; | |
| 76 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; | |
| 77 | |
| 78 dst[0] = SkToU8(y); | |
| 79 dst[1] = SkToU8(u + 128); | |
| 80 dst[2] = SkToU8(v + 128); | |
| 81 } | |
| 82 | |
| 83 static void rgb2yuv_4444(uint8_t dst[], U16CPU c) { | |
| 84 int r = SkGetPackedR4444(c); | |
| 85 int g = SkGetPackedG4444(c); | |
| 86 int b = SkGetPackedB4444(c); | |
| 87 | |
| 88 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4); | |
| 89 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4); | |
| 90 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4); | |
| 91 | |
| 92 dst[0] = SkToU8(y); | |
| 93 dst[1] = SkToU8(u + 128); | |
| 94 dst[2] = SkToU8(v + 128); | |
| 95 } | |
| 96 | |
| 97 static void rgb2yuv_16(uint8_t dst[], U16CPU c) { | |
| 98 int r = SkGetPackedR16(c); | |
| 99 int g = SkGetPackedG16(c); | |
| 100 int b = SkGetPackedB16(c); | |
| 101 | |
| 102 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2); | |
| 103 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2); | |
| 104 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2); | |
| 105 | |
| 106 dst[0] = SkToU8(y); | |
| 107 dst[1] = SkToU8(u + 128); | |
| 108 dst[2] = SkToU8(v + 128); | |
| 109 } | |
| 110 | |
| 111 /////////////////////////////////////////////////////////////////////////////// | |
| 112 | |
| 113 typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, | 26 typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, |
| 114 const void* SK_RESTRICT src, int width, | 27 const void* SK_RESTRICT src, int width, |
| 115 const SkPMColor* SK_RESTRICT ctable); | 28 const SkPMColor* SK_RESTRICT ctable); |
| 116 | 29 |
| 117 static void Write_32_YUV(uint8_t* SK_RESTRICT dst, | 30 static void Write_32_RGB(uint8_t* SK_RESTRICT dst, |
| 118 const void* SK_RESTRICT srcRow, int width, | 31 const void* SK_RESTRICT srcRow, int width, |
| 119 const SkPMColor*) { | 32 const SkPMColor*) { |
| 120 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; | 33 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; |
| 121 while (--width >= 0) { | 34 while (--width >= 0) { |
| 122 #ifdef WE_CONVERT_TO_YUV | |
| 123 rgb2yuv_32(dst, *src++); | |
| 124 #else | |
| 125 uint32_t c = *src++; | 35 uint32_t c = *src++; |
| 126 dst[0] = SkGetPackedR32(c); | 36 dst[0] = SkGetPackedR32(c); |
| 127 dst[1] = SkGetPackedG32(c); | 37 dst[1] = SkGetPackedG32(c); |
| 128 dst[2] = SkGetPackedB32(c); | 38 dst[2] = SkGetPackedB32(c); |
| 129 #endif | |
| 130 dst += 3; | 39 dst += 3; |
| 131 } | 40 } |
| 132 } | 41 } |
| 133 | 42 |
| 134 static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, | 43 static void Write_4444_RGB(uint8_t* SK_RESTRICT dst, |
| 135 const void* SK_RESTRICT srcRow, int width, | 44 const void* SK_RESTRICT srcRow, int width, |
| 136 const SkPMColor*) { | 45 const SkPMColor*) { |
| 137 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; | 46 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; |
| 138 while (--width >= 0) { | 47 while (--width >= 0) { |
| 139 #ifdef WE_CONVERT_TO_YUV | |
| 140 rgb2yuv_4444(dst, *src++); | |
| 141 #else | |
| 142 SkPMColor16 c = *src++; | 48 SkPMColor16 c = *src++; |
| 143 dst[0] = SkPacked4444ToR32(c); | 49 dst[0] = SkPacked4444ToR32(c); |
| 144 dst[1] = SkPacked4444ToG32(c); | 50 dst[1] = SkPacked4444ToG32(c); |
| 145 dst[2] = SkPacked4444ToB32(c); | 51 dst[2] = SkPacked4444ToB32(c); |
| 146 #endif | |
| 147 dst += 3; | 52 dst += 3; |
| 148 } | 53 } |
| 149 } | 54 } |
| 150 | 55 |
| 151 static void Write_16_YUV(uint8_t* SK_RESTRICT dst, | 56 static void Write_16_RGB(uint8_t* SK_RESTRICT dst, |
| 152 const void* SK_RESTRICT srcRow, int width, | 57 const void* SK_RESTRICT srcRow, int width, |
| 153 const SkPMColor*) { | 58 const SkPMColor*) { |
| 154 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; | 59 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; |
| 155 while (--width >= 0) { | 60 while (--width >= 0) { |
| 156 #ifdef WE_CONVERT_TO_YUV | |
| 157 rgb2yuv_16(dst, *src++); | |
| 158 #else | |
| 159 uint16_t c = *src++; | 61 uint16_t c = *src++; |
| 160 dst[0] = SkPacked16ToR32(c); | 62 dst[0] = SkPacked16ToR32(c); |
| 161 dst[1] = SkPacked16ToG32(c); | 63 dst[1] = SkPacked16ToG32(c); |
| 162 dst[2] = SkPacked16ToB32(c); | 64 dst[2] = SkPacked16ToB32(c); |
| 163 #endif | |
| 164 dst += 3; | 65 dst += 3; |
| 165 } | 66 } |
| 166 } | 67 } |
| 167 | 68 |
| 168 static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, | 69 static void Write_Index_RGB(uint8_t* SK_RESTRICT dst, |
| 169 const void* SK_RESTRICT srcRow, int width, | 70 const void* SK_RESTRICT srcRow, int width, |
| 170 const SkPMColor* SK_RESTRICT ctable) { | 71 const SkPMColor* SK_RESTRICT ctable) { |
| 171 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; | 72 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; |
| 172 while (--width >= 0) { | 73 while (--width >= 0) { |
| 173 #ifdef WE_CONVERT_TO_YUV | |
| 174 rgb2yuv_32(dst, ctable[*src++]); | |
| 175 #else | |
| 176 uint32_t c = ctable[*src++]; | 74 uint32_t c = ctable[*src++]; |
| 177 dst[0] = SkGetPackedR32(c); | 75 dst[0] = SkGetPackedR32(c); |
| 178 dst[1] = SkGetPackedG32(c); | 76 dst[1] = SkGetPackedG32(c); |
| 179 dst[2] = SkGetPackedB32(c); | 77 dst[2] = SkGetPackedB32(c); |
| 180 #endif | |
| 181 dst += 3; | 78 dst += 3; |
| 182 } | 79 } |
| 183 } | 80 } |
| 184 | 81 |
| 185 static WriteScanline ChooseWriter(const SkBitmap& bm) { | 82 static WriteScanline ChooseWriter(const SkBitmap& bm) { |
| 186 switch (bm.colorType()) { | 83 switch (bm.colorType()) { |
| 187 case kN32_SkColorType: | 84 case kN32_SkColorType: |
| 188 return Write_32_YUV; | 85 return Write_32_RGB; |
| 189 case kRGB_565_SkColorType: | 86 case kRGB_565_SkColorType: |
| 190 return Write_16_YUV; | 87 return Write_16_RGB; |
| 191 case kARGB_4444_SkColorType: | 88 case kARGB_4444_SkColorType: |
| 192 return Write_4444_YUV; | 89 return Write_4444_RGB; |
| 193 case kIndex_8_SkColorType: | 90 case kIndex_8_SkColorType: |
| 194 return Write_Index_YUV; | 91 return Write_Index_RGB; |
| 195 default: | 92 default: |
| 196 return nullptr; | 93 return nullptr; |
| 197 } | 94 } |
| 198 } | 95 } |
| 199 | 96 |
| 200 class SkJPEGImageEncoder : public SkImageEncoder { | 97 class SkJPEGImageEncoder : public SkImageEncoder { |
| 201 protected: | 98 protected: |
| 202 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { | 99 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { |
| 203 #ifdef TIME_ENCODE | |
| 204 SkAutoTime atm("JPEG Encode"); | |
|
scroggo
2016/04/18 20:04:09
This seems like an unrelated change. That change s
msarett
2016/04/18 21:12:13
Reverting Time related aspects of this change.
| |
| 205 #endif | |
| 206 | |
| 207 SkAutoLockPixels alp(bm); | 100 SkAutoLockPixels alp(bm); |
| 208 if (nullptr == bm.getPixels()) { | 101 if (nullptr == bm.getPixels()) { |
| 209 return false; | 102 return false; |
| 210 } | 103 } |
| 211 | 104 |
| 212 jpeg_compress_struct cinfo; | 105 jpeg_compress_struct cinfo; |
| 213 skjpeg_error_mgr sk_err; | 106 skjpeg_error_mgr sk_err; |
| 214 skjpeg_destination_mgr sk_wstream(stream); | 107 skjpeg_destination_mgr sk_wstream(stream); |
| 215 | 108 |
| 216 // allocate these before set call setjmp | 109 // allocate these before set call setjmp |
| 217 SkAutoTMalloc<uint8_t> oneRow; | 110 SkAutoTMalloc<uint8_t> oneRow; |
| 218 | 111 |
| 219 cinfo.err = jpeg_std_error(&sk_err); | 112 cinfo.err = jpeg_std_error(&sk_err); |
| 220 sk_err.error_exit = skjpeg_error_exit; | 113 sk_err.error_exit = skjpeg_error_exit; |
| 221 if (setjmp(sk_err.fJmpBuf)) { | 114 if (setjmp(sk_err.fJmpBuf)) { |
| 222 return false; | 115 return false; |
| 223 } | 116 } |
| 224 | 117 |
| 225 // Keep after setjmp or mark volatile. | 118 // Keep after setjmp or mark volatile. |
| 226 const WriteScanline writer = ChooseWriter(bm); | 119 const WriteScanline writer = ChooseWriter(bm); |
| 227 if (nullptr == writer) { | 120 if (nullptr == writer) { |
| 228 return false; | 121 return false; |
| 229 } | 122 } |
| 230 | 123 |
| 231 jpeg_create_compress(&cinfo); | 124 jpeg_create_compress(&cinfo); |
| 232 cinfo.dest = &sk_wstream; | 125 cinfo.dest = &sk_wstream; |
| 233 cinfo.image_width = bm.width(); | 126 cinfo.image_width = bm.width(); |
| 234 cinfo.image_height = bm.height(); | 127 cinfo.image_height = bm.height(); |
| 235 cinfo.input_components = 3; | 128 cinfo.input_components = 3; |
| 236 #ifdef WE_CONVERT_TO_YUV | 129 |
| 237 cinfo.in_color_space = JCS_YCbCr; | 130 // FIXME: Can we take advantage of additional libjpeg-turbo in_color_spa ces |
|
scroggo
2016/04/18 20:04:09
nit: This took me a few tries to parse. I think it
msarett
2016/04/18 21:12:13
Done.
| |
| 238 #else | 131 // (Ex: RGBA) and skip the "swizzle to RGB" step. |
| 239 cinfo.in_color_space = JCS_RGB; | 132 cinfo.in_color_space = JCS_RGB; |
| 240 #endif | 133 |
| 134 // The gamma value is ignored by libjpeg-turbo. | |
| 241 cinfo.input_gamma = 1; | 135 cinfo.input_gamma = 1; |
| 242 | 136 |
| 243 jpeg_set_defaults(&cinfo); | 137 jpeg_set_defaults(&cinfo); |
| 138 | |
| 139 // Tells libjpeg-turbo to compute optimal Huffman coding tables | |
| 140 // for the image. This improves compression at the cost of | |
| 141 // slower encode performance. | |
| 244 cinfo.optimize_coding = TRUE; | 142 cinfo.optimize_coding = TRUE; |
| 245 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); | 143 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); |
| 246 #ifdef DCT_IFAST_SUPPORTED | |
| 247 cinfo.dct_method = JDCT_IFAST; | |
| 248 #endif | |
| 249 | 144 |
| 250 jpeg_start_compress(&cinfo, TRUE); | 145 jpeg_start_compress(&cinfo, TRUE); |
| 251 | 146 |
| 252 const int width = bm.width(); | 147 const int width = bm.width(); |
| 253 uint8_t* oneRowP = oneRow.reset(width * 3); | 148 uint8_t* oneRowP = oneRow.reset(width * 3); |
| 254 | 149 |
| 255 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readC olors() : nullptr; | 150 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readC olors() : nullptr; |
| 256 const void* srcRow = bm.getPixels(); | 151 const void* srcRow = bm.getPixels(); |
| 257 | 152 |
| 258 while (cinfo.next_scanline < cinfo.image_height) { | 153 while (cinfo.next_scanline < cinfo.image_height) { |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 273 | 168 |
| 274 /////////////////////////////////////////////////////////////////////////////// | 169 /////////////////////////////////////////////////////////////////////////////// |
| 275 DEFINE_ENCODER_CREATOR(JPEGImageEncoder); | 170 DEFINE_ENCODER_CREATOR(JPEGImageEncoder); |
| 276 /////////////////////////////////////////////////////////////////////////////// | 171 /////////////////////////////////////////////////////////////////////////////// |
| 277 | 172 |
| 278 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { | 173 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { |
| 279 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr; | 174 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr; |
| 280 } | 175 } |
| 281 | 176 |
| 282 static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory); | 177 static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory); |
| OLD | NEW |