blob: 97b8bbc30d905bb0e73c9bc079c569335bbbc976 [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
Hal Canarydb683012016-11-23 08:55:18 -07008#include "SkImageEncoderPriv.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00009
Hal Canarydb683012016-11-23 08:55:18 -070010#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000011#include "SkColorPriv.h"
12#include "SkDither.h"
Hal Canarydb683012016-11-23 08:55:18 -070013#include "SkJPEGWriteUtility.h"
14#include "SkRect.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000015#include "SkStream.h"
16#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000017#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000018#include "SkUtils.h"
halcanary@google.comfed30372013-10-04 12:46:45 +000019
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000020#include <stdio.h>
Hal Canarydb683012016-11-23 08:55:18 -070021
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000022extern "C" {
23 #include "jpeglib.h"
24 #include "jerror.h"
25}
26
msarette8597a42016-03-24 10:41:47 -070027// These enable timing code that report milliseconds for an encoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000028//#define TIME_ENCODE
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000029
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000030typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
31 const void* SK_RESTRICT src, int width,
32 const SkPMColor* SK_RESTRICT ctable);
33
msarettc7d01d32016-04-18 14:21:55 -070034static void Write_32_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000035 const void* SK_RESTRICT srcRow, int width,
36 const SkPMColor*) {
37 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
38 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000039 uint32_t c = *src++;
40 dst[0] = SkGetPackedR32(c);
41 dst[1] = SkGetPackedG32(c);
42 dst[2] = SkGetPackedB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000043 dst += 3;
44 }
45}
46
msarettc7d01d32016-04-18 14:21:55 -070047static void Write_4444_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000048 const void* SK_RESTRICT srcRow, int width,
49 const SkPMColor*) {
50 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
51 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000052 SkPMColor16 c = *src++;
53 dst[0] = SkPacked4444ToR32(c);
54 dst[1] = SkPacked4444ToG32(c);
55 dst[2] = SkPacked4444ToB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000056 dst += 3;
57 }
58}
59
msarettc7d01d32016-04-18 14:21:55 -070060static void Write_16_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000061 const void* SK_RESTRICT srcRow, int width,
62 const SkPMColor*) {
63 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
64 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000065 uint16_t c = *src++;
66 dst[0] = SkPacked16ToR32(c);
67 dst[1] = SkPacked16ToG32(c);
68 dst[2] = SkPacked16ToB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000069 dst += 3;
70 }
71}
72
msarettc7d01d32016-04-18 14:21:55 -070073static void Write_Index_RGB(uint8_t* SK_RESTRICT dst,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000074 const void* SK_RESTRICT srcRow, int width,
75 const SkPMColor* SK_RESTRICT ctable) {
76 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
77 while (--width >= 0) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000078 uint32_t c = ctable[*src++];
79 dst[0] = SkGetPackedR32(c);
80 dst[1] = SkGetPackedG32(c);
81 dst[2] = SkGetPackedB32(c);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000082 dst += 3;
83 }
84}
85
86static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -070087 switch (bm.colorType()) {
88 case kN32_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070089 return Write_32_RGB;
reed6c225732014-06-09 19:52:07 -070090 case kRGB_565_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070091 return Write_16_RGB;
reed6c225732014-06-09 19:52:07 -070092 case kARGB_4444_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070093 return Write_4444_RGB;
reed6c225732014-06-09 19:52:07 -070094 case kIndex_8_SkColorType:
msarettc7d01d32016-04-18 14:21:55 -070095 return Write_Index_RGB;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000096 default:
halcanary96fcdcc2015-08-27 07:41:13 -070097 return nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000098 }
99}
100
101class SkJPEGImageEncoder : public SkImageEncoder {
102protected:
103 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
104#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000105 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000106#endif
107
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000108 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700109 if (nullptr == bm.getPixels()) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000110 return false;
111 }
112
113 jpeg_compress_struct cinfo;
114 skjpeg_error_mgr sk_err;
115 skjpeg_destination_mgr sk_wstream(stream);
116
117 // allocate these before set call setjmp
scroggo565901d2015-12-10 10:44:13 -0800118 SkAutoTMalloc<uint8_t> oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000119
120 cinfo.err = jpeg_std_error(&sk_err);
121 sk_err.error_exit = skjpeg_error_exit;
122 if (setjmp(sk_err.fJmpBuf)) {
123 return false;
124 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000125
mtklein@google.com8d725b22013-07-24 16:20:05 +0000126 // Keep after setjmp or mark volatile.
127 const WriteScanline writer = ChooseWriter(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700128 if (nullptr == writer) {
mtklein@google.com8d725b22013-07-24 16:20:05 +0000129 return false;
130 }
131
132 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000133 cinfo.dest = &sk_wstream;
134 cinfo.image_width = bm.width();
135 cinfo.image_height = bm.height();
136 cinfo.input_components = 3;
msarettc7d01d32016-04-18 14:21:55 -0700137
138 // FIXME: Can we take advantage of other in_color_spaces in libjpeg-turbo?
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000139 cinfo.in_color_space = JCS_RGB;
msarettc7d01d32016-04-18 14:21:55 -0700140
141 // The gamma value is ignored by libjpeg-turbo.
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000142 cinfo.input_gamma = 1;
143
144 jpeg_set_defaults(&cinfo);
msarettc7d01d32016-04-18 14:21:55 -0700145
146 // Tells libjpeg-turbo to compute optimal Huffman coding tables
147 // for the image. This improves compression at the cost of
148 // slower encode performance.
benjaminwagner0a356202016-01-14 18:13:32 -0800149 cinfo.optimize_coding = TRUE;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000150 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000151
152 jpeg_start_compress(&cinfo, TRUE);
153
154 const int width = bm.width();
scroggo565901d2015-12-10 10:44:13 -0800155 uint8_t* oneRowP = oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000156
halcanary96fcdcc2015-08-27 07:41:13 -0700157 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000158 const void* srcRow = bm.getPixels();
159
160 while (cinfo.next_scanline < cinfo.image_height) {
161 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
162
163 writer(oneRowP, srcRow, width, colors);
164 row_pointer[0] = oneRowP;
165 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
166 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
167 }
168
169 jpeg_finish_compress(&cinfo);
170 jpeg_destroy_compress(&cinfo);
171
172 return true;
173 }
174};
175
176///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000177DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
178///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000179
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000180static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
Hal Canarydb683012016-11-23 08:55:18 -0700181 return (SkEncodedImageFormat::kJPEG == (SkEncodedImageFormat)t) ? new SkJPEGImageEncoder : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000182}
183
mtklein@google.combd6343b2013-09-04 17:20:18 +0000184static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);