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