tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2007 The Android Open Source Project |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 9 | #include "SkImageEncoder.h" |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 10 | #include "SkColorPriv.h" |
| 11 | #include "SkDither.h" |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 12 | #include "SkStream.h" |
| 13 | #include "SkTemplates.h" |
djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 14 | #include "SkTime.h" |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 15 | #include "SkUtils.h" |
halcanary@google.com | 2a10318 | 2013-10-14 12:49:15 +0000 | [diff] [blame] | 16 | #include "SkRTConf.h" |
djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 17 | #include "SkRect.h" |
| 18 | #include "SkCanvas.h" |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 19 | |
halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 20 | |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 21 | #include <stdio.h> |
msarett | c1d0312 | 2016-03-25 08:58:55 -0700 | [diff] [blame^] | 22 | #include "SkJPEGWriteUtility.h" |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 23 | extern "C" { |
| 24 | #include "jpeglib.h" |
| 25 | #include "jerror.h" |
| 26 | } |
| 27 | |
msarett | e8597a4 | 2016-03-24 10:41:47 -0700 | [diff] [blame] | 28 | // These enable timing code that report milliseconds for an encoding |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 29 | //#define TIME_ENCODE |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 30 | |
| 31 | // this enables our rgb->yuv code, which is faster than libjpeg on ARM |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 32 | #define WE_CONVERT_TO_YUV |
| 33 | |
scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 34 | /////////////////////////////////////////////////////////////////////////////// |
| 35 | |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 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, |
| 114 | const void* SK_RESTRICT src, int width, |
| 115 | const SkPMColor* SK_RESTRICT ctable); |
| 116 | |
| 117 | static void Write_32_YUV(uint8_t* SK_RESTRICT dst, |
| 118 | const void* SK_RESTRICT srcRow, int width, |
| 119 | const SkPMColor*) { |
| 120 | const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; |
| 121 | while (--width >= 0) { |
| 122 | #ifdef WE_CONVERT_TO_YUV |
| 123 | rgb2yuv_32(dst, *src++); |
| 124 | #else |
| 125 | uint32_t c = *src++; |
| 126 | dst[0] = SkGetPackedR32(c); |
| 127 | dst[1] = SkGetPackedG32(c); |
| 128 | dst[2] = SkGetPackedB32(c); |
| 129 | #endif |
| 130 | dst += 3; |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, |
| 135 | const void* SK_RESTRICT srcRow, int width, |
| 136 | const SkPMColor*) { |
| 137 | const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; |
| 138 | while (--width >= 0) { |
| 139 | #ifdef WE_CONVERT_TO_YUV |
| 140 | rgb2yuv_4444(dst, *src++); |
| 141 | #else |
| 142 | SkPMColor16 c = *src++; |
| 143 | dst[0] = SkPacked4444ToR32(c); |
| 144 | dst[1] = SkPacked4444ToG32(c); |
| 145 | dst[2] = SkPacked4444ToB32(c); |
| 146 | #endif |
| 147 | dst += 3; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | static void Write_16_YUV(uint8_t* SK_RESTRICT dst, |
| 152 | const void* SK_RESTRICT srcRow, int width, |
| 153 | const SkPMColor*) { |
| 154 | const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; |
| 155 | while (--width >= 0) { |
| 156 | #ifdef WE_CONVERT_TO_YUV |
| 157 | rgb2yuv_16(dst, *src++); |
| 158 | #else |
| 159 | uint16_t c = *src++; |
| 160 | dst[0] = SkPacked16ToR32(c); |
| 161 | dst[1] = SkPacked16ToG32(c); |
| 162 | dst[2] = SkPacked16ToB32(c); |
| 163 | #endif |
| 164 | dst += 3; |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, |
| 169 | const void* SK_RESTRICT srcRow, int width, |
| 170 | const SkPMColor* SK_RESTRICT ctable) { |
| 171 | const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; |
| 172 | while (--width >= 0) { |
| 173 | #ifdef WE_CONVERT_TO_YUV |
| 174 | rgb2yuv_32(dst, ctable[*src++]); |
| 175 | #else |
| 176 | uint32_t c = ctable[*src++]; |
| 177 | dst[0] = SkGetPackedR32(c); |
| 178 | dst[1] = SkGetPackedG32(c); |
| 179 | dst[2] = SkGetPackedB32(c); |
| 180 | #endif |
| 181 | dst += 3; |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | static WriteScanline ChooseWriter(const SkBitmap& bm) { |
reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 186 | switch (bm.colorType()) { |
| 187 | case kN32_SkColorType: |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 188 | return Write_32_YUV; |
reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 189 | case kRGB_565_SkColorType: |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 190 | return Write_16_YUV; |
reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 191 | case kARGB_4444_SkColorType: |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 192 | return Write_4444_YUV; |
reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 193 | case kIndex_8_SkColorType: |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 194 | return Write_Index_YUV; |
| 195 | default: |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 196 | return nullptr; |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 197 | } |
| 198 | } |
| 199 | |
| 200 | class SkJPEGImageEncoder : public SkImageEncoder { |
| 201 | protected: |
| 202 | virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { |
| 203 | #ifdef TIME_ENCODE |
djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 204 | SkAutoTime atm("JPEG Encode"); |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 205 | #endif |
| 206 | |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 207 | SkAutoLockPixels alp(bm); |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 208 | if (nullptr == bm.getPixels()) { |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 209 | return false; |
| 210 | } |
| 211 | |
| 212 | jpeg_compress_struct cinfo; |
| 213 | skjpeg_error_mgr sk_err; |
| 214 | skjpeg_destination_mgr sk_wstream(stream); |
| 215 | |
| 216 | // allocate these before set call setjmp |
scroggo | 565901d | 2015-12-10 10:44:13 -0800 | [diff] [blame] | 217 | SkAutoTMalloc<uint8_t> oneRow; |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 218 | |
| 219 | cinfo.err = jpeg_std_error(&sk_err); |
| 220 | sk_err.error_exit = skjpeg_error_exit; |
| 221 | if (setjmp(sk_err.fJmpBuf)) { |
| 222 | return false; |
| 223 | } |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 224 | |
mtklein@google.com | 8d725b2 | 2013-07-24 16:20:05 +0000 | [diff] [blame] | 225 | // Keep after setjmp or mark volatile. |
| 226 | const WriteScanline writer = ChooseWriter(bm); |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 227 | if (nullptr == writer) { |
mtklein@google.com | 8d725b2 | 2013-07-24 16:20:05 +0000 | [diff] [blame] | 228 | return false; |
| 229 | } |
| 230 | |
| 231 | jpeg_create_compress(&cinfo); |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 232 | cinfo.dest = &sk_wstream; |
| 233 | cinfo.image_width = bm.width(); |
| 234 | cinfo.image_height = bm.height(); |
| 235 | cinfo.input_components = 3; |
| 236 | #ifdef WE_CONVERT_TO_YUV |
| 237 | cinfo.in_color_space = JCS_YCbCr; |
| 238 | #else |
| 239 | cinfo.in_color_space = JCS_RGB; |
| 240 | #endif |
| 241 | cinfo.input_gamma = 1; |
| 242 | |
| 243 | jpeg_set_defaults(&cinfo); |
benjaminwagner | 0a35620 | 2016-01-14 18:13:32 -0800 | [diff] [blame] | 244 | cinfo.optimize_coding = TRUE; |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 245 | jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); |
scroggo@google.com | b7decc5 | 2013-04-17 17:37:56 +0000 | [diff] [blame] | 246 | #ifdef DCT_IFAST_SUPPORTED |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 247 | cinfo.dct_method = JDCT_IFAST; |
scroggo@google.com | b7decc5 | 2013-04-17 17:37:56 +0000 | [diff] [blame] | 248 | #endif |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 249 | |
| 250 | jpeg_start_compress(&cinfo, TRUE); |
| 251 | |
| 252 | const int width = bm.width(); |
scroggo | 565901d | 2015-12-10 10:44:13 -0800 | [diff] [blame] | 253 | uint8_t* oneRowP = oneRow.reset(width * 3); |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 254 | |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 255 | const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr; |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 256 | const void* srcRow = bm.getPixels(); |
| 257 | |
| 258 | while (cinfo.next_scanline < cinfo.image_height) { |
| 259 | JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ |
| 260 | |
| 261 | writer(oneRowP, srcRow, width, colors); |
| 262 | row_pointer[0] = oneRowP; |
| 263 | (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); |
| 264 | srcRow = (const void*)((const char*)srcRow + bm.rowBytes()); |
| 265 | } |
| 266 | |
| 267 | jpeg_finish_compress(&cinfo); |
| 268 | jpeg_destroy_compress(&cinfo); |
| 269 | |
| 270 | return true; |
| 271 | } |
| 272 | }; |
| 273 | |
| 274 | /////////////////////////////////////////////////////////////////////////////// |
robertphillips@google.com | ec51cb8 | 2012-03-23 18:13:47 +0000 | [diff] [blame] | 275 | DEFINE_ENCODER_CREATOR(JPEGImageEncoder); |
| 276 | /////////////////////////////////////////////////////////////////////////////// |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 277 | |
robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 278 | static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 279 | return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr; |
tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 280 | } |
| 281 | |
mtklein@google.com | bd6343b | 2013-09-04 17:20:18 +0000 | [diff] [blame] | 282 | static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory); |