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