blob: 0c3fd768d7711d6e1145000cb147e38ccc1e4898 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/images/SkImageEncoderPriv.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkStream.h"
11#include "include/encode/SkJpegEncoder.h"
12#include "include/private/SkColorData.h"
13#include "include/private/SkImageInfoPriv.h"
14#include "include/private/SkTemplates.h"
15#include "src/images/SkImageEncoderFns.h"
16#include "src/images/SkJPEGWriteUtility.h"
halcanary@google.comfed30372013-10-04 12:46:45 +000017
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000018#include <stdio.h>
Hal Canarydb683012016-11-23 08:55:18 -070019
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000020extern "C" {
21 #include "jpeglib.h"
22 #include "jerror.h"
23}
24
Matt Sarettc367d032017-05-05 11:13:26 -040025class SkJpegEncoderMgr final : SkNoncopyable {
Matt Sarett26b44df2017-05-02 16:04:56 -040026public:
27
28 /*
29 * Create the decode manager
30 * Does not take ownership of stream
31 */
32 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
33 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
34 }
35
Matt Sarett2e61b182017-05-09 12:46:50 -040036 bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
Matt Sarett26b44df2017-05-02 16:04:56 -040037
38 jpeg_compress_struct* cinfo() { return &fCInfo; }
39
Chris Dalton3e794592017-12-01 13:11:09 -070040 skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
Matt Sarett26b44df2017-05-02 16:04:56 -040041
42 transform_scanline_proc proc() const { return fProc; }
43
44 ~SkJpegEncoderMgr() {
45 jpeg_destroy_compress(&fCInfo);
46 }
47
48private:
49
50 SkJpegEncoderMgr(SkWStream* stream)
51 : fDstMgr(stream)
52 , fProc(nullptr)
53 {
54 fCInfo.err = jpeg_std_error(&fErrMgr);
55 fErrMgr.error_exit = skjpeg_error_exit;
56 jpeg_create_compress(&fCInfo);
57 fCInfo.dest = &fDstMgr;
58 }
59
60 jpeg_compress_struct fCInfo;
61 skjpeg_error_mgr fErrMgr;
62 skjpeg_destination_mgr fDstMgr;
63 transform_scanline_proc fProc;
64};
65
Matt Sarett2e61b182017-05-09 12:46:50 -040066bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
67{
68 auto chooseProc8888 = [&]() {
Mike Kleine8354b22018-11-02 14:23:45 -040069 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
70 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
71 return transform_scanline_to_premul_legacy;
Matt Sarett2e61b182017-05-09 12:46:50 -040072 }
Mike Kleine8354b22018-11-02 14:23:45 -040073 return (transform_scanline_proc) nullptr;
Matt Sarett2e61b182017-05-09 12:46:50 -040074 };
75
Matt Sarett26b44df2017-05-02 16:04:56 -040076 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
77 int numComponents = 0;
78 switch (srcInfo.colorType()) {
Matt Sarette95941f2017-01-27 18:16:40 -050079 case kRGBA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040080 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040081 jpegColorType = JCS_EXT_RGBA;
82 numComponents = 4;
83 break;
Matt Sarette95941f2017-01-27 18:16:40 -050084 case kBGRA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040085 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040086 jpegColorType = JCS_EXT_BGRA;
87 numComponents = 4;
88 break;
reed6c225732014-06-09 19:52:07 -070089 case kRGB_565_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040090 fProc = transform_scanline_565;
91 jpegColorType = JCS_RGB;
92 numComponents = 3;
93 break;
reed6c225732014-06-09 19:52:07 -070094 case kARGB_4444_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040095 if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
96 return false;
97 }
98
Matt Sarett26b44df2017-05-02 16:04:56 -040099 fProc = transform_scanline_444;
100 jpegColorType = JCS_RGB;
101 numComponents = 3;
102 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500103 case kGray_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400104 SkASSERT(srcInfo.isOpaque());
105 jpegColorType = JCS_GRAYSCALE;
106 numComponents = 1;
107 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500108 case kRGBA_F16_SkColorType:
Mike Kleine8354b22018-11-02 14:23:45 -0400109 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
110 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
Matt Sarett2e61b182017-05-09 12:46:50 -0400111 fProc = transform_scanline_F16_to_premul_8888;
Mike Kleine8354b22018-11-02 14:23:45 -0400112 } else {
113 fProc = transform_scanline_F16_to_8888;
Matt Sarett2e61b182017-05-09 12:46:50 -0400114 }
Matt Sarett26b44df2017-05-02 16:04:56 -0400115 jpegColorType = JCS_EXT_RGBA;
116 numComponents = 4;
117 break;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000118 default:
Matt Sarette95941f2017-01-27 18:16:40 -0500119 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000120 }
Matt Sarette95941f2017-01-27 18:16:40 -0500121
Matt Sarett26b44df2017-05-02 16:04:56 -0400122 fCInfo.image_width = srcInfo.width();
123 fCInfo.image_height = srcInfo.height();
124 fCInfo.in_color_space = jpegColorType;
125 fCInfo.input_components = numComponents;
126 jpeg_set_defaults(&fCInfo);
Hal Canary1fcc4042016-11-30 17:07:59 -0500127
Matt Sarettfe319082017-05-09 14:02:10 -0400128 if (kGray_8_SkColorType != srcInfo.colorType()) {
129 switch (options.fDownsample) {
130 case SkJpegEncoder::Downsample::k420:
131 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
132 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
133 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
134 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
135 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
136 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
137 break;
138 case SkJpegEncoder::Downsample::k422:
139 fCInfo.comp_info[0].h_samp_factor = 2;
140 fCInfo.comp_info[0].v_samp_factor = 1;
141 fCInfo.comp_info[1].h_samp_factor = 1;
142 fCInfo.comp_info[1].v_samp_factor = 1;
143 fCInfo.comp_info[2].h_samp_factor = 1;
144 fCInfo.comp_info[2].v_samp_factor = 1;
145 break;
146 case SkJpegEncoder::Downsample::k444:
147 fCInfo.comp_info[0].h_samp_factor = 1;
148 fCInfo.comp_info[0].v_samp_factor = 1;
149 fCInfo.comp_info[1].h_samp_factor = 1;
150 fCInfo.comp_info[1].v_samp_factor = 1;
151 fCInfo.comp_info[2].h_samp_factor = 1;
152 fCInfo.comp_info[2].v_samp_factor = 1;
153 break;
154 }
155 }
156
Hal Canary1fcc4042016-11-30 17:07:59 -0500157 // Tells libjpeg-turbo to compute optimal Huffman coding tables
158 // for the image. This improves compression at the cost of
159 // slower encode performance.
Matt Sarett26b44df2017-05-02 16:04:56 -0400160 fCInfo.optimize_coding = TRUE;
161 return true;
162}
Hal Canary1fcc4042016-11-30 17:07:59 -0500163
Matt Sarett6a4dc662017-05-11 09:32:59 -0400164std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
165 const Options& options) {
Brian Osmane1adc3a2018-06-04 09:21:17 -0400166 if (!SkPixmapIsValid(src)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400167 return nullptr;
168 }
169
170 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
Chris Dalton3e794592017-12-01 13:11:09 -0700171
172 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
173 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400174 return nullptr;
175 }
176
Matt Sarett2e61b182017-05-09 12:46:50 -0400177 if (!encoderMgr->setParams(src.info(), options)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400178 return nullptr;
179 }
180
181 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
182 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
183
Matt Sarett1950e0a2017-06-12 16:17:30 -0400184 sk_sp<SkData> icc = icc_from_color_space(src.info());
185 if (icc) {
186 // Create a contiguous block of memory with the icc signature followed by the profile.
187 sk_sp<SkData> markerData =
188 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
189 uint8_t* ptr = (uint8_t*) markerData->writable_data();
190 memcpy(ptr, kICCSig, sizeof(kICCSig));
191 ptr += sizeof(kICCSig);
192 *ptr++ = 1; // This is the first marker.
193 *ptr++ = 1; // Out of one total markers.
194 memcpy(ptr, icc->data(), icc->size());
Mike Klein5e819ca2017-06-12 13:07:22 -0400195
Matt Sarett1950e0a2017-06-12 16:17:30 -0400196 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
Matt Sarett5df93de2017-03-22 21:52:47 +0000197 }
198
Matt Sarettc367d032017-05-05 11:13:26 -0400199 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
Matt Sarett26b44df2017-05-02 16:04:56 -0400200}
201
Matt Sarettc367d032017-05-05 11:13:26 -0400202SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
203 : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
204 , fEncoderMgr(std::move(encoderMgr))
Matt Sarett26b44df2017-05-02 16:04:56 -0400205{}
206
Matt Sarettc367d032017-05-05 11:13:26 -0400207SkJpegEncoder::~SkJpegEncoder() {}
Matt Sarett26b44df2017-05-02 16:04:56 -0400208
Matt Sarettc367d032017-05-05 11:13:26 -0400209bool SkJpegEncoder::onEncodeRows(int numRows) {
Chris Dalton3e794592017-12-01 13:11:09 -0700210 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
211 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400212 return false;
213 }
214
215 const void* srcRow = fSrc.addr(0, fCurrRow);
Matt Sarett26b44df2017-05-02 16:04:56 -0400216 for (int i = 0; i < numRows; i++) {
Matt Sarette95941f2017-01-27 18:16:40 -0500217 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
Matt Sarett26b44df2017-05-02 16:04:56 -0400218 if (fEncoderMgr->proc()) {
Mike Klein0c904fa2018-11-02 12:24:15 -0400219 fEncoderMgr->proc()((char*)fStorage.get(),
220 (const char*)srcRow,
221 fSrc.width(),
222 fEncoderMgr->cinfo()->input_components);
Matt Sarett26b44df2017-05-02 16:04:56 -0400223 jpegSrcRow = fStorage.get();
Matt Sarette95941f2017-01-27 18:16:40 -0500224 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500225
Matt Sarett26b44df2017-05-02 16:04:56 -0400226 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
227 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
Hal Canary1fcc4042016-11-30 17:07:59 -0500228 }
229
Matt Sarett26b44df2017-05-02 16:04:56 -0400230 fCurrRow += numRows;
231 if (fCurrRow == fSrc.height()) {
232 jpeg_finish_compress(fEncoderMgr->cinfo());
233 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500234
235 return true;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000236}
Matt Sarett26b44df2017-05-02 16:04:56 -0400237
238bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
239 auto encoder = SkJpegEncoder::Make(dst, src, options);
240 return encoder.get() && encoder->encodeRows(src.height());
241}