32 const float kLog2 = std::log(2.f);
46 auto write_scalar_attr = [&
s](
const char* attrib,
SkScalar value) {
50 s.writeScalarAsText(
value);
56 auto maybe_write_scalar_attr = [&write_scalar_attr](
const char* attrib,
SkColor4f value) {
60 write_scalar_attr(attrib,
value.fR);
65 auto maybe_write_float3_attr = [&
s](
const char* attrib,
SkColor4f value) {
72 s.writeText(
" <rdf:Seq>\n");
73 s.writeText(
" <rdf:li>");
74 s.writeScalarAsText(
value.fR);
75 s.writeText(
"</rdf:li>\n");
76 s.writeText(
" <rdf:li>");
77 s.writeScalarAsText(
value.fG);
78 s.writeText(
"</rdf:li>\n");
79 s.writeText(
" <rdf:li>");
80 s.writeScalarAsText(
value.fB);
81 s.writeText(
"</rdf:li>\n");
82 s.writeText(
" </rdf:Seq>\n");
89 "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"XMP Core 5.5.0\">\n"
90 " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
91 " <rdf:Description rdf:about=\"\"\n"
92 " xmlns:hdrgm=\"http://ns.adobe.com/hdr-gain-map/1.0/\"\n"
93 " hdrgm:Version=\"1.0\"\n");
94 maybe_write_scalar_attr(
"hdrgm:GainMapMin", gainMapMin);
95 maybe_write_scalar_attr(
"hdrgm:GainMapMax", gainMapMax);
96 maybe_write_scalar_attr(
"hdrgm:Gamma", gamma);
97 maybe_write_scalar_attr(
"hdrgm:OffsetSDR", gainmapInfo.
fEpsilonSdr);
98 maybe_write_scalar_attr(
"hdrgm:OffsetHDR", gainmapInfo.
fEpsilonHdr);
99 write_scalar_attr(
"hdrgm:HDRCapacityMin", std::log(gainmapInfo.
fDisplayRatioSdr) / kLog2);
100 write_scalar_attr(
"hdrgm:HDRCapacityMax", std::log(gainmapInfo.
fDisplayRatioHdr) / kLog2);
103 s.writeText(
" hdrgm:BaseRenditionIsHDR=\"False\">\n");
106 s.writeText(
" hdrgm:BaseRenditionIsHDR=\"True\">\n");
112 maybe_write_float3_attr(
"hdrgm:GainMapMin", gainMapMin);
113 maybe_write_float3_attr(
"hdrgm:GainMapMax", gainMapMax);
114 maybe_write_float3_attr(
"hdrgm:Gamma", gamma);
115 maybe_write_float3_attr(
"hdrgm:OffsetSDR", gainmapInfo.
fEpsilonSdr);
116 maybe_write_float3_attr(
"hdrgm:OffsetHDR", gainmapInfo.
fEpsilonHdr);
118 " </rdf:Description>\n"
121 return s.detachAsData();
128 "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 5.1.2\">\n"
129 " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
130 " <rdf:Description\n"
131 " xmlns:Container=\"http://ns.google.com/photos/1.0/container/\"\n"
132 " xmlns:Item=\"http://ns.google.com/photos/1.0/container/item/\"\n"
133 " xmlns:hdrgm=\"http://ns.adobe.com/hdr-gain-map/1.0/\"\n"
134 " hdrgm:Version=\"1.0\">\n"
135 " <Container:Directory>\n"
137 " <rdf:li rdf:parseType=\"Resource\">\n"
139 " Item:Semantic=\"Primary\"\n"
140 " Item:Mime=\"image/jpeg\"/>\n"
142 " <rdf:li rdf:parseType=\"Resource\">\n"
144 " Item:Semantic=\"GainMap\"\n"
145 " Item:Mime=\"image/jpeg\"\n"
147 s.writeDecAsText(gainmapItemLength);
152 " </Container:Directory>\n"
153 " </rdf:Description>\n"
156 return s.detachAsData();
161 size_t segmentMaxDataSize) {
167 const size_t kGainmapPayloadSize = segmentMaxDataSize - kGainmapHeaderSize;
170 const size_t segmentCount = (
image->size() + kGainmapPayloadSize - 1) / kGainmapPayloadSize;
171 std::vector<sk_sp<SkData>>
result;
172 result.reserve(segmentCount);
175 const uint8_t* imageData =
image->bytes();
176 const uint8_t* imageDataEnd =
image->bytes() +
image->size();
177 while (imageData < imageDataEnd) {
184 size_t segmentIndex =
result.size() + 1;
185 uint8_t segmentIndexBytes[2] = {
186 static_cast<uint8_t
>(segmentIndex / 256u),
187 static_cast<uint8_t
>(segmentIndex % 256u),
189 segmentStream.
write(segmentIndexBytes,
sizeof(segmentIndexBytes));
192 uint8_t segmentCountBytes[2] = {
193 static_cast<uint8_t
>(segmentCount / 256u),
194 static_cast<uint8_t
>(segmentCount % 256u),
196 segmentStream.
write(segmentCountBytes,
sizeof(segmentCountBytes));
202 size_t bytesToWrite =
203 std::min(imageDataEnd - imageData,
static_cast<intptr_t
>(kGainmapPayloadSize));
204 segmentStream.
write(imageData, bytesToWrite);
205 imageData += bytesToWrite;
208 if (segmentIndex == segmentCount) {
217 SkASSERT(imageData == imageDataEnd);
237 auto segmentParameters = mpParams.
serialize();
241 s.write8(mpParameterLength / 256);
242 s.write8(mpParameterLength % 256);
243 s.write(segmentParameters->data(), segmentParameters->size());
244 return s.detachAsData();
258 gainmapData =
encode_to_data(gainmap, gainmapOptions, hdrgmXmp.get());
285 if (imageCount < 1) {
292 if (!primaryScan.
isDone()) {
293 SkCodecPrintf(
"Failed to scan encoded primary image header.\n");
299 size_t bytesRead = 0;
300 size_t bytesWritten = 0;
301 for (
const auto& segment : primaryScan.
getSegments()) {
304 size_t ecdBytesToWrite = segment.offset - bytesRead;
305 if (!dst->write(
images[0]->bytes() + bytesRead, ecdBytesToWrite)) {
309 bytesWritten += ecdBytesToWrite;
310 bytesRead = segment.offset;
316 if (!dst->write(
images[0]->bytes() + bytesRead, bytesToWrite)) {
320 bytesWritten += bytesToWrite;
321 bytesRead += bytesToWrite;
326 const size_t bytesRemaining =
images[0]->size() - bytesRead;
331 mpParams.
images.resize(imageCount);
335 static_cast<uint32_t
>(bytesWritten + mpSegmentSize + bytesRemaining);
339 for (
size_t i = 1; i < imageCount; ++i) {
341 mpParams.
images[i].size =
static_cast<uint32_t
>(
images[i]->size());
348 if (!dst->write(mpfSegment->data(), mpfSegment->size())) {
354 if (!dst->write(
images[0]->bytes() + bytesRead, bytesRemaining)) {
355 SkCodecPrintf(
"Failed to write remainder of primary image.\n");
358 bytesRead += bytesRemaining;
364 for (
size_t i = 1; i < imageCount; ++i) {
365 if (!dst->write(
images[i]->data(),
images[i]->size())) {
#define SkCodecPrintf(...)
static constexpr uint32_t kMpfMarker
static constexpr uint8_t kMpfSig[]
static constexpr uint32_t kGainmapMarkerIndexSize
static constexpr uint8_t kJpegMarkerStartOfScan
static constexpr size_t kJpegSegmentParameterLengthSize
static constexpr size_t kJpegMarkerCodeSize
static constexpr uint8_t kGainmapSig[]
static bool is_single_channel(SkColor4f c)
static sk_sp< SkData > get_mpf_segment(const SkJpegMultiPictureParameters &mpParams)
static sk_sp< SkData > get_gcontainer_xmp_data(size_t gainmapItemLength)
static sk_sp< SkData > encode_to_data(const SkPixmap &pm, const SkJpegEncoder::Options &options, SkData *xmpMetadata)
sk_sp< SkData > get_hdrgm_xmp_data(const SkGainmapInfo &gainmapInfo)
std::vector< sk_sp< SkData > > get_hdrgm_image_segments(sk_sp< SkData > image, size_t segmentMaxDataSize)
size_t bytesWritten() const override
bool write(const void *buffer, size_t size) override
sk_sp< SkData > detachAsData()
static bool EncodeHDRGM(SkWStream *dst, const SkPixmap &base, const SkJpegEncoder::Options &baseOptions, const SkPixmap &gainmap, const SkJpegEncoder::Options &gainmapOptions, const SkGainmapInfo &gainmapInfo)
static bool MakeMPF(SkWStream *dst, const SkData **images, size_t imageCount)
const std::vector< SkJpegSegment > & getSegments() const
void onBytes(const void *data, size_t size)
std::array< MockImage, 3 > images
SK_API std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap &src, const Options &options)
SkColor4f fGainmapRatioMax
SkColor4f fGainmapRatioMin
BaseImageType fBaseImageType
const SkData * xmpMetadata
sk_sp< SkData > serialize() const
std::vector< Image > images