xref: /aosp_15_r20/external/skia/src/pdf/SkPDFBitmap.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFBitmap.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkEncodedOrigin.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkJpegDecoder.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorPriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkExecutor.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/docs/SkPDFDocument.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkICC.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkJpegEncoder.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkEncodedInfo.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMutex.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skcms/skcms.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkDeflate.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFDocumentPriv.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFTypes.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFUnion.h"
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
41*c8dee2aaSAndroid Build Coastguard Worker #include <array>
42*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
43*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
44*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
45*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
46*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
47*c8dee2aaSAndroid Build Coastguard Worker 
GetEncodedInfo(SkCodec & codec)48*c8dee2aaSAndroid Build Coastguard Worker /*static*/ const SkEncodedInfo& SkPDFBitmap::GetEncodedInfo(SkCodec& codec) {
49*c8dee2aaSAndroid Build Coastguard Worker     return codec.getEncodedInfo();
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker namespace {
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker // write a single byte to a stream n times.
fill_stream(SkWStream * out,char value,size_t n)55*c8dee2aaSAndroid Build Coastguard Worker void fill_stream(SkWStream* out, char value, size_t n) {
56*c8dee2aaSAndroid Build Coastguard Worker     char buffer[4096];
57*c8dee2aaSAndroid Build Coastguard Worker     memset(buffer, value, sizeof(buffer));
58*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < n / sizeof(buffer); ++i) {
59*c8dee2aaSAndroid Build Coastguard Worker         out->write(buffer, sizeof(buffer));
60*c8dee2aaSAndroid Build Coastguard Worker     }
61*c8dee2aaSAndroid Build Coastguard Worker     out->write(buffer, n % sizeof(buffer));
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
64*c8dee2aaSAndroid Build Coastguard Worker /* It is necessary to average the color component of transparent
65*c8dee2aaSAndroid Build Coastguard Worker    pixels with their surrounding neighbors since the PDF renderer may
66*c8dee2aaSAndroid Build Coastguard Worker    separately re-sample the alpha and color channels when the image is
67*c8dee2aaSAndroid Build Coastguard Worker    not displayed at its native resolution. Since an alpha of zero
68*c8dee2aaSAndroid Build Coastguard Worker    gives no information about the color component, the pathological
69*c8dee2aaSAndroid Build Coastguard Worker    case is a white image with sharp transparency bounds - the color
70*c8dee2aaSAndroid Build Coastguard Worker    channel goes to black, and the should-be-transparent pixels are
71*c8dee2aaSAndroid Build Coastguard Worker    rendered as grey because of the separate soft mask and color
72*c8dee2aaSAndroid Build Coastguard Worker    resizing. e.g.: gm/bitmappremul.cpp */
get_neighbor_avg_color(const SkPixmap & bm,int xOrig,int yOrig)73*c8dee2aaSAndroid Build Coastguard Worker SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
74*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
75*c8dee2aaSAndroid Build Coastguard Worker     unsigned r = 0, g = 0, b = 0, n = 0;
76*c8dee2aaSAndroid Build Coastguard Worker     // Clamp the range to the edge of the bitmap.
77*c8dee2aaSAndroid Build Coastguard Worker     int ymin = std::max(0, yOrig - 1);
78*c8dee2aaSAndroid Build Coastguard Worker     int ymax = std::min(yOrig + 1, bm.height() - 1);
79*c8dee2aaSAndroid Build Coastguard Worker     int xmin = std::max(0, xOrig - 1);
80*c8dee2aaSAndroid Build Coastguard Worker     int xmax = std::min(xOrig + 1, bm.width() - 1);
81*c8dee2aaSAndroid Build Coastguard Worker     for (int y = ymin; y <= ymax; ++y) {
82*c8dee2aaSAndroid Build Coastguard Worker         const SkColor* scanline = bm.addr32(0, y);
83*c8dee2aaSAndroid Build Coastguard Worker         for (int x = xmin; x <= xmax; ++x) {
84*c8dee2aaSAndroid Build Coastguard Worker             SkColor color = scanline[x];
85*c8dee2aaSAndroid Build Coastguard Worker             if (color != SK_ColorTRANSPARENT) {
86*c8dee2aaSAndroid Build Coastguard Worker                 r += SkColorGetR(color);
87*c8dee2aaSAndroid Build Coastguard Worker                 g += SkColorGetG(color);
88*c8dee2aaSAndroid Build Coastguard Worker                 b += SkColorGetB(color);
89*c8dee2aaSAndroid Build Coastguard Worker                 n++;
90*c8dee2aaSAndroid Build Coastguard Worker             }
91*c8dee2aaSAndroid Build Coastguard Worker         }
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker     return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n))
94*c8dee2aaSAndroid Build Coastguard Worker                  : SK_ColorTRANSPARENT;
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker enum class SkPDFStreamFormat { DCT, Flate, Uncompressed };
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
emit_image_stream(SkPDFDocument * doc,SkPDFIndirectReference ref,T writeStream,SkISize size,SkPDFUnion && colorSpace,SkPDFIndirectReference sMask,int length,SkPDFStreamFormat format)100*c8dee2aaSAndroid Build Coastguard Worker void emit_image_stream(SkPDFDocument* doc,
101*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFIndirectReference ref,
102*c8dee2aaSAndroid Build Coastguard Worker                        T writeStream,
103*c8dee2aaSAndroid Build Coastguard Worker                        SkISize size,
104*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFUnion&& colorSpace,
105*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFIndirectReference sMask,
106*c8dee2aaSAndroid Build Coastguard Worker                        int length,
107*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFStreamFormat format) {
108*c8dee2aaSAndroid Build Coastguard Worker     SkPDFDict pdfDict("XObject");
109*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertName("Subtype", "Image");
110*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertInt("Width", size.width());
111*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertInt("Height", size.height());
112*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertUnion("ColorSpace", std::move(colorSpace));
113*c8dee2aaSAndroid Build Coastguard Worker     if (sMask) {
114*c8dee2aaSAndroid Build Coastguard Worker         pdfDict.insertRef("SMask", sMask);
115*c8dee2aaSAndroid Build Coastguard Worker     }
116*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertInt("BitsPerComponent", 8);
117*c8dee2aaSAndroid Build Coastguard Worker     #ifdef SK_PDF_BASE85_BINARY
118*c8dee2aaSAndroid Build Coastguard Worker     auto filters = SkPDFMakeArray();
119*c8dee2aaSAndroid Build Coastguard Worker     filters->appendName("ASCII85Decode");
120*c8dee2aaSAndroid Build Coastguard Worker     switch (format) {
121*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::DCT: filters->appendName("DCTDecode"); break;
122*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::Flate: filters->appendName("FlateDecode"); break;
123*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::Uncompressed: break;
124*c8dee2aaSAndroid Build Coastguard Worker     }
125*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertObject("Filter", std::move(filters));
126*c8dee2aaSAndroid Build Coastguard Worker     #else
127*c8dee2aaSAndroid Build Coastguard Worker     switch (format) {
128*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::DCT: pdfDict.insertName("Filter", "DCTDecode"); break;
129*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::Flate: pdfDict.insertName("Filter", "FlateDecode"); break;
130*c8dee2aaSAndroid Build Coastguard Worker         case SkPDFStreamFormat::Uncompressed: break;
131*c8dee2aaSAndroid Build Coastguard Worker     }
132*c8dee2aaSAndroid Build Coastguard Worker     #endif
133*c8dee2aaSAndroid Build Coastguard Worker     if (format == SkPDFStreamFormat::DCT) {
134*c8dee2aaSAndroid Build Coastguard Worker         pdfDict.insertInt("ColorTransform", 0);
135*c8dee2aaSAndroid Build Coastguard Worker     }
136*c8dee2aaSAndroid Build Coastguard Worker     pdfDict.insertInt("Length", length);
137*c8dee2aaSAndroid Build Coastguard Worker     doc->emitStream(pdfDict, std::move(writeStream), ref);
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker 
do_deflated_alpha(const SkPixmap & pm,SkPDFDocument * doc,SkPDFIndirectReference ref)140*c8dee2aaSAndroid Build Coastguard Worker void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) {
141*c8dee2aaSAndroid Build Coastguard Worker     SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
142*c8dee2aaSAndroid Build Coastguard Worker     SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
143*c8dee2aaSAndroid Build Coastguard Worker                              ? SkPDFStreamFormat::Uncompressed
144*c8dee2aaSAndroid Build Coastguard Worker                              : SkPDFStreamFormat::Flate;
145*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream buffer;
146*c8dee2aaSAndroid Build Coastguard Worker     SkWStream* stream = &buffer;
147*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SkDeflateWStream> deflateWStream;
148*c8dee2aaSAndroid Build Coastguard Worker     if (format == SkPDFStreamFormat::Flate) {
149*c8dee2aaSAndroid Build Coastguard Worker         deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
150*c8dee2aaSAndroid Build Coastguard Worker         stream = &*deflateWStream;
151*c8dee2aaSAndroid Build Coastguard Worker     }
152*c8dee2aaSAndroid Build Coastguard Worker     if (kAlpha_8_SkColorType == pm.colorType()) {
153*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(pm.rowBytes() == (size_t)pm.width());
154*c8dee2aaSAndroid Build Coastguard Worker         stream->write(pm.addr8(), pm.width() * pm.height());
155*c8dee2aaSAndroid Build Coastguard Worker     } else {
156*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
157*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
158*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
159*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t* ptr = pm.addr32();
160*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t* stop = ptr + pm.height() * pm.width();
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker         uint8_t byteBuffer[4092];
163*c8dee2aaSAndroid Build Coastguard Worker         uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
164*c8dee2aaSAndroid Build Coastguard Worker         uint8_t* dst = byteBuffer;
165*c8dee2aaSAndroid Build Coastguard Worker         while (ptr != stop) {
166*c8dee2aaSAndroid Build Coastguard Worker             *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
167*c8dee2aaSAndroid Build Coastguard Worker             if (dst == bufferStop) {
168*c8dee2aaSAndroid Build Coastguard Worker                 stream->write(byteBuffer, sizeof(byteBuffer));
169*c8dee2aaSAndroid Build Coastguard Worker                 dst = byteBuffer;
170*c8dee2aaSAndroid Build Coastguard Worker             }
171*c8dee2aaSAndroid Build Coastguard Worker         }
172*c8dee2aaSAndroid Build Coastguard Worker         stream->write(byteBuffer, dst - byteBuffer);
173*c8dee2aaSAndroid Build Coastguard Worker     }
174*c8dee2aaSAndroid Build Coastguard Worker     if (deflateWStream) {
175*c8dee2aaSAndroid Build Coastguard Worker         deflateWStream->finalize();
176*c8dee2aaSAndroid Build Coastguard Worker     }
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     #ifdef SK_PDF_BASE85_BINARY
179*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
180*c8dee2aaSAndroid Build Coastguard Worker     #endif
181*c8dee2aaSAndroid Build Coastguard Worker     int length = SkToInt(buffer.bytesWritten());
182*c8dee2aaSAndroid Build Coastguard Worker     emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
183*c8dee2aaSAndroid Build Coastguard Worker                       pm.info().dimensions(), SkPDFUnion::Name("DeviceGray"),
184*c8dee2aaSAndroid Build Coastguard Worker                       SkPDFIndirectReference(), length, format);
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker 
write_icc_profile(SkPDFDocument * doc,sk_sp<SkData> && icc,int channels)187*c8dee2aaSAndroid Build Coastguard Worker SkPDFUnion write_icc_profile(SkPDFDocument* doc, sk_sp<SkData>&& icc, int channels) {
188*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference iccStreamRef;
189*c8dee2aaSAndroid Build Coastguard Worker     {
190*c8dee2aaSAndroid Build Coastguard Worker         static SkMutex iccProfileMapMutex;
191*c8dee2aaSAndroid Build Coastguard Worker         SkAutoMutexExclusive lock(iccProfileMapMutex);
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker         SkPDFIndirectReference* ref = doc->fICCProfileMap.find(SkPDFIccProfileKey{icc, channels});
194*c8dee2aaSAndroid Build Coastguard Worker         if (ref) {
195*c8dee2aaSAndroid Build Coastguard Worker             iccStreamRef = *ref;
196*c8dee2aaSAndroid Build Coastguard Worker         } else {
197*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<SkPDFDict> iccStreamDict = SkPDFMakeDict();
198*c8dee2aaSAndroid Build Coastguard Worker             iccStreamDict->insertInt("N", channels);
199*c8dee2aaSAndroid Build Coastguard Worker             iccStreamRef = SkPDFStreamOut(std::move(iccStreamDict), SkMemoryStream::Make(icc), doc);
200*c8dee2aaSAndroid Build Coastguard Worker             doc->fICCProfileMap.set(SkPDFIccProfileKey{icc, channels}, iccStreamRef);
201*c8dee2aaSAndroid Build Coastguard Worker         }
202*c8dee2aaSAndroid Build Coastguard Worker     }
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkPDFArray> iccPDF = SkPDFMakeArray();
205*c8dee2aaSAndroid Build Coastguard Worker     iccPDF->appendName("ICCBased");
206*c8dee2aaSAndroid Build Coastguard Worker     iccPDF->appendRef(iccStreamRef);
207*c8dee2aaSAndroid Build Coastguard Worker     return SkPDFUnion::Object(std::move(iccPDF));
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker 
icc_channel_mismatch(const skcms_ICCProfile * iccProfile,int expectedChannels)210*c8dee2aaSAndroid Build Coastguard Worker bool icc_channel_mismatch(const skcms_ICCProfile* iccProfile, int expectedChannels) {
211*c8dee2aaSAndroid Build Coastguard Worker     int iccChannels = -1;
212*c8dee2aaSAndroid Build Coastguard Worker     if (iccProfile) {
213*c8dee2aaSAndroid Build Coastguard Worker         iccChannels = skcms_GetInputChannelCount(iccProfile);
214*c8dee2aaSAndroid Build Coastguard Worker     }
215*c8dee2aaSAndroid Build Coastguard Worker     return 0 < iccChannels && expectedChannels != iccChannels;
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker 
do_deflated_image(const SkPixmap & pm,SkPDFDocument * doc,bool isOpaque,SkPDFIndirectReference ref)218*c8dee2aaSAndroid Build Coastguard Worker void do_deflated_image(const SkPixmap& pm,
219*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFDocument* doc,
220*c8dee2aaSAndroid Build Coastguard Worker                        bool isOpaque,
221*c8dee2aaSAndroid Build Coastguard Worker                        SkPDFIndirectReference ref) {
222*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference sMask;
223*c8dee2aaSAndroid Build Coastguard Worker     if (!isOpaque) {
224*c8dee2aaSAndroid Build Coastguard Worker         sMask = doc->reserveRef();
225*c8dee2aaSAndroid Build Coastguard Worker     }
226*c8dee2aaSAndroid Build Coastguard Worker     SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
227*c8dee2aaSAndroid Build Coastguard Worker     SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
228*c8dee2aaSAndroid Build Coastguard Worker                              ? SkPDFStreamFormat::Uncompressed
229*c8dee2aaSAndroid Build Coastguard Worker                              : SkPDFStreamFormat::Flate;
230*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream buffer;
231*c8dee2aaSAndroid Build Coastguard Worker     SkWStream* stream = &buffer;
232*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SkDeflateWStream> deflateWStream;
233*c8dee2aaSAndroid Build Coastguard Worker     if (format == SkPDFStreamFormat::Flate) {
234*c8dee2aaSAndroid Build Coastguard Worker         deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
235*c8dee2aaSAndroid Build Coastguard Worker         stream = &*deflateWStream;
236*c8dee2aaSAndroid Build Coastguard Worker     }
237*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUnion colorSpace = SkPDFUnion::Name("DeviceGray");
238*c8dee2aaSAndroid Build Coastguard Worker     int channels;
239*c8dee2aaSAndroid Build Coastguard Worker     switch (pm.colorType()) {
240*c8dee2aaSAndroid Build Coastguard Worker         case kAlpha_8_SkColorType:
241*c8dee2aaSAndroid Build Coastguard Worker             channels = 1;
242*c8dee2aaSAndroid Build Coastguard Worker             fill_stream(stream, '\x00', pm.width() * pm.height());
243*c8dee2aaSAndroid Build Coastguard Worker             break;
244*c8dee2aaSAndroid Build Coastguard Worker         case kGray_8_SkColorType:
245*c8dee2aaSAndroid Build Coastguard Worker             channels = 1;
246*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(sMask.fValue = -1);
247*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(pm.rowBytes() == (size_t)pm.width());
248*c8dee2aaSAndroid Build Coastguard Worker             stream->write(pm.addr8(), pm.width() * pm.height());
249*c8dee2aaSAndroid Build Coastguard Worker             break;
250*c8dee2aaSAndroid Build Coastguard Worker         default:
251*c8dee2aaSAndroid Build Coastguard Worker             colorSpace = SkPDFUnion::Name("DeviceRGB");
252*c8dee2aaSAndroid Build Coastguard Worker             channels = 3;
253*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
254*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
255*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
256*c8dee2aaSAndroid Build Coastguard Worker             uint8_t byteBuffer[3072];
257*c8dee2aaSAndroid Build Coastguard Worker             static_assert(std::size(byteBuffer) % 3 == 0, "");
258*c8dee2aaSAndroid Build Coastguard Worker             uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
259*c8dee2aaSAndroid Build Coastguard Worker             uint8_t* dst = byteBuffer;
260*c8dee2aaSAndroid Build Coastguard Worker             for (int y = 0; y < pm.height(); ++y) {
261*c8dee2aaSAndroid Build Coastguard Worker                 const SkColor* src = pm.addr32(0, y);
262*c8dee2aaSAndroid Build Coastguard Worker                 for (int x = 0; x < pm.width(); ++x) {
263*c8dee2aaSAndroid Build Coastguard Worker                     SkColor color = *src++;
264*c8dee2aaSAndroid Build Coastguard Worker                     if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
265*c8dee2aaSAndroid Build Coastguard Worker                         color = get_neighbor_avg_color(pm, x, y);
266*c8dee2aaSAndroid Build Coastguard Worker                     }
267*c8dee2aaSAndroid Build Coastguard Worker                     *dst++ = SkColorGetR(color);
268*c8dee2aaSAndroid Build Coastguard Worker                     *dst++ = SkColorGetG(color);
269*c8dee2aaSAndroid Build Coastguard Worker                     *dst++ = SkColorGetB(color);
270*c8dee2aaSAndroid Build Coastguard Worker                     if (dst == bufferStop) {
271*c8dee2aaSAndroid Build Coastguard Worker                         stream->write(byteBuffer, sizeof(byteBuffer));
272*c8dee2aaSAndroid Build Coastguard Worker                         dst = byteBuffer;
273*c8dee2aaSAndroid Build Coastguard Worker                     }
274*c8dee2aaSAndroid Build Coastguard Worker                 }
275*c8dee2aaSAndroid Build Coastguard Worker             }
276*c8dee2aaSAndroid Build Coastguard Worker             stream->write(byteBuffer, dst - byteBuffer);
277*c8dee2aaSAndroid Build Coastguard Worker     }
278*c8dee2aaSAndroid Build Coastguard Worker     if (deflateWStream) {
279*c8dee2aaSAndroid Build Coastguard Worker         deflateWStream->finalize();
280*c8dee2aaSAndroid Build Coastguard Worker     }
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     if (pm.colorSpace()) {
283*c8dee2aaSAndroid Build Coastguard Worker         skcms_ICCProfile iccProfile;
284*c8dee2aaSAndroid Build Coastguard Worker         pm.colorSpace()->toProfile(&iccProfile);
285*c8dee2aaSAndroid Build Coastguard Worker         if (!icc_channel_mismatch(&iccProfile, channels)) {
286*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<SkData> iccData = SkWriteICCProfile(&iccProfile, "");
287*c8dee2aaSAndroid Build Coastguard Worker             colorSpace = write_icc_profile(doc, std::move(iccData), channels);
288*c8dee2aaSAndroid Build Coastguard Worker         }
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     #ifdef SK_PDF_BASE85_BINARY
292*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
293*c8dee2aaSAndroid Build Coastguard Worker     #endif
294*c8dee2aaSAndroid Build Coastguard Worker     int length = SkToInt(buffer.bytesWritten());
295*c8dee2aaSAndroid Build Coastguard Worker     emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
296*c8dee2aaSAndroid Build Coastguard Worker                       pm.info().dimensions(), std::move(colorSpace), sMask, length, format);
297*c8dee2aaSAndroid Build Coastguard Worker     if (!isOpaque) {
298*c8dee2aaSAndroid Build Coastguard Worker         do_deflated_alpha(pm, doc, sMask);
299*c8dee2aaSAndroid Build Coastguard Worker     }
300*c8dee2aaSAndroid Build Coastguard Worker }
301*c8dee2aaSAndroid Build Coastguard Worker 
do_jpeg(sk_sp<SkData> data,SkColorSpace * imageColorSpace,SkPDFDocument * doc,SkISize size,SkPDFIndirectReference ref)302*c8dee2aaSAndroid Build Coastguard Worker bool do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size,
303*c8dee2aaSAndroid Build Coastguard Worker              SkPDFIndirectReference ref) {
304*c8dee2aaSAndroid Build Coastguard Worker     static constexpr const SkCodecs::Decoder decoders[] = {
305*c8dee2aaSAndroid Build Coastguard Worker         SkJpegDecoder::Decoder(),
306*c8dee2aaSAndroid Build Coastguard Worker     };
307*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data, decoders);
308*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
309*c8dee2aaSAndroid Build Coastguard Worker         return false;
310*c8dee2aaSAndroid Build Coastguard Worker     }
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker     SkISize jpegSize = codec->dimensions();
313*c8dee2aaSAndroid Build Coastguard Worker     const SkEncodedInfo& encodedInfo = SkPDFBitmap::GetEncodedInfo(*codec);
314*c8dee2aaSAndroid Build Coastguard Worker     SkEncodedInfo::Color jpegColorType = encodedInfo.color();
315*c8dee2aaSAndroid Build Coastguard Worker     SkEncodedOrigin exifOrientation = codec->getOrigin();
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker     bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
318*c8dee2aaSAndroid Build Coastguard Worker     bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
319*c8dee2aaSAndroid Build Coastguard Worker     if (jpegSize != size  // Safety check.
320*c8dee2aaSAndroid Build Coastguard Worker             || !goodColorType
321*c8dee2aaSAndroid Build Coastguard Worker             || kTopLeft_SkEncodedOrigin != exifOrientation) {
322*c8dee2aaSAndroid Build Coastguard Worker         return false;
323*c8dee2aaSAndroid Build Coastguard Worker     }
324*c8dee2aaSAndroid Build Coastguard Worker     #ifdef SK_PDF_BASE85_BINARY
325*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream buffer;
326*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
327*c8dee2aaSAndroid Build Coastguard Worker     data = buffer.detachAsData();
328*c8dee2aaSAndroid Build Coastguard Worker     #endif
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     int channels = yuv ? 3 : 1;
331*c8dee2aaSAndroid Build Coastguard Worker     SkPDFUnion colorSpace = yuv ? SkPDFUnion::Name("DeviceRGB") : SkPDFUnion::Name("DeviceGray");
332*c8dee2aaSAndroid Build Coastguard Worker 
333*c8dee2aaSAndroid Build Coastguard Worker     if (sk_sp<SkData> encodedIccProfileData = encodedInfo.profileData();
334*c8dee2aaSAndroid Build Coastguard Worker         encodedIccProfileData && !icc_channel_mismatch(encodedInfo.profile(), channels))
335*c8dee2aaSAndroid Build Coastguard Worker     {
336*c8dee2aaSAndroid Build Coastguard Worker         colorSpace = write_icc_profile(doc, std::move(encodedIccProfileData), channels);
337*c8dee2aaSAndroid Build Coastguard Worker     } else if (const skcms_ICCProfile* codecIccProfile = codec->getICCProfile();
338*c8dee2aaSAndroid Build Coastguard Worker                codecIccProfile && !icc_channel_mismatch(codecIccProfile, channels))
339*c8dee2aaSAndroid Build Coastguard Worker     {
340*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkData> codecIccData = SkWriteICCProfile(codecIccProfile, "");
341*c8dee2aaSAndroid Build Coastguard Worker         colorSpace = write_icc_profile(doc, std::move(codecIccData), channels);
342*c8dee2aaSAndroid Build Coastguard Worker     } else if (imageColorSpace) {
343*c8dee2aaSAndroid Build Coastguard Worker         skcms_ICCProfile imageIccProfile;
344*c8dee2aaSAndroid Build Coastguard Worker         imageColorSpace->toProfile(&imageIccProfile);
345*c8dee2aaSAndroid Build Coastguard Worker         if (!icc_channel_mismatch(&imageIccProfile, channels)) {
346*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<SkData> imageIccData = SkWriteICCProfile(&imageIccProfile, "");
347*c8dee2aaSAndroid Build Coastguard Worker             colorSpace = write_icc_profile(doc, std::move(imageIccData), channels);
348*c8dee2aaSAndroid Build Coastguard Worker         }
349*c8dee2aaSAndroid Build Coastguard Worker     }
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker     emit_image_stream(doc, ref,
352*c8dee2aaSAndroid Build Coastguard Worker                       [&data](SkWStream* dst) { dst->write(data->data(), data->size()); },
353*c8dee2aaSAndroid Build Coastguard Worker                       jpegSize, std::move(colorSpace),
354*c8dee2aaSAndroid Build Coastguard Worker                       SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT);
355*c8dee2aaSAndroid Build Coastguard Worker     return true;
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker 
to_pixels(const SkImage * image)358*c8dee2aaSAndroid Build Coastguard Worker SkBitmap to_pixels(const SkImage* image) {
359*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
360*c8dee2aaSAndroid Build Coastguard Worker     int w = image->width(),
361*c8dee2aaSAndroid Build Coastguard Worker         h = image->height();
362*c8dee2aaSAndroid Build Coastguard Worker     switch (image->colorType()) {
363*c8dee2aaSAndroid Build Coastguard Worker         case kAlpha_8_SkColorType:
364*c8dee2aaSAndroid Build Coastguard Worker             bm.allocPixels(SkImageInfo::MakeA8(w, h));
365*c8dee2aaSAndroid Build Coastguard Worker             break;
366*c8dee2aaSAndroid Build Coastguard Worker         case kGray_8_SkColorType:
367*c8dee2aaSAndroid Build Coastguard Worker             bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
368*c8dee2aaSAndroid Build Coastguard Worker             break;
369*c8dee2aaSAndroid Build Coastguard Worker         default: {
370*c8dee2aaSAndroid Build Coastguard Worker             // TODO: makeColorSpace(sRGB) or actually tag the images
371*c8dee2aaSAndroid Build Coastguard Worker             SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
372*c8dee2aaSAndroid Build Coastguard Worker             bm.allocPixels(
373*c8dee2aaSAndroid Build Coastguard Worker                 SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at, image->refColorSpace()));
374*c8dee2aaSAndroid Build Coastguard Worker         }
375*c8dee2aaSAndroid Build Coastguard Worker     }
376*c8dee2aaSAndroid Build Coastguard Worker     // TODO: support GPU images in PDFs
377*c8dee2aaSAndroid Build Coastguard Worker     if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) {
378*c8dee2aaSAndroid Build Coastguard Worker         bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
379*c8dee2aaSAndroid Build Coastguard Worker     }
380*c8dee2aaSAndroid Build Coastguard Worker     return bm;
381*c8dee2aaSAndroid Build Coastguard Worker }
382*c8dee2aaSAndroid Build Coastguard Worker 
serialize_image(const SkImage * img,int encodingQuality,SkPDFDocument * doc,SkPDFIndirectReference ref)383*c8dee2aaSAndroid Build Coastguard Worker void serialize_image(const SkImage* img,
384*c8dee2aaSAndroid Build Coastguard Worker                      int encodingQuality,
385*c8dee2aaSAndroid Build Coastguard Worker                      SkPDFDocument* doc,
386*c8dee2aaSAndroid Build Coastguard Worker                      SkPDFIndirectReference ref) {
387*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(img);
388*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(doc);
389*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(encodingQuality >= 0);
390*c8dee2aaSAndroid Build Coastguard Worker     SkISize dimensions = img->dimensions();
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker     if (sk_sp<SkData> data = img->refEncodedData()) {
393*c8dee2aaSAndroid Build Coastguard Worker         if (do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) {
394*c8dee2aaSAndroid Build Coastguard Worker             return;
395*c8dee2aaSAndroid Build Coastguard Worker         }
396*c8dee2aaSAndroid Build Coastguard Worker     }
397*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm = to_pixels(img);
398*c8dee2aaSAndroid Build Coastguard Worker     const SkPixmap& pm = bm.pixmap();
399*c8dee2aaSAndroid Build Coastguard Worker     bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
400*c8dee2aaSAndroid Build Coastguard Worker     if (encodingQuality <= 100 && isOpaque) {
401*c8dee2aaSAndroid Build Coastguard Worker         SkJpegEncoder::Options jOpts;
402*c8dee2aaSAndroid Build Coastguard Worker         jOpts.fQuality = encodingQuality;
403*c8dee2aaSAndroid Build Coastguard Worker         SkDynamicMemoryWStream stream;
404*c8dee2aaSAndroid Build Coastguard Worker         if (SkJpegEncoder::Encode(&stream, pm, jOpts)) {
405*c8dee2aaSAndroid Build Coastguard Worker             if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) {
406*c8dee2aaSAndroid Build Coastguard Worker                 return;
407*c8dee2aaSAndroid Build Coastguard Worker             }
408*c8dee2aaSAndroid Build Coastguard Worker         }
409*c8dee2aaSAndroid Build Coastguard Worker     }
410*c8dee2aaSAndroid Build Coastguard Worker     do_deflated_image(pm, doc, isOpaque, ref);
411*c8dee2aaSAndroid Build Coastguard Worker }
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker } // namespace
414*c8dee2aaSAndroid Build Coastguard Worker 
SkPDFSerializeImage(const SkImage * img,SkPDFDocument * doc,int encodingQuality)415*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
416*c8dee2aaSAndroid Build Coastguard Worker                                            SkPDFDocument* doc,
417*c8dee2aaSAndroid Build Coastguard Worker                                            int encodingQuality) {
418*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(img);
419*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(doc);
420*c8dee2aaSAndroid Build Coastguard Worker     SkPDFIndirectReference ref = doc->reserveRef();
421*c8dee2aaSAndroid Build Coastguard Worker     if (SkExecutor* executor = doc->executor()) {
422*c8dee2aaSAndroid Build Coastguard Worker         SkRef(img);
423*c8dee2aaSAndroid Build Coastguard Worker         doc->incrementJobCount();
424*c8dee2aaSAndroid Build Coastguard Worker         executor->add([img, encodingQuality, doc, ref]() {
425*c8dee2aaSAndroid Build Coastguard Worker             serialize_image(img, encodingQuality, doc, ref);
426*c8dee2aaSAndroid Build Coastguard Worker             SkSafeUnref(img);
427*c8dee2aaSAndroid Build Coastguard Worker             doc->signalJobComplete();
428*c8dee2aaSAndroid Build Coastguard Worker         });
429*c8dee2aaSAndroid Build Coastguard Worker         return ref;
430*c8dee2aaSAndroid Build Coastguard Worker     }
431*c8dee2aaSAndroid Build Coastguard Worker     serialize_image(img, encodingQuality, doc, ref);
432*c8dee2aaSAndroid Build Coastguard Worker     return ref;
433*c8dee2aaSAndroid Build Coastguard Worker }
434