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