blob: 0603447e92e27c268faba19e82bb08344fe130b4 [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 Canary1fcc4042016-11-30 17:07:59 -050010#ifdef SK_HAS_JPEG_LIBRARY
11
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000012#include "SkColorPriv.h"
Matt Sarett5df93de2017-03-22 21:52:47 +000013#include "SkImageEncoderFns.h"
Matt Sarett26b44df2017-05-02 16:04:56 -040014#include "SkImageInfoPriv.h"
15#include "SkJpegEncoder.h"
Hal Canarydb683012016-11-23 08:55:18 -070016#include "SkJPEGWriteUtility.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000017#include "SkStream.h"
18#include "SkTemplates.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
Matt Sarett26b44df2017-05-02 16:04:56 -040027// This warning triggers false postives way too often in here.
28#if defined(__GNUC__) && !defined(__clang__)
29 #pragma GCC diagnostic ignored "-Wclobbered"
30#endif
31
32class SkJpegEncoderMgr : SkNoncopyable {
33public:
34
35 /*
36 * Create the decode manager
37 * Does not take ownership of stream
38 */
39 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
40 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
41 }
42
43 bool setParams(const SkImageInfo& srcInfo);
44
45 jpeg_compress_struct* cinfo() { return &fCInfo; }
46
47 jmp_buf& jmpBuf() { return fErrMgr.fJmpBuf; }
48
49 transform_scanline_proc proc() const { return fProc; }
50
51 ~SkJpegEncoderMgr() {
52 jpeg_destroy_compress(&fCInfo);
53 }
54
55private:
56
57 SkJpegEncoderMgr(SkWStream* stream)
58 : fDstMgr(stream)
59 , fProc(nullptr)
60 {
61 fCInfo.err = jpeg_std_error(&fErrMgr);
62 fErrMgr.error_exit = skjpeg_error_exit;
63 jpeg_create_compress(&fCInfo);
64 fCInfo.dest = &fDstMgr;
65 }
66
67 jpeg_compress_struct fCInfo;
68 skjpeg_error_mgr fErrMgr;
69 skjpeg_destination_mgr fDstMgr;
70 transform_scanline_proc fProc;
71};
72
73bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo) {
74 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
75 int numComponents = 0;
76 switch (srcInfo.colorType()) {
Matt Sarette95941f2017-01-27 18:16:40 -050077 case kRGBA_8888_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040078 jpegColorType = JCS_EXT_RGBA;
79 numComponents = 4;
80 break;
Matt Sarette95941f2017-01-27 18:16:40 -050081 case kBGRA_8888_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040082 jpegColorType = JCS_EXT_BGRA;
83 numComponents = 4;
84 break;
reed6c225732014-06-09 19:52:07 -070085 case kRGB_565_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040086 fProc = transform_scanline_565;
87 jpegColorType = JCS_RGB;
88 numComponents = 3;
89 break;
reed6c225732014-06-09 19:52:07 -070090 case kARGB_4444_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040091 fProc = transform_scanline_444;
92 jpegColorType = JCS_RGB;
93 numComponents = 3;
94 break;
reed6c225732014-06-09 19:52:07 -070095 case kIndex_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040096 fProc = transform_scanline_index8_opaque;
97 jpegColorType = JCS_RGB;
98 numComponents = 3;
99 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500100 case kGray_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400101 SkASSERT(srcInfo.isOpaque());
102 jpegColorType = JCS_GRAYSCALE;
103 numComponents = 1;
104 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500105 case kRGBA_F16_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400106 if (!srcInfo.colorSpace() || !srcInfo.colorSpace()->gammaIsLinear()) {
Matt Sarette95941f2017-01-27 18:16:40 -0500107 return false;
108 }
109
Matt Sarett26b44df2017-05-02 16:04:56 -0400110 fProc = transform_scanline_F16_to_8888;
111 jpegColorType = JCS_EXT_RGBA;
112 numComponents = 4;
113 break;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000114 default:
Matt Sarette95941f2017-01-27 18:16:40 -0500115 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000116 }
Matt Sarette95941f2017-01-27 18:16:40 -0500117
Matt Sarett26b44df2017-05-02 16:04:56 -0400118 fCInfo.image_width = srcInfo.width();
119 fCInfo.image_height = srcInfo.height();
120 fCInfo.in_color_space = jpegColorType;
121 fCInfo.input_components = numComponents;
122 jpeg_set_defaults(&fCInfo);
Hal Canary1fcc4042016-11-30 17:07:59 -0500123
124 // Tells libjpeg-turbo to compute optimal Huffman coding tables
125 // for the image. This improves compression at the cost of
126 // slower encode performance.
Matt Sarett26b44df2017-05-02 16:04:56 -0400127 fCInfo.optimize_coding = TRUE;
128 return true;
129}
Hal Canary1fcc4042016-11-30 17:07:59 -0500130
Matt Sarett26b44df2017-05-02 16:04:56 -0400131class SkJpegEncoder_Base : public SkJpegEncoder {
132public:
133 SkJpegEncoder_Base(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src);
Hal Canary1fcc4042016-11-30 17:07:59 -0500134
Matt Sarett26b44df2017-05-02 16:04:56 -0400135 bool onEncodeRows(int numRows);
136
137private:
138 std::unique_ptr<SkJpegEncoderMgr> fEncoderMgr;
139 SkPixmap fSrc;
140 int fCurrRow;
141 SkAutoTMalloc<uint8_t> fStorage;
142};
143
144std::unique_ptr<SkJpegEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
145 const Options& options) {
146 if (!SkImageInfoIsValidAllowNumericalCS(src.info()) || !src.addr() ||
147 src.rowBytes() < src.info().minRowBytes()) {
148 return nullptr;
149 }
150
151 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
152 if (setjmp(encoderMgr->jmpBuf())) {
153 return nullptr;
154 }
155
156 if (!encoderMgr->setParams(src.info())) {
157 return nullptr;
158 }
159
160 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
161 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
162
163 if (src.colorSpace()) {
164 sk_sp<SkData> icc = icc_from_color_space(*src.colorSpace());
Matt Sarett5df93de2017-03-22 21:52:47 +0000165 if (icc) {
166 // Create a contiguous block of memory with the icc signature followed by the profile.
167 sk_sp<SkData> markerData =
168 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
169 uint8_t* ptr = (uint8_t*) markerData->writable_data();
170 memcpy(ptr, kICCSig, sizeof(kICCSig));
171 ptr += sizeof(kICCSig);
172 *ptr++ = 1; // This is the first marker.
173 *ptr++ = 1; // Out of one total markers.
174 memcpy(ptr, icc->data(), icc->size());
175
Matt Sarett26b44df2017-05-02 16:04:56 -0400176 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(),
177 markerData->size());
Matt Sarett5df93de2017-03-22 21:52:47 +0000178 }
179 }
180
Matt Sarett26b44df2017-05-02 16:04:56 -0400181 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder_Base(std::move(encoderMgr), src));
182}
183
184
185SkJpegEncoder_Base::SkJpegEncoder_Base(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,
186 const SkPixmap& src)
187 : fEncoderMgr(std::move(encoderMgr))
188 , fSrc(src)
189 , fCurrRow(0)
190 , fStorage(fEncoderMgr->proc() ? fEncoderMgr->cinfo()->input_components*src.width() : 0)
191{}
192
193bool SkJpegEncoder::encodeRows(int numRows) {
194 return ((SkJpegEncoder_Base*) this)->onEncodeRows(numRows);
195}
196
197bool SkJpegEncoder_Base::onEncodeRows(int numRows) {
198 SkASSERT(numRows > 0 && fCurrRow < fSrc.height());
199 if (numRows <= 0 || fCurrRow >= fSrc.height()) {
200 return false;
Matt Sarette95941f2017-01-27 18:16:40 -0500201 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500202
Matt Sarett26b44df2017-05-02 16:04:56 -0400203 if (fCurrRow + numRows > fSrc.height()) {
204 numRows = fSrc.height() - fCurrRow;
205 }
206
207 if (setjmp(fEncoderMgr->jmpBuf())) {
208 // Short circuit any future calls after failing.
209 fCurrRow = fSrc.height();
210 return false;
211 }
212
213 const void* srcRow = fSrc.addr(0, fCurrRow);
214 const SkPMColor* colors = fSrc.ctable() ? fSrc.ctable()->readColors() : nullptr;
215 for (int i = 0; i < numRows; i++) {
Matt Sarette95941f2017-01-27 18:16:40 -0500216 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
Matt Sarett26b44df2017-05-02 16:04:56 -0400217 if (fEncoderMgr->proc()) {
218 fEncoderMgr->proc()((char*)fStorage.get(), (const char*)srcRow, fSrc.width(),
219 fEncoderMgr->cinfo()->input_components, colors);
220 jpegSrcRow = fStorage.get();
Matt Sarette95941f2017-01-27 18:16:40 -0500221 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500222
Matt Sarett26b44df2017-05-02 16:04:56 -0400223 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
224 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
Hal Canary1fcc4042016-11-30 17:07:59 -0500225 }
226
Matt Sarett26b44df2017-05-02 16:04:56 -0400227 fCurrRow += numRows;
228 if (fCurrRow == fSrc.height()) {
229 jpeg_finish_compress(fEncoderMgr->cinfo());
230 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500231
232 return true;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000233}
Matt Sarett26b44df2017-05-02 16:04:56 -0400234
235bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
236 auto encoder = SkJpegEncoder::Make(dst, src, options);
237 return encoder.get() && encoder->encodeRows(src.height());
238}
239
Hal Canary1fcc4042016-11-30 17:07:59 -0500240#endif