blob: 1051aec205db715695d1ccaeb130434a66374894 [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
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000031typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
32 const void* SK_RESTRICT src, int width,
33 const SkPMColor* SK_RESTRICT ctable);
34
msarettc7d01d32016-04-18 14:21:55 -070035static void Write_32_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000036 const void* SK_RESTRICT srcRow, int width,
37 const SkPMColor*) {
38 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
39 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000040 uint32_t c = *src++;
41 dst[0] = SkGetPackedR32(c);
42 dst[1] = SkGetPackedG32(c);
43 dst[2] = SkGetPackedB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000044 dst += 3;
45 }
46}
47
msarettc7d01d32016-04-18 14:21:55 -070048static void Write_4444_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000049 const void* SK_RESTRICT srcRow, int width,
50 const SkPMColor*) {
51 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
52 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000053 SkPMColor16 c = *src++;
54 dst[0] = SkPacked4444ToR32(c);
55 dst[1] = SkPacked4444ToG32(c);
56 dst[2] = SkPacked4444ToB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000057 dst += 3;
58 }
59}
60
msarettc7d01d32016-04-18 14:21:55 -070061static void Write_16_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000062 const void* SK_RESTRICT srcRow, int width,
63 const SkPMColor*) {
64 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
65 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000066 uint16_t c = *src++;
67 dst[0] = SkPacked16ToR32(c);
68 dst[1] = SkPacked16ToG32(c);
69 dst[2] = SkPacked16ToB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000070 dst += 3;
71 }
72}
73
msarettc7d01d32016-04-18 14:21:55 -070074static void Write_Index_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000075 const void* SK_RESTRICT srcRow, int width,
76 const SkPMColor* SK_RESTRICT ctable) {
77 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
78 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000079 uint32_t c = ctable[*src++];
80 dst[0] = SkGetPackedR32(c);
81 dst[1] = SkGetPackedG32(c);
82 dst[2] = SkGetPackedB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000083 dst += 3;
84 }
85}
86
87static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -070088 switch (bm.colorType()) {
89 case kN32_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070090 return Write_32_RGB;
reed6c225732014-06-09 19:52:07 -070091 case kRGB_565_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070092 return Write_16_RGB;
reed6c225732014-06-09 19:52:07 -070093 case kARGB_4444_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070094 return Write_4444_RGB;
reed6c225732014-06-09 19:52:07 -070095 case kIndex_8_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070096 return Write_Index_RGB;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000097 default:
halcanary96fcdcc2015-08-27 07:41:13 -070098 return nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000099 }
100}
101
102class SkJPEGImageEncoder : public SkImageEncoder {
103protected:
104 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
105#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000106 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000107#endif
108
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000109 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700110 if (nullptr == bm.getPixels()) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000111 return false;
112 }
113
114 jpeg_compress_struct cinfo;
115 skjpeg_error_mgr sk_err;
116 skjpeg_destination_mgr sk_wstream(stream);
117
118 // allocate these before set call setjmp
scroggo565901d2015-12-10 10:44:13 -0800119 SkAutoTMalloc<uint8_t> oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000120
121 cinfo.err = jpeg_std_error(&sk_err);
122 sk_err.error_exit = skjpeg_error_exit;
123 if (setjmp(sk_err.fJmpBuf)) {
124 return false;
125 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000126
mtklein@google.com8d725b22013-07-24 16:20:05 +0000127 // Keep after setjmp or mark volatile.
128 const WriteScanline writer = ChooseWriter(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700129 if (nullptr == writer) {
mtklein@google.com8d725b22013-07-24 16:20:05 +0000130 return false;
131 }
132
133 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000134 cinfo.dest = &sk_wstream;
135 cinfo.image_width = bm.width();
136 cinfo.image_height = bm.height();
137 cinfo.input_components = 3;
msarettc7d01d32016-04-18 14:21:55 -0700138
139 // FIXME: Can we take advantage of other in_color_spaces in libjpeg-turbo?
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000140 cinfo.in_color_space = JCS_RGB;
msarettc7d01d32016-04-18 14:21:55 -0700141
142 // The gamma value is ignored by libjpeg-turbo.
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000143 cinfo.input_gamma = 1;
144
145 jpeg_set_defaults(&cinfo);
msarettc7d01d32016-04-18 14:21:55 -0700146
147 // Tells libjpeg-turbo to compute optimal Huffman coding tables
148 // for the image. This improves compression at the cost of
149 // slower encode performance.
benjaminwagner0a356202016-01-14 18:13:32 -0800150 cinfo.optimize_coding = TRUE;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000151 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000152
153 jpeg_start_compress(&cinfo, TRUE);
154
155 const int width = bm.width();
scroggo565901d2015-12-10 10:44:13 -0800156 uint8_t* oneRowP = oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000157
halcanary96fcdcc2015-08-27 07:41:13 -0700158 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000159 const void* srcRow = bm.getPixels();
160
161 while (cinfo.next_scanline < cinfo.image_height) {
162 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
163
164 writer(oneRowP, srcRow, width, colors);
165 row_pointer[0] = oneRowP;
166 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
167 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
168 }
169
170 jpeg_finish_compress(&cinfo);
171 jpeg_destroy_compress(&cinfo);
172
173 return true;
174 }
175};
176
177///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000178DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
179///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000180
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000181static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
halcanary96fcdcc2015-08-27 07:41:13 -0700182 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000183}
184
mtklein@google.combd6343b2013-09-04 17:20:18 +0000185static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);