blob: e3e7600ade26c97fb3eed8221288368e6a4f50da [file] [log] [blame]
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001/*
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.comd33b26e2012-03-02 16:12:14 +00009#include "SkImageEncoder.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000010#include "SkColorPriv.h"
11#include "SkDither.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000012#include "SkStream.h"
13#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000014#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000015#include "SkUtils.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000016#include "SkRTConf.h"
djsollen@google.com11399402013-03-20 17:45:27 +000017#include "SkRect.h"
18#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000019
halcanary@google.comfed30372013-10-04 12:46:45 +000020
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000021#include <stdio.h>
msarettc1d03122016-03-25 08:58:55 -070022#include "SkJPEGWriteUtility.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000023extern "C" {
24 #include "jpeglib.h"
25 #include "jerror.h"
26}
27
msarette8597a42016-03-24 10:41:47 -070028// These enable timing code that report milliseconds for an encoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000029//#define TIME_ENCODE
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000030
31// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000032#define WE_CONVERT_TO_YUV
33
scroggo2a120802014-10-22 12:07:00 -070034///////////////////////////////////////////////////////////////////////////////
35
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000036#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
69static 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
83static 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
97static 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
113typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
114 const void* SK_RESTRICT src, int width,
115 const SkPMColor* SK_RESTRICT ctable);
116
117static 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
134static 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
151static 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
168static 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
185static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -0700186 switch (bm.colorType()) {
187 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000188 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -0700189 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000190 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -0700191 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000192 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -0700193 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000194 return Write_Index_YUV;
195 default:
halcanary96fcdcc2015-08-27 07:41:13 -0700196 return nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000197 }
198}
199
200class SkJPEGImageEncoder : public SkImageEncoder {
201protected:
202 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
203#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000204 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000205#endif
206
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000207 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700208 if (nullptr == bm.getPixels()) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000209 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
scroggo565901d2015-12-10 10:44:13 -0800217 SkAutoTMalloc<uint8_t> oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000218
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.comd33b26e2012-03-02 16:12:14 +0000224
mtklein@google.com8d725b22013-07-24 16:20:05 +0000225 // Keep after setjmp or mark volatile.
226 const WriteScanline writer = ChooseWriter(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700227 if (nullptr == writer) {
mtklein@google.com8d725b22013-07-24 16:20:05 +0000228 return false;
229 }
230
231 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000232 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);
benjaminwagner0a356202016-01-14 18:13:32 -0800244 cinfo.optimize_coding = TRUE;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000245 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000246#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000247 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000248#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000249
250 jpeg_start_compress(&cinfo, TRUE);
251
252 const int width = bm.width();
scroggo565901d2015-12-10 10:44:13 -0800253 uint8_t* oneRowP = oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000254
halcanary96fcdcc2015-08-27 07:41:13 -0700255 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000256 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.comec51cb82012-03-23 18:13:47 +0000275DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
276///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000277
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000278static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
halcanary96fcdcc2015-08-27 07:41:13 -0700279 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000280}
281
mtklein@google.combd6343b2013-09-04 17:20:18 +0000282static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);