blob: b87d73a6a909460df486594d4945d1cee82ac2f2 [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
Leon Scroggins IIIa77f30c2020-03-09 14:23:30 -040010#ifdef SK_ENCODE_JPEG
Mike Kleinab737e42019-05-15 21:58:15 +000011
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkStream.h"
13#include "include/encode/SkJpegEncoder.h"
14#include "include/private/SkColorData.h"
15#include "include/private/SkImageInfoPriv.h"
16#include "include/private/SkTemplates.h"
Leon Scroggins III112ac7f2019-07-08 14:08:50 -040017#include "src/core/SkMSAN.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/images/SkImageEncoderFns.h"
19#include "src/images/SkJPEGWriteUtility.h"
halcanary@google.comfed30372013-10-04 12:46:45 +000020
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000021#include <stdio.h>
Hal Canarydb683012016-11-23 08:55:18 -070022
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000023extern "C" {
24 #include "jpeglib.h"
25 #include "jerror.h"
26}
27
Matt Sarettc367d032017-05-05 11:13:26 -040028class SkJpegEncoderMgr final : SkNoncopyable {
Matt Sarett26b44df2017-05-02 16:04:56 -040029public:
30
31 /*
32 * Create the decode manager
33 * Does not take ownership of stream
34 */
35 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
36 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
37 }
38
Matt Sarett2e61b182017-05-09 12:46:50 -040039 bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
Matt Sarett26b44df2017-05-02 16:04:56 -040040
41 jpeg_compress_struct* cinfo() { return &fCInfo; }
42
Chris Dalton3e794592017-12-01 13:11:09 -070043 skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
Matt Sarett26b44df2017-05-02 16:04:56 -040044
45 transform_scanline_proc proc() const { return fProc; }
46
47 ~SkJpegEncoderMgr() {
48 jpeg_destroy_compress(&fCInfo);
49 }
50
51private:
52
53 SkJpegEncoderMgr(SkWStream* stream)
54 : fDstMgr(stream)
55 , fProc(nullptr)
56 {
57 fCInfo.err = jpeg_std_error(&fErrMgr);
58 fErrMgr.error_exit = skjpeg_error_exit;
59 jpeg_create_compress(&fCInfo);
60 fCInfo.dest = &fDstMgr;
61 }
62
63 jpeg_compress_struct fCInfo;
64 skjpeg_error_mgr fErrMgr;
65 skjpeg_destination_mgr fDstMgr;
66 transform_scanline_proc fProc;
67};
68
Matt Sarett2e61b182017-05-09 12:46:50 -040069bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
70{
71 auto chooseProc8888 = [&]() {
Mike Kleine8354b22018-11-02 14:23:45 -040072 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
73 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
74 return transform_scanline_to_premul_legacy;
Matt Sarett2e61b182017-05-09 12:46:50 -040075 }
Mike Kleine8354b22018-11-02 14:23:45 -040076 return (transform_scanline_proc) nullptr;
Matt Sarett2e61b182017-05-09 12:46:50 -040077 };
78
Matt Sarett26b44df2017-05-02 16:04:56 -040079 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
80 int numComponents = 0;
81 switch (srcInfo.colorType()) {
Matt Sarette95941f2017-01-27 18:16:40 -050082 case kRGBA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040083 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040084 jpegColorType = JCS_EXT_RGBA;
85 numComponents = 4;
86 break;
Matt Sarette95941f2017-01-27 18:16:40 -050087 case kBGRA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040088 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040089 jpegColorType = JCS_EXT_BGRA;
90 numComponents = 4;
91 break;
reed6c225732014-06-09 19:52:07 -070092 case kRGB_565_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040093 fProc = transform_scanline_565;
94 jpegColorType = JCS_RGB;
95 numComponents = 3;
96 break;
reed6c225732014-06-09 19:52:07 -070097 case kARGB_4444_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040098 if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
99 return false;
100 }
101
Matt Sarett26b44df2017-05-02 16:04:56 -0400102 fProc = transform_scanline_444;
103 jpegColorType = JCS_RGB;
104 numComponents = 3;
105 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500106 case kGray_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400107 SkASSERT(srcInfo.isOpaque());
108 jpegColorType = JCS_GRAYSCALE;
109 numComponents = 1;
110 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500111 case kRGBA_F16_SkColorType:
Mike Kleine8354b22018-11-02 14:23:45 -0400112 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
113 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
Matt Sarett2e61b182017-05-09 12:46:50 -0400114 fProc = transform_scanline_F16_to_premul_8888;
Mike Kleine8354b22018-11-02 14:23:45 -0400115 } else {
116 fProc = transform_scanline_F16_to_8888;
Matt Sarett2e61b182017-05-09 12:46:50 -0400117 }
Matt Sarett26b44df2017-05-02 16:04:56 -0400118 jpegColorType = JCS_EXT_RGBA;
119 numComponents = 4;
120 break;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000121 default:
Matt Sarette95941f2017-01-27 18:16:40 -0500122 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000123 }
Matt Sarette95941f2017-01-27 18:16:40 -0500124
Matt Sarett26b44df2017-05-02 16:04:56 -0400125 fCInfo.image_width = srcInfo.width();
126 fCInfo.image_height = srcInfo.height();
127 fCInfo.in_color_space = jpegColorType;
128 fCInfo.input_components = numComponents;
129 jpeg_set_defaults(&fCInfo);
Hal Canary1fcc4042016-11-30 17:07:59 -0500130
Matt Sarettfe319082017-05-09 14:02:10 -0400131 if (kGray_8_SkColorType != srcInfo.colorType()) {
132 switch (options.fDownsample) {
133 case SkJpegEncoder::Downsample::k420:
134 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
135 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
136 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
137 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
138 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
139 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
140 break;
141 case SkJpegEncoder::Downsample::k422:
142 fCInfo.comp_info[0].h_samp_factor = 2;
143 fCInfo.comp_info[0].v_samp_factor = 1;
144 fCInfo.comp_info[1].h_samp_factor = 1;
145 fCInfo.comp_info[1].v_samp_factor = 1;
146 fCInfo.comp_info[2].h_samp_factor = 1;
147 fCInfo.comp_info[2].v_samp_factor = 1;
148 break;
149 case SkJpegEncoder::Downsample::k444:
150 fCInfo.comp_info[0].h_samp_factor = 1;
151 fCInfo.comp_info[0].v_samp_factor = 1;
152 fCInfo.comp_info[1].h_samp_factor = 1;
153 fCInfo.comp_info[1].v_samp_factor = 1;
154 fCInfo.comp_info[2].h_samp_factor = 1;
155 fCInfo.comp_info[2].v_samp_factor = 1;
156 break;
157 }
158 }
159
Hal Canary1fcc4042016-11-30 17:07:59 -0500160 // Tells libjpeg-turbo to compute optimal Huffman coding tables
161 // for the image. This improves compression at the cost of
162 // slower encode performance.
Matt Sarett26b44df2017-05-02 16:04:56 -0400163 fCInfo.optimize_coding = TRUE;
164 return true;
165}
Hal Canary1fcc4042016-11-30 17:07:59 -0500166
Matt Sarett6a4dc662017-05-11 09:32:59 -0400167std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
168 const Options& options) {
Brian Osmane1adc3a2018-06-04 09:21:17 -0400169 if (!SkPixmapIsValid(src)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400170 return nullptr;
171 }
172
173 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
Chris Dalton3e794592017-12-01 13:11:09 -0700174
175 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
176 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400177 return nullptr;
178 }
179
Matt Sarett2e61b182017-05-09 12:46:50 -0400180 if (!encoderMgr->setParams(src.info(), options)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400181 return nullptr;
182 }
183
184 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
185 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
186
Matt Sarett1950e0a2017-06-12 16:17:30 -0400187 sk_sp<SkData> icc = icc_from_color_space(src.info());
188 if (icc) {
189 // Create a contiguous block of memory with the icc signature followed by the profile.
190 sk_sp<SkData> markerData =
191 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
192 uint8_t* ptr = (uint8_t*) markerData->writable_data();
193 memcpy(ptr, kICCSig, sizeof(kICCSig));
194 ptr += sizeof(kICCSig);
195 *ptr++ = 1; // This is the first marker.
196 *ptr++ = 1; // Out of one total markers.
197 memcpy(ptr, icc->data(), icc->size());
Mike Klein5e819ca2017-06-12 13:07:22 -0400198
Matt Sarett1950e0a2017-06-12 16:17:30 -0400199 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
Matt Sarett5df93de2017-03-22 21:52:47 +0000200 }
201
Matt Sarettc367d032017-05-05 11:13:26 -0400202 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
Matt Sarett26b44df2017-05-02 16:04:56 -0400203}
204
Matt Sarettc367d032017-05-05 11:13:26 -0400205SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
206 : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
207 , fEncoderMgr(std::move(encoderMgr))
Matt Sarett26b44df2017-05-02 16:04:56 -0400208{}
209
Matt Sarettc367d032017-05-05 11:13:26 -0400210SkJpegEncoder::~SkJpegEncoder() {}
Matt Sarett26b44df2017-05-02 16:04:56 -0400211
Matt Sarettc367d032017-05-05 11:13:26 -0400212bool SkJpegEncoder::onEncodeRows(int numRows) {
Chris Dalton3e794592017-12-01 13:11:09 -0700213 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
214 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400215 return false;
216 }
217
Leon Scroggins III112ac7f2019-07-08 14:08:50 -0400218 const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width();
219 const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width();
220
Matt Sarett26b44df2017-05-02 16:04:56 -0400221 const void* srcRow = fSrc.addr(0, fCurrRow);
Matt Sarett26b44df2017-05-02 16:04:56 -0400222 for (int i = 0; i < numRows; i++) {
Matt Sarette95941f2017-01-27 18:16:40 -0500223 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
Matt Sarett26b44df2017-05-02 16:04:56 -0400224 if (fEncoderMgr->proc()) {
Leon Scroggins III112ac7f2019-07-08 14:08:50 -0400225 sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes));
Mike Klein0c904fa2018-11-02 12:24:15 -0400226 fEncoderMgr->proc()((char*)fStorage.get(),
227 (const char*)srcRow,
228 fSrc.width(),
229 fEncoderMgr->cinfo()->input_components);
Matt Sarett26b44df2017-05-02 16:04:56 -0400230 jpegSrcRow = fStorage.get();
Leon Scroggins III112ac7f2019-07-08 14:08:50 -0400231 sk_msan_assert_initialized(jpegSrcRow,
232 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
233 } else {
234 // Same as above, but this repetition allows determining whether a
235 // proc was used when msan asserts.
236 sk_msan_assert_initialized(jpegSrcRow,
237 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
Matt Sarette95941f2017-01-27 18:16:40 -0500238 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500239
Matt Sarett26b44df2017-05-02 16:04:56 -0400240 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
241 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
Hal Canary1fcc4042016-11-30 17:07:59 -0500242 }
243
Matt Sarett26b44df2017-05-02 16:04:56 -0400244 fCurrRow += numRows;
245 if (fCurrRow == fSrc.height()) {
246 jpeg_finish_compress(fEncoderMgr->cinfo());
247 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500248
249 return true;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000250}
Matt Sarett26b44df2017-05-02 16:04:56 -0400251
252bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
253 auto encoder = SkJpegEncoder::Make(dst, src, options);
254 return encoder.get() && encoder->encodeRows(src.height());
255}
Mike Kleinab737e42019-05-15 21:58:15 +0000256
257#endif