blob: d6828b42e8012172b2674734183ec68e943b46a2 [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
Cary Clarka4083c92017-09-15 11:59:23 -040012#include "SkColorData.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 Sarettc367d032017-05-05 11:13:26 -040027class SkJpegEncoderMgr final : SkNoncopyable {
Matt Sarett26b44df2017-05-02 16:04:56 -040028public:
29
30 /*
31 * Create the decode manager
32 * Does not take ownership of stream
33 */
34 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
35 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
36 }
37
Matt Sarett2e61b182017-05-09 12:46:50 -040038 bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
Matt Sarett26b44df2017-05-02 16:04:56 -040039
40 jpeg_compress_struct* cinfo() { return &fCInfo; }
41
Chris Dalton3e794592017-12-01 13:11:09 -070042 skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
Matt Sarett26b44df2017-05-02 16:04:56 -040043
44 transform_scanline_proc proc() const { return fProc; }
45
46 ~SkJpegEncoderMgr() {
47 jpeg_destroy_compress(&fCInfo);
48 }
49
50private:
51
52 SkJpegEncoderMgr(SkWStream* stream)
53 : fDstMgr(stream)
54 , fProc(nullptr)
55 {
56 fCInfo.err = jpeg_std_error(&fErrMgr);
57 fErrMgr.error_exit = skjpeg_error_exit;
58 jpeg_create_compress(&fCInfo);
59 fCInfo.dest = &fDstMgr;
60 }
61
62 jpeg_compress_struct fCInfo;
63 skjpeg_error_mgr fErrMgr;
64 skjpeg_destination_mgr fDstMgr;
65 transform_scanline_proc fProc;
66};
67
Matt Sarett2e61b182017-05-09 12:46:50 -040068bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
69{
70 auto chooseProc8888 = [&]() {
71 if (kUnpremul_SkAlphaType != srcInfo.alphaType() ||
72 SkJpegEncoder::AlphaOption::kIgnore == options.fAlphaOption)
73 {
74 return (transform_scanline_proc) nullptr;
75 }
76
Brian Osmanb62f50c2018-07-12 14:44:27 -040077 return transform_scanline_to_premul_legacy;
Matt Sarett2e61b182017-05-09 12:46:50 -040078 };
79
Matt Sarett26b44df2017-05-02 16:04:56 -040080 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
81 int numComponents = 0;
82 switch (srcInfo.colorType()) {
Matt Sarette95941f2017-01-27 18:16:40 -050083 case kRGBA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040084 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040085 jpegColorType = JCS_EXT_RGBA;
86 numComponents = 4;
87 break;
Matt Sarette95941f2017-01-27 18:16:40 -050088 case kBGRA_8888_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040089 fProc = chooseProc8888();
Matt Sarett26b44df2017-05-02 16:04:56 -040090 jpegColorType = JCS_EXT_BGRA;
91 numComponents = 4;
92 break;
reed6c225732014-06-09 19:52:07 -070093 case kRGB_565_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040094 fProc = transform_scanline_565;
95 jpegColorType = JCS_RGB;
96 numComponents = 3;
97 break;
reed6c225732014-06-09 19:52:07 -070098 case kARGB_4444_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -040099 if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
100 return false;
101 }
102
Matt Sarett26b44df2017-05-02 16:04:56 -0400103 fProc = transform_scanline_444;
104 jpegColorType = JCS_RGB;
105 numComponents = 3;
106 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500107 case kGray_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400108 SkASSERT(srcInfo.isOpaque());
109 jpegColorType = JCS_GRAYSCALE;
110 numComponents = 1;
111 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500112 case kRGBA_F16_SkColorType:
Matt Sarett2e61b182017-05-09 12:46:50 -0400113 if (kUnpremul_SkAlphaType != srcInfo.alphaType() ||
114 SkJpegEncoder::AlphaOption::kIgnore == options.fAlphaOption)
115 {
116 fProc = transform_scanline_F16_to_8888;
117 } else {
118 fProc = transform_scanline_F16_to_premul_8888;
119 }
Matt Sarett26b44df2017-05-02 16:04:56 -0400120 jpegColorType = JCS_EXT_RGBA;
121 numComponents = 4;
122 break;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000123 default:
Matt Sarette95941f2017-01-27 18:16:40 -0500124 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000125 }
Matt Sarette95941f2017-01-27 18:16:40 -0500126
Matt Sarett26b44df2017-05-02 16:04:56 -0400127 fCInfo.image_width = srcInfo.width();
128 fCInfo.image_height = srcInfo.height();
129 fCInfo.in_color_space = jpegColorType;
130 fCInfo.input_components = numComponents;
131 jpeg_set_defaults(&fCInfo);
Hal Canary1fcc4042016-11-30 17:07:59 -0500132
Matt Sarettfe319082017-05-09 14:02:10 -0400133 if (kGray_8_SkColorType != srcInfo.colorType()) {
134 switch (options.fDownsample) {
135 case SkJpegEncoder::Downsample::k420:
136 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
137 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
138 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
139 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
140 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
141 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
142 break;
143 case SkJpegEncoder::Downsample::k422:
144 fCInfo.comp_info[0].h_samp_factor = 2;
145 fCInfo.comp_info[0].v_samp_factor = 1;
146 fCInfo.comp_info[1].h_samp_factor = 1;
147 fCInfo.comp_info[1].v_samp_factor = 1;
148 fCInfo.comp_info[2].h_samp_factor = 1;
149 fCInfo.comp_info[2].v_samp_factor = 1;
150 break;
151 case SkJpegEncoder::Downsample::k444:
152 fCInfo.comp_info[0].h_samp_factor = 1;
153 fCInfo.comp_info[0].v_samp_factor = 1;
154 fCInfo.comp_info[1].h_samp_factor = 1;
155 fCInfo.comp_info[1].v_samp_factor = 1;
156 fCInfo.comp_info[2].h_samp_factor = 1;
157 fCInfo.comp_info[2].v_samp_factor = 1;
158 break;
159 }
160 }
161
Hal Canary1fcc4042016-11-30 17:07:59 -0500162 // Tells libjpeg-turbo to compute optimal Huffman coding tables
163 // for the image. This improves compression at the cost of
164 // slower encode performance.
Matt Sarett26b44df2017-05-02 16:04:56 -0400165 fCInfo.optimize_coding = TRUE;
166 return true;
167}
Hal Canary1fcc4042016-11-30 17:07:59 -0500168
Matt Sarett6a4dc662017-05-11 09:32:59 -0400169std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
170 const Options& options) {
Brian Osmane1adc3a2018-06-04 09:21:17 -0400171 if (!SkPixmapIsValid(src)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400172 return nullptr;
173 }
174
175 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
Chris Dalton3e794592017-12-01 13:11:09 -0700176
177 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
178 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400179 return nullptr;
180 }
181
Matt Sarett2e61b182017-05-09 12:46:50 -0400182 if (!encoderMgr->setParams(src.info(), options)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400183 return nullptr;
184 }
185
186 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
187 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
188
Matt Sarett1950e0a2017-06-12 16:17:30 -0400189 sk_sp<SkData> icc = icc_from_color_space(src.info());
190 if (icc) {
191 // Create a contiguous block of memory with the icc signature followed by the profile.
192 sk_sp<SkData> markerData =
193 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
194 uint8_t* ptr = (uint8_t*) markerData->writable_data();
195 memcpy(ptr, kICCSig, sizeof(kICCSig));
196 ptr += sizeof(kICCSig);
197 *ptr++ = 1; // This is the first marker.
198 *ptr++ = 1; // Out of one total markers.
199 memcpy(ptr, icc->data(), icc->size());
Mike Klein5e819ca2017-06-12 13:07:22 -0400200
Matt Sarett1950e0a2017-06-12 16:17:30 -0400201 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
Matt Sarett5df93de2017-03-22 21:52:47 +0000202 }
203
Matt Sarettc367d032017-05-05 11:13:26 -0400204 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
Matt Sarett26b44df2017-05-02 16:04:56 -0400205}
206
Matt Sarettc367d032017-05-05 11:13:26 -0400207SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
208 : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
209 , fEncoderMgr(std::move(encoderMgr))
Matt Sarett26b44df2017-05-02 16:04:56 -0400210{}
211
Matt Sarettc367d032017-05-05 11:13:26 -0400212SkJpegEncoder::~SkJpegEncoder() {}
Matt Sarett26b44df2017-05-02 16:04:56 -0400213
Matt Sarettc367d032017-05-05 11:13:26 -0400214bool SkJpegEncoder::onEncodeRows(int numRows) {
Chris Dalton3e794592017-12-01 13:11:09 -0700215 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
216 if (setjmp(jmp)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400217 return false;
218 }
219
220 const void* srcRow = fSrc.addr(0, fCurrRow);
Matt Sarett26b44df2017-05-02 16:04:56 -0400221 for (int i = 0; i < numRows; i++) {
Matt Sarette95941f2017-01-27 18:16:40 -0500222 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
Matt Sarett26b44df2017-05-02 16:04:56 -0400223 if (fEncoderMgr->proc()) {
Mike Klein0c904fa2018-11-02 12:24:15 -0400224 fEncoderMgr->proc()((char*)fStorage.get(),
225 (const char*)srcRow,
226 fSrc.width(),
227 fEncoderMgr->cinfo()->input_components);
Matt Sarett26b44df2017-05-02 16:04:56 -0400228 jpegSrcRow = fStorage.get();
Matt Sarette95941f2017-01-27 18:16:40 -0500229 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500230
Matt Sarett26b44df2017-05-02 16:04:56 -0400231 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
232 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
Hal Canary1fcc4042016-11-30 17:07:59 -0500233 }
234
Matt Sarett26b44df2017-05-02 16:04:56 -0400235 fCurrRow += numRows;
236 if (fCurrRow == fSrc.height()) {
237 jpeg_finish_compress(fEncoderMgr->cinfo());
238 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500239
240 return true;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000241}
Matt Sarett26b44df2017-05-02 16:04:56 -0400242
243bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
244 auto encoder = SkJpegEncoder::Make(dst, src, options);
245 return encoder.get() && encoder->encodeRows(src.height());
246}
247
Hal Canary1fcc4042016-11-30 17:07:59 -0500248#endif