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