Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkJpegEncoderImpl.cpp
Go to the documentation of this file.
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
9
13#include "include/core/SkData.h"
25#include "src/base/SkMSAN.h"
32
33#include <csetjmp>
34#include <cstdint>
35#include <cstring>
36#include <memory>
37#include <utility>
38
39class GrDirectContext;
40class SkColorSpace;
41class SkImage;
42
43extern "C" {
44#include "jpeglib.h" // NO_G3_REWRITE
45}
46
48public:
49 /*
50 * Create the decode manager
51 * Does not take ownership of stream.
52 */
53 static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) {
54 return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream));
55 }
56
57 bool setParams(const SkImageInfo& srcInfo, const SkJpegEncoder::Options& options);
58 bool setParams(const SkYUVAPixmapInfo& srcInfo, const SkJpegEncoder::Options& options);
59
60 jpeg_compress_struct* cinfo() { return &fCInfo; }
61
62 skjpeg_error_mgr* errorMgr() { return &fErrMgr; }
63
64 transform_scanline_proc proc() const { return fProc; }
65
66 ~SkJpegEncoderMgr() { jpeg_destroy_compress(&fCInfo); }
67
68private:
69 SkJpegEncoderMgr(SkWStream* stream) : fDstMgr(stream), fProc(nullptr) {
70 fCInfo.err = jpeg_std_error(&fErrMgr);
71 fErrMgr.error_exit = skjpeg_error_exit;
72 jpeg_create_compress(&fCInfo);
73 fCInfo.dest = &fDstMgr;
74 }
75
76 jpeg_compress_struct fCInfo;
77 skjpeg_error_mgr fErrMgr;
80};
81
84 auto chooseProc8888 = [&]() {
85 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
88 }
89 return (transform_scanline_proc) nullptr;
90 };
91
92 J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA;
93 int numComponents = 0;
94 switch (srcInfo.colorType()) {
96 fProc = chooseProc8888();
97 jpegColorType = JCS_EXT_RGBA;
98 numComponents = 4;
99 break;
101 fProc = chooseProc8888();
102 jpegColorType = JCS_EXT_BGRA;
103 numComponents = 4;
104 break;
107 jpegColorType = JCS_RGB;
108 numComponents = 3;
109 break;
112 return false;
113 }
114
116 jpegColorType = JCS_RGB;
117 numComponents = 3;
118 break;
122 jpegColorType = JCS_GRAYSCALE;
123 numComponents = 1;
124 break;
126 if (kUnpremul_SkAlphaType == srcInfo.alphaType() &&
129 } else {
131 }
132 jpegColorType = JCS_EXT_RGBA;
133 numComponents = 4;
134 break;
135 default:
136 return false;
137 }
138
139 fCInfo.image_width = srcInfo.width();
140 fCInfo.image_height = srcInfo.height();
141 fCInfo.in_color_space = jpegColorType;
142 fCInfo.input_components = numComponents;
143 jpeg_set_defaults(&fCInfo);
144
145 if (numComponents != 1) {
146 switch (options.fDownsample) {
148 SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor);
149 SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor);
150 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
151 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
152 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
153 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
154 break;
156 fCInfo.comp_info[0].h_samp_factor = 2;
157 fCInfo.comp_info[0].v_samp_factor = 1;
158 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
159 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
160 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
161 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
162 break;
164 fCInfo.comp_info[0].h_samp_factor = 1;
165 fCInfo.comp_info[0].v_samp_factor = 1;
166 SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor);
167 SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor);
168 SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor);
169 SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor);
170 break;
171 }
172 }
173
174 // Tells libjpeg-turbo to compute optimal Huffman coding tables
175 // for the image. This improves compression at the cost of
176 // slower encode performance.
177 fCInfo.optimize_coding = TRUE;
178 return true;
179}
180
181// Convert a row of an SkYUVAPixmaps to a row of Y,U,V triples.
182// TODO(ccameron): This is horribly inefficient.
183static void yuva_copy_row(const SkYUVAPixmaps* src, int row, uint8_t* dst) {
184 int width = src->plane(0).width();
185 switch (src->yuvaInfo().planeConfig()) {
187 auto [ssWidthU, ssHeightU] = src->yuvaInfo().planeSubsamplingFactors(1);
188 auto [ssWidthV, ssHeightV] = src->yuvaInfo().planeSubsamplingFactors(2);
189 const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src->plane(0).addr(0, row));
190 const uint8_t* srcU =
191 reinterpret_cast<const uint8_t*>(src->plane(1).addr(0, row / ssHeightU));
192 const uint8_t* srcV =
193 reinterpret_cast<const uint8_t*>(src->plane(2).addr(0, row / ssHeightV));
194 for (int col = 0; col < width; ++col) {
195 dst[3 * col + 0] = srcY[col];
196 dst[3 * col + 1] = srcU[col / ssWidthU];
197 dst[3 * col + 2] = srcV[col / ssWidthV];
198 }
199 break;
200 }
202 auto [ssWidthUV, ssHeightUV] = src->yuvaInfo().planeSubsamplingFactors(1);
203 const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src->plane(0).addr(0, row));
204 const uint8_t* srcUV =
205 reinterpret_cast<const uint8_t*>(src->plane(1).addr(0, row / ssHeightUV));
206 for (int col = 0; col < width; ++col) {
207 dst[3 * col + 0] = srcY[col];
208 dst[3 * col + 1] = srcUV[2 * (col / ssWidthUV) + 0];
209 dst[3 * col + 2] = srcUV[2 * (col / ssWidthUV) + 1];
210 }
211 break;
212 }
213 default:
214 break;
215 }
216}
217
220 fCInfo.image_width = srcInfo.yuvaInfo().width();
221 fCInfo.image_height = srcInfo.yuvaInfo().height();
222 fCInfo.in_color_space = JCS_YCbCr;
223 fCInfo.input_components = 3;
224 jpeg_set_defaults(&fCInfo);
225
226 // Support no color space conversion.
227 if (srcInfo.yuvColorSpace() != kJPEG_Full_SkYUVColorSpace) {
228 return false;
229 }
230
231 // Support only 8-bit data.
232 switch (srcInfo.dataType()) {
234 break;
235 default:
236 return false;
237 }
238
239 // Support only Y,U,V and Y,UV configurations (they are the only ones supported by
240 // yuva_copy_row).
241 switch (srcInfo.yuvaInfo().planeConfig()) {
244 break;
245 default:
246 return false;
247 }
248
249 // Specify to the encoder to use the same subsampling as the input image. The U and V planes
250 // always have a sampling factor of 1.
251 auto [ssHoriz, ssVert] = SkYUVAInfo::SubsamplingFactors(srcInfo.yuvaInfo().subsampling());
252 fCInfo.comp_info[0].h_samp_factor = ssHoriz;
253 fCInfo.comp_info[0].v_samp_factor = ssVert;
254
255 fCInfo.optimize_coding = TRUE;
256 return true;
257}
258
259static std::unique_ptr<SkEncoder> Make(SkWStream* dst,
260 const SkPixmap* src,
261 const SkYUVAPixmaps* srcYUVA,
262 const SkColorSpace* srcYUVAColorSpace,
264 // Exactly one of |src| or |srcYUVA| should be specified.
265 if (srcYUVA) {
266 SkASSERT(!src);
267 if (!srcYUVA->isValid()) {
268 return nullptr;
269 }
270 } else {
271 SkASSERT(src);
272 if (!src || !SkPixmapIsValid(*src)) {
273 return nullptr;
274 }
275 }
276
277 std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst);
278
279 skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr());
280 if (setjmp(jmp)) {
281 return nullptr;
282 }
283
284 if (srcYUVA) {
285 if (!encoderMgr->setParams(srcYUVA->pixmapsInfo(), options)) {
286 return nullptr;
287 }
288 } else {
289 if (!encoderMgr->setParams(src->info(), options)) {
290 return nullptr;
291 }
292 }
293
294 jpeg_set_quality(encoderMgr->cinfo(), options.fQuality, TRUE);
295 jpeg_start_compress(encoderMgr->cinfo(), TRUE);
296
297 // Write XMP metadata. This will only write the standard XMP segment.
298 // TODO(ccameron): Split this into a standard and extended XMP segment if needed.
299 if (options.xmpMetadata) {
301 s.write(kXMPStandardSig, sizeof(kXMPStandardSig));
302 s.write(options.xmpMetadata->data(), options.xmpMetadata->size());
303 auto data = s.detachAsData();
304 jpeg_write_marker(encoderMgr->cinfo(), kXMPMarker, data->bytes(), data->size());
305 }
306
307 // Write the ICC profile.
308 // TODO(ccameron): This limits ICC profile size to a single segment's parameters (less than
309 // 64k). Split larger profiles into more segments.
310 sk_sp<SkData> icc = icc_from_color_space(srcYUVA ? srcYUVAColorSpace : src->colorSpace(),
311 options.fICCProfile,
312 options.fICCProfileDescription);
313 if (icc) {
314 // Create a contiguous block of memory with the icc signature followed by the profile.
316 uint8_t* ptr = (uint8_t*)markerData->writable_data();
317 memcpy(ptr, kICCSig, sizeof(kICCSig));
318 ptr += sizeof(kICCSig);
319 *ptr++ = 1; // This is the first marker.
320 *ptr++ = 1; // Out of one total markers.
321 memcpy(ptr, icc->data(), icc->size());
322
323 jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
324 }
325
326 if (srcYUVA) {
327 return std::make_unique<SkJpegEncoderImpl>(std::move(encoderMgr), srcYUVA);
328 }
329 return std::make_unique<SkJpegEncoderImpl>(std::move(encoderMgr), *src);
330}
331
332SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,
333 const SkPixmap& src)
334 : SkEncoder(src,
335 encoderMgr->proc() ? encoderMgr->cinfo()->input_components * src.width() : 0)
336 , fEncoderMgr(std::move(encoderMgr)) {}
337
338SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr,
339 const SkYUVAPixmaps* src)
340 : SkEncoder(src->plane(0), encoderMgr->cinfo()->input_components * src->yuvaInfo().width())
341 , fEncoderMgr(std::move(encoderMgr))
342 , fSrcYUVA(src) {}
343
345
347 skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr());
348 if (setjmp(jmp)) {
349 return false;
350 }
351
352 if (fSrcYUVA) {
353 // TODO(ccameron): Consider using jpeg_write_raw_data, to avoid having to re-pack the data.
354 for (int i = 0; i < numRows; i++) {
355 yuva_copy_row(fSrcYUVA, fCurrRow + i, fStorage.get());
356 JSAMPLE* jpegSrcRow = fStorage.get();
357 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
358 }
359 } else {
360 const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width();
361 const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width();
362 const void* srcRow = fSrc.addr(0, fCurrRow);
363 for (int i = 0; i < numRows; i++) {
364 JSAMPLE* jpegSrcRow = (JSAMPLE*)(const_cast<void*>(srcRow));
365 if (fEncoderMgr->proc()) {
366 sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes));
367 fEncoderMgr->proc()((char*)fStorage.get(),
368 (const char*)srcRow,
369 fSrc.width(),
370 fEncoderMgr->cinfo()->input_components);
371 jpegSrcRow = fStorage.get();
373 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
374 } else {
375 // Same as above, but this repetition allows determining whether a
376 // proc was used when msan asserts.
378 SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes));
379 }
380
381 jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1);
382 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
383 }
384 }
385
386 fCurrRow += numRows;
387 if (fCurrRow == fSrc.height()) {
388 jpeg_finish_compress(fEncoderMgr->cinfo());
389 }
390
391 return true;
392}
393
394namespace SkJpegEncoder {
395
396bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
397 auto encoder = Make(dst, src, options);
398 return encoder.get() && encoder->encodeRows(src.height());
399}
400
402 const SkYUVAPixmaps& src,
403 const SkColorSpace* srcColorSpace,
404 const Options& options) {
405 auto encoder = Make(dst, src, srcColorSpace, options);
406 return encoder.get() && encoder->encodeRows(src.yuvaInfo().height());
407}
408
409sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) {
410 if (!img) {
411 return nullptr;
412 }
413 SkBitmap bm;
414 if (!as_IB(img)->getROPixels(ctx, &bm)) {
415 return nullptr;
416 }
418 if (Encode(&stream, bm.pixmap(), options)) {
419 return stream.detachAsData();
420 }
421 return nullptr;
422}
423
424std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Options& options) {
425 return Make(dst, &src, nullptr, nullptr, options);
426}
427
428std::unique_ptr<SkEncoder> Make(SkWStream* dst,
429 const SkYUVAPixmaps& src,
430 const SkColorSpace* srcColorSpace,
431 const Options& options) {
432 return Make(dst, nullptr, &src, srcColorSpace, options);
433}
434
435} // namespace SkJpegEncoder
const char * options
kUnpremul_SkAlphaType
#define SkASSERT(cond)
Definition SkAssert.h:116
@ kR8_unorm_SkColorType
Definition SkColorType.h:54
@ kARGB_4444_SkColorType
pixel with 4 bits for alpha, red, green, blue; in 16-bit word
Definition SkColorType.h:23
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition SkColorType.h:26
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition SkColorType.h:38
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
@ kGray_8_SkColorType
pixel with grayscale level in 8-bit byte
Definition SkColorType.h:35
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition SkColorType.h:22
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
static void transform_scanline_to_premul_legacy(char *dst, const char *src, int width, int)
static void transform_scanline_F16_to_8888(char *dst, const char *src, int width, int)
void(* transform_scanline_proc)(char *dst, const char *src, int width, int bpp)
static void transform_scanline_565(char *dst, const char *src, int width, int)
static void transform_scanline_F16_to_premul_8888(char *dst, const char *src, int width, int)
static void transform_scanline_444(char *dst, const char *src, int width, int)
static sk_sp< SkData > icc_from_color_space(const SkColorSpace *cs, const skcms_ICCProfile *profile, const char *profile_description)
static bool SkPixmapIsValid(const SkPixmap &src)
SK_API int SkColorTypeBytesPerPixel(SkColorType ct)
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition SkImageInfo.h:69
static SkImage_Base * as_IB(SkImage *image)
void skjpeg_error_exit(j_common_ptr cinfo)
static constexpr uint8_t kICCSig[]
static constexpr uint32_t kICCMarker
static constexpr uint32_t kXMPMarker
static constexpr uint8_t kXMPStandardSig[]
static constexpr uint32_t kICCMarkerHeaderSize
static void yuva_copy_row(const SkYUVAPixmaps *src, int row, uint8_t *dst)
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
static void sk_msan_assert_initialized(const void *begin, const void *end)
Definition SkMSAN.h:24
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition SkData.cpp:116
skia_private::AutoTMalloc< uint8_t > fStorage
Definition SkEncoder.h:60
int fCurrRow
Definition SkEncoder.h:59
const SkPixmap & fSrc
Definition SkEncoder.h:58
SkJpegEncoderImpl(std::unique_ptr< SkJpegEncoderMgr >, const SkPixmap &src)
bool onEncodeRows(int numRows) override
skjpeg_error_mgr * errorMgr()
jpeg_compress_struct * cinfo()
bool setParams(const SkImageInfo &srcInfo, const SkJpegEncoder::Options &options)
transform_scanline_proc proc() const
static std::unique_ptr< SkJpegEncoderMgr > Make(SkWStream *stream)
size_t rowBytes() const
Definition SkPixmap.h:145
int width() const
Definition SkPixmap.h:160
SkColorType colorType() const
Definition SkPixmap.h:173
const void * addr() const
Definition SkPixmap.h:153
int height() const
Definition SkPixmap.h:166
int width() const
Definition SkYUVAInfo.h:172
@ kY_U_V
Plane 0: Y, Plane 1: U, Plane 2: V.
@ kY_UV
Plane 0: Y, Plane 1: UV.
int height() const
Definition SkYUVAInfo.h:173
PlaneConfig planeConfig() const
Definition SkYUVAInfo.h:160
static std::tuple< int, int > SubsamplingFactors(Subsampling)
Subsampling subsampling() const
Definition SkYUVAInfo.h:161
SkYUVColorSpace yuvColorSpace() const
const SkYUVAInfo & yuvaInfo() const
@ kUnorm8
8 bit unsigned normalized
DataType dataType() const
SkYUVAPixmapInfo pixmapsInfo() const
bool isValid() const
struct MyStruct s
SK_API std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap &src, const Options &options)
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
Definition ref_ptr.h:256
int32_t width
int width() const
SkAlphaType alphaType() const
SkColorType colorType() const
int height() const