1 /* 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 8 #include "SkImageEncoderPriv.h" 9 10 #ifdef SK_HAS_JPEG_LIBRARY 11 12 #include "SkColorData.h" 13 #include "SkImageEncoderFns.h" 14 #include "SkImageInfoPriv.h" 15 #include "SkJpegEncoder.h" 16 #include "SkJPEGWriteUtility.h" 17 #include "SkStream.h" 18 #include "SkTemplates.h" 19 20 #include <stdio.h> 21 22 extern "C" { 23 #include "jpeglib.h" 24 #include "jerror.h" 25 } 26 27 class SkJpegEncoderMgr final : SkNoncopyable { 28 public: 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, const SkJpegEncoder::Options& options); 39 40 jpeg_compress_struct* cinfo() { return &fCInfo; } 41 42 skjpeg_error_mgr* errorMgr() { return &fErrMgr; } 43 44 transform_scanline_proc proc() const { return fProc; } 45 46 ~SkJpegEncoderMgr() { 47 jpeg_destroy_compress(&fCInfo); 48 } 49 50 private: 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 68 bool SkJpegEncoderMgr::setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options) 69 { 70 auto chooseProc8888 = [&]() { 71 if (kUnpremul_SkAlphaType == srcInfo.alphaType() && 72 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { 73 return transform_scanline_to_premul_legacy; 74 } 75 return (transform_scanline_proc) nullptr; 76 }; 77 78 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA; 79 int numComponents = 0; 80 switch (srcInfo.colorType()) { 81 case kRGBA_8888_SkColorType: 82 fProc = chooseProc8888(); 83 jpegColorType = JCS_EXT_RGBA; 84 numComponents = 4; 85 break; 86 case kBGRA_8888_SkColorType: 87 fProc = chooseProc8888(); 88 jpegColorType = JCS_EXT_BGRA; 89 numComponents = 4; 90 break; 91 case kRGB_565_SkColorType: 92 fProc = transform_scanline_565; 93 jpegColorType = JCS_RGB; 94 numComponents = 3; 95 break; 96 case kARGB_4444_SkColorType: 97 if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) { 98 return false; 99 } 100 101 fProc = transform_scanline_444; 102 jpegColorType = JCS_RGB; 103 numComponents = 3; 104 break; 105 case kGray_8_SkColorType: 106 SkASSERT(srcInfo.isOpaque()); 107 jpegColorType = JCS_GRAYSCALE; 108 numComponents = 1; 109 break; 110 case kRGBA_F16_SkColorType: 111 if (kUnpremul_SkAlphaType == srcInfo.alphaType() && 112 options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { 113 fProc = transform_scanline_F16_to_premul_8888; 114 } else { 115 fProc = transform_scanline_F16_to_8888; 116 } 117 jpegColorType = JCS_EXT_RGBA; 118 numComponents = 4; 119 break; 120 default: 121 return false; 122 } 123 124 fCInfo.image_width = srcInfo.width(); 125 fCInfo.image_height = srcInfo.height(); 126 fCInfo.in_color_space = jpegColorType; 127 fCInfo.input_components = numComponents; 128 jpeg_set_defaults(&fCInfo); 129 130 if (kGray_8_SkColorType != srcInfo.colorType()) { 131 switch (options.fDownsample) { 132 case SkJpegEncoder::Downsample::k420: 133 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor); 134 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor); 135 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor); 136 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor); 137 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor); 138 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor); 139 break; 140 case SkJpegEncoder::Downsample::k422: 141 fCInfo.comp_info[0].h_samp_factor = 2; 142 fCInfo.comp_info[0].v_samp_factor = 1; 143 fCInfo.comp_info[1].h_samp_factor = 1; 144 fCInfo.comp_info[1].v_samp_factor = 1; 145 fCInfo.comp_info[2].h_samp_factor = 1; 146 fCInfo.comp_info[2].v_samp_factor = 1; 147 break; 148 case SkJpegEncoder::Downsample::k444: 149 fCInfo.comp_info[0].h_samp_factor = 1; 150 fCInfo.comp_info[0].v_samp_factor = 1; 151 fCInfo.comp_info[1].h_samp_factor = 1; 152 fCInfo.comp_info[1].v_samp_factor = 1; 153 fCInfo.comp_info[2].h_samp_factor = 1; 154 fCInfo.comp_info[2].v_samp_factor = 1; 155 break; 156 } 157 } 158 159 // Tells libjpeg-turbo to compute optimal Huffman coding tables 160 // for the image. This improves compression at the cost of 161 // slower encode performance. 162 fCInfo.optimize_coding = TRUE; 163 return true; 164 } 165 166 std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& src, 167 const Options& options) { 168 if (!SkPixmapIsValid(src)) { 169 return nullptr; 170 } 171 172 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst); 173 174 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr()); 175 if (setjmp(jmp)) { 176 return nullptr; 177 } 178 179 if (!encoderMgr->setParams(src.info(), options)) { 180 return nullptr; 181 } 182 183 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE); 184 jpeg_start_compress(encoderMgr->cinfo(), TRUE); 185 186 sk_sp<SkData> icc = icc_from_color_space(src.info()); 187 if (icc) { 188 // Create a contiguous block of memory with the icc signature followed by the profile. 189 sk_sp<SkData> markerData = 190 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size()); 191 uint8_t* ptr = (uint8_t*) markerData->writable_data(); 192 memcpy(ptr, kICCSig, sizeof(kICCSig)); 193 ptr += sizeof(kICCSig); 194 *ptr++ = 1; // This is the first marker. 195 *ptr++ = 1; // Out of one total markers. 196 memcpy(ptr, icc->data(), icc->size()); 197 198 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size()); 199 } 200 201 return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src)); 202 } 203 204 SkJpegEncoder::SkJpegEncoder(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, const SkPixmap& src) 205 : INHERITED(src, encoderMgr->proc() ? encoderMgr->cinfo()->input_components*src.width() : 0) 206 , fEncoderMgr(std::move(encoderMgr)) 207 {} 208 209 SkJpegEncoder::~SkJpegEncoder() {} 210 211 bool SkJpegEncoder::onEncodeRows(int numRows) { 212 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr()); 213 if (setjmp(jmp)) { 214 return false; 215 } 216 217 const void* srcRow = fSrc.addr(0, fCurrRow); 218 for (int i = 0; i < numRows; i++) { 219 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow; 220 if (fEncoderMgr->proc()) { 221 fEncoderMgr->proc()((char*)fStorage.get(), 222 (const char*)srcRow, 223 fSrc.width(), 224 fEncoderMgr->cinfo()->input_components); 225 jpegSrcRow = fStorage.get(); 226 } 227 228 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1); 229 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes()); 230 } 231 232 fCurrRow += numRows; 233 if (fCurrRow == fSrc.height()) { 234 jpeg_finish_compress(fEncoderMgr->cinfo()); 235 } 236 237 return true; 238 } 239 240 bool SkJpegEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { 241 auto encoder = SkJpegEncoder::Make(dst, src, options); 242 return encoder.get() && encoder->encodeRows(src.height()); 243 } 244 245 #endif 246