blob: fd88a5c8f6f0d01e6b70a0d50323514fb3882eff [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 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
38 bool setParams(const SkImageInfo& srcInfo);
39
40 jpeg_compress_struct* cinfo() { return &fCInfo; }
41
42 jmp_buf& jmpBuf() { return fErrMgr.fJmpBuf; }
43
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
68bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo) {
69 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
70 int numComponents = 0;
71 switch (srcInfo.colorType()) {
Matt Sarette95941f2017-01-27 18:16:40 -050072 case kRGBA_8888_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040073 jpegColorType = JCS_EXT_RGBA;
74 numComponents = 4;
75 break;
Matt Sarette95941f2017-01-27 18:16:40 -050076 case kBGRA_8888_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040077 jpegColorType = JCS_EXT_BGRA;
78 numComponents = 4;
79 break;
reed6c225732014-06-09 19:52:07 -070080 case kRGB_565_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040081 fProc = transform_scanline_565;
82 jpegColorType = JCS_RGB;
83 numComponents = 3;
84 break;
reed6c225732014-06-09 19:52:07 -070085 case kARGB_4444_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040086 fProc = transform_scanline_444;
87 jpegColorType = JCS_RGB;
88 numComponents = 3;
89 break;
reed6c225732014-06-09 19:52:07 -070090 case kIndex_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040091 fProc = transform_scanline_index8_opaque;
92 jpegColorType = JCS_RGB;
93 numComponents = 3;
94 break;
Matt Sarette95941f2017-01-27 18:16:40 -050095 case kGray_8_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -040096 SkASSERT(srcInfo.isOpaque());
97 jpegColorType = JCS_GRAYSCALE;
98 numComponents = 1;
99 break;
Matt Sarette95941f2017-01-27 18:16:40 -0500100 case kRGBA_F16_SkColorType:
Matt Sarett26b44df2017-05-02 16:04:56 -0400101 if (!srcInfo.colorSpace() || !srcInfo.colorSpace()->gammaIsLinear()) {
Matt Sarette95941f2017-01-27 18:16:40 -0500102 return false;
103 }
104
Matt Sarett26b44df2017-05-02 16:04:56 -0400105 fProc = transform_scanline_F16_to_8888;
106 jpegColorType = JCS_EXT_RGBA;
107 numComponents = 4;
108 break;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000109 default:
Matt Sarette95941f2017-01-27 18:16:40 -0500110 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000111 }
Matt Sarette95941f2017-01-27 18:16:40 -0500112
Matt Sarett26b44df2017-05-02 16:04:56 -0400113 fCInfo.image_width = srcInfo.width();
114 fCInfo.image_height = srcInfo.height();
115 fCInfo.in_color_space = jpegColorType;
116 fCInfo.input_components = numComponents;
117 jpeg_set_defaults(&fCInfo);
Hal Canary1fcc4042016-11-30 17:07:59 -0500118
119 // Tells libjpeg-turbo to compute optimal Huffman coding tables
120 // for the image. This improves compression at the cost of
121 // slower encode performance.
Matt Sarett26b44df2017-05-02 16:04:56 -0400122 fCInfo.optimize_coding = TRUE;
123 return true;
124}
Hal Canary1fcc4042016-11-30 17:07:59 -0500125
Matt Sarett26b44df2017-05-02 16:04:56 -0400126std::unique_ptr<SkJpegEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
127 const Options& options) {
Matt Sarettc367d032017-05-05 11:13:26 -0400128 if (!SkPixmapIsValid(src, SkTransferFunctionBehavior::kIgnore)) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400129 return nullptr;
130 }
131
132 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
133 if (setjmp(encoderMgr->jmpBuf())) {
134 return nullptr;
135 }
136
137 if (!encoderMgr->setParams(src.info())) {
138 return nullptr;
139 }
140
141 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
142 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
143
144 if (src.colorSpace()) {
145 sk_sp<SkData> icc = icc_from_color_space(*src.colorSpace());
Matt Sarett5df93de2017-03-22 21:52:47 +0000146 if (icc) {
147 // Create a contiguous block of memory with the icc signature followed by the profile.
148 sk_sp<SkData> markerData =
149 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
150 uint8_t* ptr = (uint8_t*) markerData->writable_data();
151 memcpy(ptr, kICCSig, sizeof(kICCSig));
152 ptr += sizeof(kICCSig);
153 *ptr++ = 1; // This is the first marker.
154 *ptr++ = 1; // Out of one total markers.
155 memcpy(ptr, icc->data(), icc->size());
156
Matt Sarett26b44df2017-05-02 16:04:56 -0400157 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(),
158 markerData->size());
Matt Sarett5df93de2017-03-22 21:52:47 +0000159 }
160 }
161
Matt Sarettc367d032017-05-05 11:13:26 -0400162 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
Matt Sarett26b44df2017-05-02 16:04:56 -0400163}
164
Matt Sarettc367d032017-05-05 11:13:26 -0400165SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
166 : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
167 , fEncoderMgr(std::move(encoderMgr))
Matt Sarett26b44df2017-05-02 16:04:56 -0400168{}
169
Matt Sarettc367d032017-05-05 11:13:26 -0400170SkJpegEncoder::~SkJpegEncoder() {}
Matt Sarett26b44df2017-05-02 16:04:56 -0400171
Matt Sarettc367d032017-05-05 11:13:26 -0400172bool SkJpegEncoder::onEncodeRows(int numRows) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400173 if (setjmp(fEncoderMgr->jmpBuf())) {
Matt Sarett26b44df2017-05-02 16:04:56 -0400174 return false;
175 }
176
177 const void* srcRow = fSrc.addr(0, fCurrRow);
178 const SkPMColor* colors = fSrc.ctable() ? fSrc.ctable()->readColors() : nullptr;
179 for (int i = 0; i < numRows; i++) {
Matt Sarette95941f2017-01-27 18:16:40 -0500180 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
Matt Sarett26b44df2017-05-02 16:04:56 -0400181 if (fEncoderMgr->proc()) {
182 fEncoderMgr->proc()((char*)fStorage.get(), (const char*)srcRow, fSrc.width(),
183 fEncoderMgr->cinfo()->input_components, colors);
184 jpegSrcRow = fStorage.get();
Matt Sarette95941f2017-01-27 18:16:40 -0500185 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500186
Matt Sarett26b44df2017-05-02 16:04:56 -0400187 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
188 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
Hal Canary1fcc4042016-11-30 17:07:59 -0500189 }
190
Matt Sarett26b44df2017-05-02 16:04:56 -0400191 fCurrRow += numRows;
192 if (fCurrRow == fSrc.height()) {
193 jpeg_finish_compress(fEncoderMgr->cinfo());
194 }
Hal Canary1fcc4042016-11-30 17:07:59 -0500195
196 return true;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000197}
Matt Sarett26b44df2017-05-02 16:04:56 -0400198
199bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
200 auto encoder = SkJpegEncoder::Make(dst, src, options);
201 return encoder.get() && encoder->encodeRows(src.height());
202}
203
Hal Canary1fcc4042016-11-30 17:07:59 -0500204#endif