1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2007 The Android Open Source Project
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/images/SkImageEncoderPriv.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#ifdef SK_ENCODE_JPEG
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
13cb93a386Sopenharmony_ci#include "include/encode/SkJpegEncoder.h"
14cb93a386Sopenharmony_ci#include "include/private/SkColorData.h"
15cb93a386Sopenharmony_ci#include "include/private/SkImageInfoPriv.h"
16cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
17cb93a386Sopenharmony_ci#include "src/core/SkMSAN.h"
18cb93a386Sopenharmony_ci#include "src/images/SkImageEncoderFns.h"
19cb93a386Sopenharmony_ci#include "src/images/SkJPEGWriteUtility.h"
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci#include <stdio.h>
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ciextern "C" {
24cb93a386Sopenharmony_ci    #include "jpeglib.h"
25cb93a386Sopenharmony_ci    #include "jerror.h"
26cb93a386Sopenharmony_ci}
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciclass SkJpegEncoderMgr final : SkNoncopyable {
29cb93a386Sopenharmony_cipublic:
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_ci    /*
32cb93a386Sopenharmony_ci     * Create the decode manager
33cb93a386Sopenharmony_ci     * Does not take ownership of stream
34cb93a386Sopenharmony_ci     */
35cb93a386Sopenharmony_ci    static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
36cb93a386Sopenharmony_ci        return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
37cb93a386Sopenharmony_ci    }
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci    bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    jpeg_compress_struct* cinfo() { return &fCInfo; }
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci    skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_ci    transform_scanline_proc proc() const { return fProc; }
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci    ~SkJpegEncoderMgr() {
48cb93a386Sopenharmony_ci        jpeg_destroy_compress(&fCInfo);
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_ciprivate:
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci    SkJpegEncoderMgr(SkWStream* stream)
54cb93a386Sopenharmony_ci        : fDstMgr(stream)
55cb93a386Sopenharmony_ci        , fProc(nullptr)
56cb93a386Sopenharmony_ci    {
57cb93a386Sopenharmony_ci        fCInfo.err = jpeg_std_error(&fErrMgr);
58cb93a386Sopenharmony_ci        fErrMgr.error_exit = skjpeg_error_exit;
59cb93a386Sopenharmony_ci        jpeg_create_compress(&fCInfo);
60cb93a386Sopenharmony_ci        fCInfo.dest = &fDstMgr;
61cb93a386Sopenharmony_ci    }
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci    jpeg_compress_struct    fCInfo;
64cb93a386Sopenharmony_ci    skjpeg_error_mgr        fErrMgr;
65cb93a386Sopenharmony_ci    skjpeg_destination_mgr  fDstMgr;
66cb93a386Sopenharmony_ci    transform_scanline_proc fProc;
67cb93a386Sopenharmony_ci};
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_cibool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options)
70cb93a386Sopenharmony_ci{
71cb93a386Sopenharmony_ci    auto chooseProc8888 = [&]() {
72cb93a386Sopenharmony_ci        if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
73cb93a386Sopenharmony_ci                options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
74cb93a386Sopenharmony_ci            return transform_scanline_to_premul_legacy;
75cb93a386Sopenharmony_ci        }
76cb93a386Sopenharmony_ci        return (transform_scanline_proc) nullptr;
77cb93a386Sopenharmony_ci    };
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
80cb93a386Sopenharmony_ci    int numComponents = 0;
81cb93a386Sopenharmony_ci    switch (srcInfo.colorType()) {
82cb93a386Sopenharmony_ci        case kRGBA_8888_SkColorType:
83cb93a386Sopenharmony_ci            fProc = chooseProc8888();
84cb93a386Sopenharmony_ci            jpegColorType = JCS_EXT_RGBA;
85cb93a386Sopenharmony_ci            numComponents = 4;
86cb93a386Sopenharmony_ci            break;
87cb93a386Sopenharmony_ci        case kBGRA_8888_SkColorType:
88cb93a386Sopenharmony_ci            fProc = chooseProc8888();
89cb93a386Sopenharmony_ci            jpegColorType = JCS_EXT_BGRA;
90cb93a386Sopenharmony_ci            numComponents = 4;
91cb93a386Sopenharmony_ci            break;
92cb93a386Sopenharmony_ci        case kRGB_565_SkColorType:
93cb93a386Sopenharmony_ci            fProc = transform_scanline_565;
94cb93a386Sopenharmony_ci            jpegColorType = JCS_RGB;
95cb93a386Sopenharmony_ci            numComponents = 3;
96cb93a386Sopenharmony_ci            break;
97cb93a386Sopenharmony_ci        case kARGB_4444_SkColorType:
98cb93a386Sopenharmony_ci            if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) {
99cb93a386Sopenharmony_ci                return false;
100cb93a386Sopenharmony_ci            }
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci            fProc = transform_scanline_444;
103cb93a386Sopenharmony_ci            jpegColorType = JCS_RGB;
104cb93a386Sopenharmony_ci            numComponents = 3;
105cb93a386Sopenharmony_ci            break;
106cb93a386Sopenharmony_ci        case kGray_8_SkColorType:
107cb93a386Sopenharmony_ci            SkASSERT(srcInfo.isOpaque());
108cb93a386Sopenharmony_ci            jpegColorType = JCS_GRAYSCALE;
109cb93a386Sopenharmony_ci            numComponents = 1;
110cb93a386Sopenharmony_ci            break;
111cb93a386Sopenharmony_ci        case kRGBA_F16_SkColorType:
112cb93a386Sopenharmony_ci            if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
113cb93a386Sopenharmony_ci                    options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) {
114cb93a386Sopenharmony_ci                fProc = transform_scanline_F16_to_premul_8888;
115cb93a386Sopenharmony_ci            } else {
116cb93a386Sopenharmony_ci                fProc = transform_scanline_F16_to_8888;
117cb93a386Sopenharmony_ci            }
118cb93a386Sopenharmony_ci            jpegColorType = JCS_EXT_RGBA;
119cb93a386Sopenharmony_ci            numComponents = 4;
120cb93a386Sopenharmony_ci            break;
121cb93a386Sopenharmony_ci        default:
122cb93a386Sopenharmony_ci            return false;
123cb93a386Sopenharmony_ci    }
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_ci    fCInfo.image_width = srcInfo.width();
126cb93a386Sopenharmony_ci    fCInfo.image_height = srcInfo.height();
127cb93a386Sopenharmony_ci    fCInfo.in_color_space = jpegColorType;
128cb93a386Sopenharmony_ci    fCInfo.input_components = numComponents;
129cb93a386Sopenharmony_ci    jpeg_set_defaults(&fCInfo);
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci    if (kGray_8_SkColorType != srcInfo.colorType()) {
132cb93a386Sopenharmony_ci        switch (options.fDownsample) {
133cb93a386Sopenharmony_ci            case SkJpegEncoder::Downsample::k420:
134cb93a386Sopenharmony_ci                SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
135cb93a386Sopenharmony_ci                SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
136cb93a386Sopenharmony_ci                SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
137cb93a386Sopenharmony_ci                SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
138cb93a386Sopenharmony_ci                SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
139cb93a386Sopenharmony_ci                SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
140cb93a386Sopenharmony_ci                break;
141cb93a386Sopenharmony_ci            case SkJpegEncoder::Downsample::k422:
142cb93a386Sopenharmony_ci                fCInfo.comp_info[0].h_samp_factor = 2;
143cb93a386Sopenharmony_ci                fCInfo.comp_info[0].v_samp_factor = 1;
144cb93a386Sopenharmony_ci                fCInfo.comp_info[1].h_samp_factor = 1;
145cb93a386Sopenharmony_ci                fCInfo.comp_info[1].v_samp_factor = 1;
146cb93a386Sopenharmony_ci                fCInfo.comp_info[2].h_samp_factor = 1;
147cb93a386Sopenharmony_ci                fCInfo.comp_info[2].v_samp_factor = 1;
148cb93a386Sopenharmony_ci                break;
149cb93a386Sopenharmony_ci            case SkJpegEncoder::Downsample::k444:
150cb93a386Sopenharmony_ci                fCInfo.comp_info[0].h_samp_factor = 1;
151cb93a386Sopenharmony_ci                fCInfo.comp_info[0].v_samp_factor = 1;
152cb93a386Sopenharmony_ci                fCInfo.comp_info[1].h_samp_factor = 1;
153cb93a386Sopenharmony_ci                fCInfo.comp_info[1].v_samp_factor = 1;
154cb93a386Sopenharmony_ci                fCInfo.comp_info[2].h_samp_factor = 1;
155cb93a386Sopenharmony_ci                fCInfo.comp_info[2].v_samp_factor = 1;
156cb93a386Sopenharmony_ci                break;
157cb93a386Sopenharmony_ci        }
158cb93a386Sopenharmony_ci    }
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci    // Tells libjpeg-turbo to compute optimal Huffman coding tables
161cb93a386Sopenharmony_ci    // for the image.  This improves compression at the cost of
162cb93a386Sopenharmony_ci    // slower encode performance.
163cb93a386Sopenharmony_ci    fCInfo.optimize_coding = TRUE;
164cb93a386Sopenharmony_ci    return true;
165cb93a386Sopenharmony_ci}
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_cistd::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src,
168cb93a386Sopenharmony_ci                                               const Options& options) {
169cb93a386Sopenharmony_ci    if (!SkPixmapIsValid(src)) {
170cb93a386Sopenharmony_ci        return nullptr;
171cb93a386Sopenharmony_ci    }
172cb93a386Sopenharmony_ci
173cb93a386Sopenharmony_ci    std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci    skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
176cb93a386Sopenharmony_ci    if (setjmp(jmp)) {
177cb93a386Sopenharmony_ci        return nullptr;
178cb93a386Sopenharmony_ci    }
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci    if (!encoderMgr->setParams(src.info(), options)) {
181cb93a386Sopenharmony_ci        return nullptr;
182cb93a386Sopenharmony_ci    }
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci    jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
185cb93a386Sopenharmony_ci    jpeg_start_compress(encoderMgr->cinfo(), TRUE);
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci    sk_sp<SkData> icc = icc_from_color_space(src.info());
188cb93a386Sopenharmony_ci    if (icc) {
189cb93a386Sopenharmony_ci        // Create a contiguous block of memory with the icc signature followed by the profile.
190cb93a386Sopenharmony_ci        sk_sp<SkData> markerData =
191cb93a386Sopenharmony_ci                SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
192cb93a386Sopenharmony_ci        uint8_t* ptr = (uint8_t*) markerData->writable_data();
193cb93a386Sopenharmony_ci        memcpy(ptr, kICCSig, sizeof(kICCSig));
194cb93a386Sopenharmony_ci        ptr += sizeof(kICCSig);
195cb93a386Sopenharmony_ci        *ptr++ = 1; // This is the first marker.
196cb93a386Sopenharmony_ci        *ptr++ = 1; // Out of one total markers.
197cb93a386Sopenharmony_ci        memcpy(ptr, icc->data(), icc->size());
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci        jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
200cb93a386Sopenharmony_ci    }
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ci    return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
203cb93a386Sopenharmony_ci}
204cb93a386Sopenharmony_ci
205cb93a386Sopenharmony_ciSkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src)
206cb93a386Sopenharmony_ci    : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0)
207cb93a386Sopenharmony_ci    , fEncoderMgr(std::move(encoderMgr))
208cb93a386Sopenharmony_ci{}
209cb93a386Sopenharmony_ci
210cb93a386Sopenharmony_ciSkJpegEncoder::~SkJpegEncoder() {}
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_cibool SkJpegEncoder::onEncodeRows(int numRows) {
213cb93a386Sopenharmony_ci    skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
214cb93a386Sopenharmony_ci    if (setjmp(jmp)) {
215cb93a386Sopenharmony_ci        return false;
216cb93a386Sopenharmony_ci    }
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_ci    const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width();
219cb93a386Sopenharmony_ci    const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width();
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci    const void* srcRow = fSrc.addr(0, fCurrRow);
222cb93a386Sopenharmony_ci    for (int i = 0; i < numRows; i++) {
223cb93a386Sopenharmony_ci        JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
224cb93a386Sopenharmony_ci        if (fEncoderMgr->proc()) {
225cb93a386Sopenharmony_ci            sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes));
226cb93a386Sopenharmony_ci            fEncoderMgr->proc()((char*)fStorage.get(),
227cb93a386Sopenharmony_ci                                (const char*)srcRow,
228cb93a386Sopenharmony_ci                                fSrc.width(),
229cb93a386Sopenharmony_ci                                fEncoderMgr->cinfo()->input_components);
230cb93a386Sopenharmony_ci            jpegSrcRow = fStorage.get();
231cb93a386Sopenharmony_ci            sk_msan_assert_initialized(jpegSrcRow,
232cb93a386Sopenharmony_ci                                       SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
233cb93a386Sopenharmony_ci        } else {
234cb93a386Sopenharmony_ci            // Same as above, but this repetition allows determining whether a
235cb93a386Sopenharmony_ci            // proc was used when msan asserts.
236cb93a386Sopenharmony_ci            sk_msan_assert_initialized(jpegSrcRow,
237cb93a386Sopenharmony_ci                                       SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
238cb93a386Sopenharmony_ci        }
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci        jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
241cb93a386Sopenharmony_ci        srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
242cb93a386Sopenharmony_ci    }
243cb93a386Sopenharmony_ci
244cb93a386Sopenharmony_ci    fCurrRow += numRows;
245cb93a386Sopenharmony_ci    if (fCurrRow == fSrc.height()) {
246cb93a386Sopenharmony_ci        jpeg_finish_compress(fEncoderMgr->cinfo());
247cb93a386Sopenharmony_ci    }
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci    return true;
250cb93a386Sopenharmony_ci}
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_cibool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
253cb93a386Sopenharmony_ci    auto encoder = SkJpegEncoder::Make(dst, src, options);
254cb93a386Sopenharmony_ci    return encoder.get() && encoder->encodeRows(src.height());
255cb93a386Sopenharmony_ci}
256cb93a386Sopenharmony_ci
257cb93a386Sopenharmony_ci#endif
258