/src/skia/src/encode/SkJpegEncoderImpl.cpp
Line | Count | Source (jump to first uncovered line) |
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 "src/encode/SkJpegEncoderImpl.h" |
9 | | |
10 | | #include "include/core/SkAlphaType.h" |
11 | | #include "include/core/SkBitmap.h" |
12 | | #include "include/core/SkColorType.h" |
13 | | #include "include/core/SkData.h" |
14 | | #include "include/core/SkImageInfo.h" |
15 | | #include "include/core/SkPixmap.h" |
16 | | #include "include/core/SkRefCnt.h" |
17 | | #include "include/core/SkStream.h" |
18 | | #include "include/core/SkYUVAInfo.h" |
19 | | #include "include/core/SkYUVAPixmaps.h" |
20 | | #include "include/encode/SkEncoder.h" |
21 | | #include "include/encode/SkJpegEncoder.h" |
22 | | #include "include/private/base/SkAssert.h" |
23 | | #include "include/private/base/SkNoncopyable.h" |
24 | | #include "include/private/base/SkTemplates.h" |
25 | | #include "src/base/SkMSAN.h" |
26 | | #include "src/codec/SkJpegConstants.h" |
27 | | #include "src/codec/SkJpegPriv.h" |
28 | | #include "src/encode/SkImageEncoderFns.h" |
29 | | #include "src/encode/SkImageEncoderPriv.h" |
30 | | #include "src/encode/SkJPEGWriteUtility.h" |
31 | | #include "src/image/SkImage_Base.h" |
32 | | |
33 | | #include <csetjmp> |
34 | | #include <cstdint> |
35 | | #include <cstring> |
36 | | #include <memory> |
37 | | #include <utility> |
38 | | |
39 | | class GrDirectContext; |
40 | | class SkColorSpace; |
41 | | class SkImage; |
42 | | |
43 | | extern "C" { |
44 | | #include "jpeglib.h" // NO_G3_REWRITE |
45 | | } |
46 | | |
47 | | class SkJpegEncoderMgr final : SkNoncopyable { |
48 | | public: |
49 | | /* |
50 | | * Create the decode manager |
51 | | * Does not take ownership of stream. |
52 | | */ |
53 | 4.67k | static std::unique_ptr<SkJpegEncoderMgr> Make(SkWStream* stream) { |
54 | 4.67k | return std::unique_ptr<SkJpegEncoderMgr>(new SkJpegEncoderMgr(stream)); |
55 | 4.67k | } |
56 | | |
57 | | bool initializeRGB(const SkImageInfo&, |
58 | | const SkJpegEncoder::Options&, |
59 | | const SkJpegMetadataEncoder::SegmentList&); |
60 | | bool initializeYUV(const SkYUVAPixmapInfo&, |
61 | | const SkJpegEncoder::Options&, |
62 | | const SkJpegMetadataEncoder::SegmentList&); |
63 | | |
64 | 810k | jpeg_compress_struct* cinfo() { return &fCInfo; } |
65 | | |
66 | 9.35k | skjpeg_error_mgr* errorMgr() { return &fErrMgr; } |
67 | | |
68 | 805k | transform_scanline_proc proc() const { return fProc; } |
69 | | |
70 | 4.67k | ~SkJpegEncoderMgr() { jpeg_destroy_compress(&fCInfo); } |
71 | | |
72 | | private: |
73 | 4.67k | SkJpegEncoderMgr(SkWStream* stream) : fDstMgr(stream), fProc(nullptr) { |
74 | 4.67k | fCInfo.err = jpeg_std_error(&fErrMgr); |
75 | 4.67k | fErrMgr.error_exit = skjpeg_error_exit; |
76 | 4.67k | jpeg_create_compress(&fCInfo); |
77 | 4.67k | fCInfo.dest = &fDstMgr; |
78 | 4.67k | } |
79 | | void initializeCommon(const SkJpegEncoder::Options&, const SkJpegMetadataEncoder::SegmentList&); |
80 | | |
81 | | jpeg_compress_struct fCInfo; |
82 | | skjpeg_error_mgr fErrMgr; |
83 | | skjpeg_destination_mgr fDstMgr; |
84 | | transform_scanline_proc fProc; |
85 | | }; |
86 | | |
87 | | bool SkJpegEncoderMgr::initializeRGB(const SkImageInfo& srcInfo, |
88 | | const SkJpegEncoder::Options& options, |
89 | 4.67k | const SkJpegMetadataEncoder::SegmentList& metadataSegments) { |
90 | 4.67k | auto chooseProc8888 = [&]() { |
91 | 4.67k | if (kUnpremul_SkAlphaType == srcInfo.alphaType() && |
92 | 4.67k | options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { |
93 | 0 | return transform_scanline_to_premul_legacy; |
94 | 0 | } |
95 | 4.67k | return (transform_scanline_proc) nullptr; |
96 | 4.67k | }; |
97 | | |
98 | 4.67k | J_COLOR_SPACE jpegColorType = JCS_EXT_RGBA; |
99 | 4.67k | int numComponents = 0; |
100 | 4.67k | switch (srcInfo.colorType()) { |
101 | 0 | case kRGBA_8888_SkColorType: |
102 | 0 | fProc = chooseProc8888(); |
103 | 0 | jpegColorType = JCS_EXT_RGBA; |
104 | 0 | numComponents = 4; |
105 | 0 | break; |
106 | 4.67k | case kBGRA_8888_SkColorType: |
107 | 4.67k | fProc = chooseProc8888(); |
108 | 4.67k | jpegColorType = JCS_EXT_BGRA; |
109 | 4.67k | numComponents = 4; |
110 | 4.67k | break; |
111 | 0 | case kRGB_565_SkColorType: |
112 | 0 | fProc = transform_scanline_565; |
113 | 0 | jpegColorType = JCS_RGB; |
114 | 0 | numComponents = 3; |
115 | 0 | break; |
116 | 0 | case kARGB_4444_SkColorType: |
117 | 0 | if (SkJpegEncoder::AlphaOption::kBlendOnBlack == options.fAlphaOption) { |
118 | 0 | return false; |
119 | 0 | } |
120 | | |
121 | 0 | fProc = transform_scanline_444; |
122 | 0 | jpegColorType = JCS_RGB; |
123 | 0 | numComponents = 3; |
124 | 0 | break; |
125 | 0 | case kGray_8_SkColorType: |
126 | 0 | case kAlpha_8_SkColorType: |
127 | 0 | case kR8_unorm_SkColorType: |
128 | 0 | jpegColorType = JCS_GRAYSCALE; |
129 | 0 | numComponents = 1; |
130 | 0 | break; |
131 | 0 | case kRGBA_F16_SkColorType: |
132 | 0 | if (kUnpremul_SkAlphaType == srcInfo.alphaType() && |
133 | 0 | options.fAlphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack) { |
134 | 0 | fProc = transform_scanline_F16_to_premul_8888; |
135 | 0 | } else { |
136 | 0 | fProc = transform_scanline_F16_to_8888; |
137 | 0 | } |
138 | 0 | jpegColorType = JCS_EXT_RGBA; |
139 | 0 | numComponents = 4; |
140 | 0 | break; |
141 | 0 | default: |
142 | 0 | return false; |
143 | 4.67k | } |
144 | | |
145 | 4.67k | fCInfo.image_width = srcInfo.width(); |
146 | 4.67k | fCInfo.image_height = srcInfo.height(); |
147 | 4.67k | fCInfo.in_color_space = jpegColorType; |
148 | 4.67k | fCInfo.input_components = numComponents; |
149 | 4.67k | jpeg_set_defaults(&fCInfo); |
150 | | |
151 | 4.67k | if (numComponents != 1) { |
152 | 4.67k | switch (options.fDownsample) { |
153 | 4.67k | case SkJpegEncoder::Downsample::k420: |
154 | 4.67k | SkASSERT(2 == fCInfo.comp_info[0].h_samp_factor); |
155 | 4.67k | SkASSERT(2 == fCInfo.comp_info[0].v_samp_factor); |
156 | 4.67k | SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor); |
157 | 4.67k | SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor); |
158 | 4.67k | SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor); |
159 | 4.67k | SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor); |
160 | 4.67k | break; |
161 | 0 | case SkJpegEncoder::Downsample::k422: |
162 | 0 | fCInfo.comp_info[0].h_samp_factor = 2; |
163 | 0 | fCInfo.comp_info[0].v_samp_factor = 1; |
164 | 0 | SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor); |
165 | 0 | SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor); |
166 | 0 | SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor); |
167 | 0 | SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor); |
168 | 0 | break; |
169 | 0 | case SkJpegEncoder::Downsample::k444: |
170 | 0 | fCInfo.comp_info[0].h_samp_factor = 1; |
171 | 0 | fCInfo.comp_info[0].v_samp_factor = 1; |
172 | 0 | SkASSERT(1 == fCInfo.comp_info[1].h_samp_factor); |
173 | 0 | SkASSERT(1 == fCInfo.comp_info[1].v_samp_factor); |
174 | 0 | SkASSERT(1 == fCInfo.comp_info[2].h_samp_factor); |
175 | 0 | SkASSERT(1 == fCInfo.comp_info[2].v_samp_factor); |
176 | 0 | break; |
177 | 4.67k | } |
178 | 4.67k | } |
179 | | |
180 | 4.67k | initializeCommon(options, metadataSegments); |
181 | 4.67k | return true; |
182 | 4.67k | } |
183 | | |
184 | | // Convert a row of an SkYUVAPixmaps to a row of Y,U,V triples. |
185 | | // TODO(ccameron): This is horribly inefficient. |
186 | 0 | static void yuva_copy_row(const SkYUVAPixmaps& src, int row, uint8_t* dst) { |
187 | 0 | int width = src.plane(0).width(); |
188 | 0 | switch (src.yuvaInfo().planeConfig()) { |
189 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V: { |
190 | 0 | auto [ssWidthU, ssHeightU] = src.yuvaInfo().planeSubsamplingFactors(1); |
191 | 0 | auto [ssWidthV, ssHeightV] = src.yuvaInfo().planeSubsamplingFactors(2); |
192 | 0 | const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src.plane(0).addr(0, row)); |
193 | 0 | const uint8_t* srcU = |
194 | 0 | reinterpret_cast<const uint8_t*>(src.plane(1).addr(0, row / ssHeightU)); |
195 | 0 | const uint8_t* srcV = |
196 | 0 | reinterpret_cast<const uint8_t*>(src.plane(2).addr(0, row / ssHeightV)); |
197 | 0 | for (int col = 0; col < width; ++col) { |
198 | 0 | dst[3 * col + 0] = srcY[col]; |
199 | 0 | dst[3 * col + 1] = srcU[col / ssWidthU]; |
200 | 0 | dst[3 * col + 2] = srcV[col / ssWidthV]; |
201 | 0 | } |
202 | 0 | break; |
203 | 0 | } |
204 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV: { |
205 | 0 | auto [ssWidthUV, ssHeightUV] = src.yuvaInfo().planeSubsamplingFactors(1); |
206 | 0 | const uint8_t* srcY = reinterpret_cast<const uint8_t*>(src.plane(0).addr(0, row)); |
207 | 0 | const uint8_t* srcUV = |
208 | 0 | reinterpret_cast<const uint8_t*>(src.plane(1).addr(0, row / ssHeightUV)); |
209 | 0 | for (int col = 0; col < width; ++col) { |
210 | 0 | dst[3 * col + 0] = srcY[col]; |
211 | 0 | dst[3 * col + 1] = srcUV[2 * (col / ssWidthUV) + 0]; |
212 | 0 | dst[3 * col + 2] = srcUV[2 * (col / ssWidthUV) + 1]; |
213 | 0 | } |
214 | 0 | break; |
215 | 0 | } |
216 | 0 | default: |
217 | 0 | break; |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | bool SkJpegEncoderMgr::initializeYUV(const SkYUVAPixmapInfo& srcInfo, |
222 | | const SkJpegEncoder::Options& options, |
223 | 0 | const SkJpegMetadataEncoder::SegmentList& metadataSegments) { |
224 | 0 | fCInfo.image_width = srcInfo.yuvaInfo().width(); |
225 | 0 | fCInfo.image_height = srcInfo.yuvaInfo().height(); |
226 | 0 | fCInfo.in_color_space = JCS_YCbCr; |
227 | 0 | fCInfo.input_components = 3; |
228 | 0 | jpeg_set_defaults(&fCInfo); |
229 | | |
230 | | // Support no color space conversion. |
231 | 0 | if (srcInfo.yuvColorSpace() != kJPEG_Full_SkYUVColorSpace) { |
232 | 0 | return false; |
233 | 0 | } |
234 | | |
235 | | // Support only 8-bit data. |
236 | 0 | switch (srcInfo.dataType()) { |
237 | 0 | case SkYUVAPixmapInfo::DataType::kUnorm8: |
238 | 0 | break; |
239 | 0 | default: |
240 | 0 | return false; |
241 | 0 | } |
242 | | |
243 | | // Support only Y,U,V and Y,UV configurations (they are the only ones supported by |
244 | | // yuva_copy_row). |
245 | 0 | switch (srcInfo.yuvaInfo().planeConfig()) { |
246 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V: |
247 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV: |
248 | 0 | break; |
249 | 0 | default: |
250 | 0 | return false; |
251 | 0 | } |
252 | | |
253 | | // Specify to the encoder to use the same subsampling as the input image. The U and V planes |
254 | | // always have a sampling factor of 1. |
255 | 0 | auto [ssHoriz, ssVert] = SkYUVAInfo::SubsamplingFactors(srcInfo.yuvaInfo().subsampling()); |
256 | 0 | fCInfo.comp_info[0].h_samp_factor = ssHoriz; |
257 | 0 | fCInfo.comp_info[0].v_samp_factor = ssVert; |
258 | |
|
259 | 0 | initializeCommon(options, metadataSegments); |
260 | 0 | return true; |
261 | 0 | } |
262 | | |
263 | | void SkJpegEncoderMgr::initializeCommon( |
264 | | const SkJpegEncoder::Options& options, |
265 | 4.67k | const SkJpegMetadataEncoder::SegmentList& metadataSegments) { |
266 | | // Tells libjpeg-turbo to compute optimal Huffman coding tables |
267 | | // for the image. This improves compression at the cost of |
268 | | // slower encode performance. |
269 | 4.67k | fCInfo.optimize_coding = TRUE; |
270 | | |
271 | 4.67k | jpeg_set_quality(&fCInfo, options.fQuality, TRUE); |
272 | 4.67k | jpeg_start_compress(&fCInfo, TRUE); |
273 | | |
274 | 4.67k | for (const auto& segment : metadataSegments) { |
275 | 0 | jpeg_write_marker(&fCInfo, |
276 | 0 | segment.fMarker, |
277 | 0 | segment.fParameters->bytes(), |
278 | 0 | segment.fParameters->size()); |
279 | 0 | } |
280 | 4.67k | } |
281 | | |
282 | | std::unique_ptr<SkEncoder> SkJpegEncoderImpl::MakeYUV( |
283 | | SkWStream* dst, |
284 | | const SkYUVAPixmaps& srcYUVA, |
285 | | const SkColorSpace* srcYUVAColorSpace, |
286 | | const SkJpegEncoder::Options& options, |
287 | 0 | const SkJpegMetadataEncoder::SegmentList& metadataSegments) { |
288 | 0 | if (!srcYUVA.isValid()) { |
289 | 0 | return nullptr; |
290 | 0 | } |
291 | 0 | std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst); |
292 | 0 | skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr()); |
293 | 0 | if (setjmp(jmp)) { |
294 | 0 | return nullptr; |
295 | 0 | } |
296 | | |
297 | 0 | if (!encoderMgr->initializeYUV(srcYUVA.pixmapsInfo(), options, metadataSegments)) { |
298 | 0 | return nullptr; |
299 | 0 | } |
300 | 0 | return std::unique_ptr<SkJpegEncoderImpl>( |
301 | 0 | new SkJpegEncoderImpl(std::move(encoderMgr), srcYUVA)); |
302 | 0 | } |
303 | | |
304 | | std::unique_ptr<SkEncoder> SkJpegEncoderImpl::MakeRGB( |
305 | | SkWStream* dst, |
306 | | const SkPixmap& src, |
307 | | const SkJpegEncoder::Options& options, |
308 | 4.79k | const SkJpegMetadataEncoder::SegmentList& metadataSegments) { |
309 | 4.79k | if (!SkPixmapIsValid(src)) { |
310 | 122 | return nullptr; |
311 | 122 | } |
312 | 4.67k | std::unique_ptr<SkJpegEncoderMgr> encoderMgr = SkJpegEncoderMgr::Make(dst); |
313 | 4.67k | skjpeg_error_mgr::AutoPushJmpBuf jmp(encoderMgr->errorMgr()); |
314 | 4.67k | if (setjmp(jmp)) { |
315 | 0 | return nullptr; |
316 | 0 | } |
317 | | |
318 | 4.67k | if (!encoderMgr->initializeRGB(src.info(), options, metadataSegments)) { |
319 | 0 | return nullptr; |
320 | 0 | } |
321 | 4.67k | return std::unique_ptr<SkJpegEncoderImpl>(new SkJpegEncoderImpl(std::move(encoderMgr), src)); |
322 | 4.67k | } |
323 | | |
324 | | SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, |
325 | | const SkPixmap& src) |
326 | | : SkEncoder(src, |
327 | | encoderMgr->proc() ? encoderMgr->cinfo()->input_components * src.width() : 0) |
328 | 4.67k | , fEncoderMgr(std::move(encoderMgr)) {} |
329 | | |
330 | | SkJpegEncoderImpl::SkJpegEncoderImpl(std::unique_ptr<SkJpegEncoderMgr> encoderMgr, |
331 | | const SkYUVAPixmaps& src) |
332 | | : SkEncoder(src.plane(0), encoderMgr->cinfo()->input_components * src.yuvaInfo().width()) |
333 | | , fEncoderMgr(std::move(encoderMgr)) |
334 | 0 | , fSrcYUVA(src) {} |
335 | | |
336 | 4.67k | SkJpegEncoderImpl::~SkJpegEncoderImpl() {} |
337 | | |
338 | 4.67k | bool SkJpegEncoderImpl::onEncodeRows(int numRows) { |
339 | 4.67k | skjpeg_error_mgr::AutoPushJmpBuf jmp(fEncoderMgr->errorMgr()); |
340 | 4.67k | if (setjmp(jmp)) { |
341 | 0 | return false; |
342 | 0 | } |
343 | | |
344 | 4.67k | if (fSrcYUVA) { |
345 | | // TODO(ccameron): Consider using jpeg_write_raw_data, to avoid having to re-pack the data. |
346 | 0 | for (int i = 0; i < numRows; i++) { |
347 | 0 | yuva_copy_row(*fSrcYUVA, fCurrRow + i, fStorage.get()); |
348 | 0 | JSAMPLE* jpegSrcRow = fStorage.get(); |
349 | 0 | jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1); |
350 | 0 | } |
351 | 4.67k | } else { |
352 | 4.67k | const size_t srcBytes = SkColorTypeBytesPerPixel(fSrc.colorType()) * fSrc.width(); |
353 | 4.67k | const size_t jpegSrcBytes = fEncoderMgr->cinfo()->input_components * fSrc.width(); |
354 | 4.67k | const void* srcRow = fSrc.addr(0, fCurrRow); |
355 | 805k | for (int i = 0; i < numRows; i++) { |
356 | 800k | JSAMPLE* jpegSrcRow = (JSAMPLE*)(const_cast<void*>(srcRow)); |
357 | 800k | if (fEncoderMgr->proc()) { |
358 | 0 | sk_msan_assert_initialized(srcRow, SkTAddOffset<const void>(srcRow, srcBytes)); |
359 | 0 | fEncoderMgr->proc()((char*)fStorage.get(), |
360 | 0 | (const char*)srcRow, |
361 | 0 | fSrc.width(), |
362 | 0 | fEncoderMgr->cinfo()->input_components); |
363 | 0 | jpegSrcRow = fStorage.get(); |
364 | 0 | sk_msan_assert_initialized(jpegSrcRow, |
365 | 0 | SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes)); |
366 | 800k | } else { |
367 | | // Same as above, but this repetition allows determining whether a |
368 | | // proc was used when msan asserts. |
369 | 800k | sk_msan_assert_initialized(jpegSrcRow, |
370 | 800k | SkTAddOffset<const void>(jpegSrcRow, jpegSrcBytes)); |
371 | 800k | } |
372 | | |
373 | 800k | jpeg_write_scanlines(fEncoderMgr->cinfo(), &jpegSrcRow, 1); |
374 | 800k | srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes()); |
375 | 800k | } |
376 | 4.67k | } |
377 | | |
378 | 4.67k | fCurrRow += numRows; |
379 | 4.67k | if (fCurrRow == fSrc.height()) { |
380 | 4.67k | jpeg_finish_compress(fEncoderMgr->cinfo()); |
381 | 4.67k | } |
382 | | |
383 | 4.67k | return true; |
384 | 4.67k | } |
385 | | |
386 | | namespace SkJpegEncoder { |
387 | | |
388 | 4.79k | bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { |
389 | 4.79k | auto encoder = Make(dst, src, options); |
390 | 4.79k | return encoder.get() && encoder->encodeRows(src.height()); |
391 | 4.79k | } |
392 | | |
393 | | bool Encode(SkWStream* dst, |
394 | | const SkYUVAPixmaps& src, |
395 | | const SkColorSpace* srcColorSpace, |
396 | 0 | const Options& options) { |
397 | 0 | auto encoder = Make(dst, src, srcColorSpace, options); |
398 | 0 | return encoder.get() && encoder->encodeRows(src.yuvaInfo().height()); |
399 | 0 | } |
400 | | |
401 | 0 | sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) { |
402 | 0 | if (!img) { |
403 | 0 | return nullptr; |
404 | 0 | } |
405 | 0 | SkBitmap bm; |
406 | 0 | if (!as_IB(img)->getROPixels(ctx, &bm)) { |
407 | 0 | return nullptr; |
408 | 0 | } |
409 | 0 | SkDynamicMemoryWStream stream; |
410 | 0 | if (Encode(&stream, bm.pixmap(), options)) { |
411 | 0 | return stream.detachAsData(); |
412 | 0 | } |
413 | 0 | return nullptr; |
414 | 0 | } |
415 | | |
416 | 4.79k | std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Options& options) { |
417 | 4.79k | SkJpegMetadataEncoder::SegmentList metadataSegments; |
418 | 4.79k | SkJpegMetadataEncoder::AppendXMPStandard(metadataSegments, options.xmpMetadata); |
419 | 4.79k | SkJpegMetadataEncoder::AppendICC(metadataSegments, options, src.colorSpace()); |
420 | 4.79k | return SkJpegEncoderImpl::MakeRGB(dst, src, options, metadataSegments); |
421 | 4.79k | } |
422 | | |
423 | | std::unique_ptr<SkEncoder> Make(SkWStream* dst, |
424 | | const SkYUVAPixmaps& src, |
425 | | const SkColorSpace* srcColorSpace, |
426 | 0 | const Options& options) { |
427 | 0 | SkJpegMetadataEncoder::SegmentList metadataSegments; |
428 | 0 | SkJpegMetadataEncoder::AppendXMPStandard(metadataSegments, options.xmpMetadata); |
429 | 0 | SkJpegMetadataEncoder::AppendICC(metadataSegments, options, srcColorSpace); |
430 | 0 | return SkJpegEncoderImpl::MakeYUV(dst, src, srcColorSpace, options, metadataSegments); |
431 | 0 | } |
432 | | |
433 | | } // namespace SkJpegEncoder |
434 | | |
435 | | namespace SkJpegMetadataEncoder { |
436 | | |
437 | | void AppendICC(SegmentList& segmentList, |
438 | | const SkJpegEncoder::Options& options, |
439 | 4.79k | const SkColorSpace* colorSpace) { |
440 | 4.79k | sk_sp<SkData> icc = |
441 | 4.79k | icc_from_color_space(colorSpace, options.fICCProfile, options.fICCProfileDescription); |
442 | 4.79k | if (!icc) { |
443 | 4.79k | return; |
444 | 4.79k | } |
445 | | |
446 | | // TODO(ccameron): This limits ICC profile size to a single segment's parameters (less than |
447 | | // 64k). Split larger profiles into more segments. |
448 | 0 | SkDynamicMemoryWStream s; |
449 | 0 | s.write(kICCSig, sizeof(kICCSig)); |
450 | 0 | s.write8(1); // This is the first marker. |
451 | 0 | s.write8(1); // Out of one total markers. |
452 | 0 | s.write(icc->data(), icc->size()); |
453 | 0 | segmentList.emplace_back(kICCMarker, s.detachAsData()); |
454 | 0 | } |
455 | | |
456 | 4.79k | void AppendXMPStandard(SegmentList& segmentList, const SkData* xmpMetadata) { |
457 | 4.79k | if (!xmpMetadata) { |
458 | 4.79k | return; |
459 | 4.79k | } |
460 | | |
461 | | // TODO(ccameron): Split this into a standard and extended XMP segment if needed. |
462 | 0 | SkDynamicMemoryWStream s; |
463 | 0 | s.write(kXMPStandardSig, sizeof(kXMPStandardSig)); |
464 | 0 | s.write(xmpMetadata->data(), xmpMetadata->size()); |
465 | 0 | segmentList.emplace_back(kXMPMarker, s.detachAsData()); |
466 | 0 | } |
467 | | |
468 | | } // namespace SkJpegMetadataEncoder |