xref: /third_party/skia/src/pdf/SkPDFBitmap.cpp (revision cb93a386)
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/core/SkBitmap.h"
11#include "include/core/SkData.h"
12#include "include/core/SkExecutor.h"
13#include "include/core/SkImage.h"
14#include "include/core/SkStream.h"
15#include "include/private/SkColorData.h"
16#include "include/private/SkImageInfoPriv.h"
17#include "include/private/SkTo.h"
18#include "src/pdf/SkDeflate.h"
19#include "src/pdf/SkJpegInfo.h"
20#include "src/pdf/SkPDFDocumentPriv.h"
21#include "src/pdf/SkPDFTypes.h"
22#include "src/pdf/SkPDFUtils.h"
23
24////////////////////////////////////////////////////////////////////////////////
25
26// write a single byte to a stream n times.
27static void fill_stream(SkWStream* out, char value, size_t n) {
28    char buffer[4096];
29    memset(buffer, value, sizeof(buffer));
30    for (size_t i = 0; i < n / sizeof(buffer); ++i) {
31        out->write(buffer, sizeof(buffer));
32    }
33    out->write(buffer, n % sizeof(buffer));
34}
35
36/* It is necessary to average the color component of transparent
37   pixels with their surrounding neighbors since the PDF renderer may
38   separately re-sample the alpha and color channels when the image is
39   not displayed at its native resolution. Since an alpha of zero
40   gives no information about the color component, the pathological
41   case is a white image with sharp transparency bounds - the color
42   channel goes to black, and the should-be-transparent pixels are
43   rendered as grey because of the separate soft mask and color
44   resizing. e.g.: gm/bitmappremul.cpp */
45static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
46    SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
47    unsigned r = 0, g = 0, b = 0, n = 0;
48    // Clamp the range to the edge of the bitmap.
49    int ymin = std::max(0, yOrig - 1);
50    int ymax = std::min(yOrig + 1, bm.height() - 1);
51    int xmin = std::max(0, xOrig - 1);
52    int xmax = std::min(xOrig + 1, bm.width() - 1);
53    for (int y = ymin; y <= ymax; ++y) {
54        const SkColor* scanline = bm.addr32(0, y);
55        for (int x = xmin; x <= xmax; ++x) {
56            SkColor color = scanline[x];
57            if (color != SK_ColorTRANSPARENT) {
58                r += SkColorGetR(color);
59                g += SkColorGetG(color);
60                b += SkColorGetB(color);
61                n++;
62            }
63        }
64    }
65    return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n))
66                 : SK_ColorTRANSPARENT;
67}
68
69template <typename T>
70static void emit_image_stream(SkPDFDocument* doc,
71                              SkPDFIndirectReference ref,
72                              T writeStream,
73                              SkISize size,
74                              const char* colorSpace,
75                              SkPDFIndirectReference sMask,
76                              int length,
77                              bool isJpeg) {
78    SkPDFDict pdfDict("XObject");
79    pdfDict.insertName("Subtype", "Image");
80    pdfDict.insertInt("Width", size.width());
81    pdfDict.insertInt("Height", size.height());
82    pdfDict.insertName("ColorSpace", colorSpace);
83    if (sMask) {
84        pdfDict.insertRef("SMask", sMask);
85    }
86    pdfDict.insertInt("BitsPerComponent", 8);
87    #ifdef SK_PDF_BASE85_BINARY
88    auto filters = SkPDFMakeArray();
89    filters->appendName("ASCII85Decode");
90    filters->appendName(isJpeg ? "DCTDecode" : "FlateDecode");
91    pdfDict.insertObject("Filter", std::move(filters));
92    #else
93    pdfDict.insertName("Filter", isJpeg ? "DCTDecode" : "FlateDecode");
94    #endif
95    if (isJpeg) {
96        pdfDict.insertInt("ColorTransform", 0);
97    }
98    pdfDict.insertInt("Length", length);
99    doc->emitStream(pdfDict, std::move(writeStream), ref);
100}
101
102static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) {
103    SkDynamicMemoryWStream buffer;
104    SkDeflateWStream deflateWStream(&buffer);
105    if (kAlpha_8_SkColorType == pm.colorType()) {
106        SkASSERT(pm.rowBytes() == (size_t)pm.width());
107        buffer.write(pm.addr8(), pm.width() * pm.height());
108    } else {
109        SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
110        SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
111        SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
112        const uint32_t* ptr = pm.addr32();
113        const uint32_t* stop = ptr + pm.height() * pm.width();
114
115        uint8_t byteBuffer[4092];
116        uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
117        uint8_t* dst = byteBuffer;
118        while (ptr != stop) {
119            *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
120            if (dst == bufferStop) {
121                deflateWStream.write(byteBuffer, sizeof(byteBuffer));
122                dst = byteBuffer;
123            }
124        }
125        deflateWStream.write(byteBuffer, dst - byteBuffer);
126    }
127    deflateWStream.finalize();
128
129    #ifdef SK_PDF_BASE85_BINARY
130    SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
131    #endif
132    int length = SkToInt(buffer.bytesWritten());
133    emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
134                      pm.info().dimensions(), "DeviceGray", SkPDFIndirectReference(),
135                      length, false);
136}
137
138static void do_deflated_image(const SkPixmap& pm,
139                              SkPDFDocument* doc,
140                              bool isOpaque,
141                              SkPDFIndirectReference ref) {
142    SkPDFIndirectReference sMask;
143    if (!isOpaque) {
144        sMask = doc->reserveRef();
145    }
146    SkDynamicMemoryWStream buffer;
147    SkDeflateWStream deflateWStream(&buffer);
148    const char* colorSpace = "DeviceGray";
149    switch (pm.colorType()) {
150        case kAlpha_8_SkColorType:
151            fill_stream(&deflateWStream, '\x00', pm.width() * pm.height());
152            break;
153        case kGray_8_SkColorType:
154            SkASSERT(sMask.fValue = -1);
155            SkASSERT(pm.rowBytes() == (size_t)pm.width());
156            deflateWStream.write(pm.addr8(), pm.width() * pm.height());
157            break;
158        default:
159            colorSpace = "DeviceRGB";
160            SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
161            SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
162            SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
163            uint8_t byteBuffer[3072];
164            static_assert(SK_ARRAY_COUNT(byteBuffer) % 3 == 0, "");
165            uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
166            uint8_t* dst = byteBuffer;
167            for (int y = 0; y < pm.height(); ++y) {
168                const SkColor* src = pm.addr32(0, y);
169                for (int x = 0; x < pm.width(); ++x) {
170                    SkColor color = *src++;
171                    if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
172                        color = get_neighbor_avg_color(pm, x, y);
173                    }
174                    *dst++ = SkColorGetR(color);
175                    *dst++ = SkColorGetG(color);
176                    *dst++ = SkColorGetB(color);
177                    if (dst == bufferStop) {
178                        deflateWStream.write(byteBuffer, sizeof(byteBuffer));
179                        dst = byteBuffer;
180                    }
181                }
182            }
183            deflateWStream.write(byteBuffer, dst - byteBuffer);
184    }
185    deflateWStream.finalize();
186    #ifdef SK_PDF_BASE85_BINARY
187    SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
188    #endif
189    int length = SkToInt(buffer.bytesWritten());
190    emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
191                      pm.info().dimensions(), colorSpace, sMask, length, false);
192    if (!isOpaque) {
193        do_deflated_alpha(pm, doc, sMask);
194    }
195}
196
197static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size,
198                    SkPDFIndirectReference ref) {
199    SkISize jpegSize;
200    SkEncodedInfo::Color jpegColorType;
201    SkEncodedOrigin exifOrientation;
202    if (!SkGetJpegInfo(data->data(), data->size(), &jpegSize,
203                       &jpegColorType, &exifOrientation)) {
204        return false;
205    }
206    bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
207    bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
208    if (jpegSize != size  // Safety check.
209            || !goodColorType
210            || kTopLeft_SkEncodedOrigin != exifOrientation) {
211        return false;
212    }
213    #ifdef SK_PDF_BASE85_BINARY
214    SkDynamicMemoryWStream buffer;
215    SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
216    data = buffer.detachAsData();
217    #endif
218
219    emit_image_stream(doc, ref,
220                      [&data](SkWStream* dst) { dst->write(data->data(), data->size()); },
221                      jpegSize, yuv ? "DeviceRGB" : "DeviceGray",
222                      SkPDFIndirectReference(), SkToInt(data->size()), true);
223    return true;
224}
225
226static SkBitmap to_pixels(const SkImage* image) {
227    SkBitmap bm;
228    int w = image->width(),
229        h = image->height();
230    switch (image->colorType()) {
231        case kAlpha_8_SkColorType:
232            bm.allocPixels(SkImageInfo::MakeA8(w, h));
233            break;
234        case kGray_8_SkColorType:
235            bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
236            break;
237        default: {
238            // TODO: makeColorSpace(sRGB) or actually tag the images
239            SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
240            bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at));
241        }
242    }
243    // TODO: support GPU images in PDFs
244    if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) {
245        bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
246    }
247    return bm;
248}
249
250void serialize_image(const SkImage* img,
251                     int encodingQuality,
252                     SkPDFDocument* doc,
253                     SkPDFIndirectReference ref) {
254    SkASSERT(img);
255    SkASSERT(doc);
256    SkASSERT(encodingQuality >= 0);
257    SkISize dimensions = img->dimensions();
258    if (sk_sp<SkData> data = img->refEncodedData()) {
259        if (do_jpeg(std::move(data), doc, dimensions, ref)) {
260            return;
261        }
262    }
263    SkBitmap bm = to_pixels(img);
264    const SkPixmap& pm = bm.pixmap();
265    bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
266    if (encodingQuality <= 100 && isOpaque) {
267        if (sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality)) {
268            if (do_jpeg(std::move(data), doc, dimensions, ref)) {
269                return;
270            }
271        }
272    }
273    do_deflated_image(pm, doc, isOpaque, ref);
274}
275
276SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
277                                           SkPDFDocument* doc,
278                                           int encodingQuality) {
279    SkASSERT(img);
280    SkASSERT(doc);
281    SkPDFIndirectReference ref = doc->reserveRef();
282    if (SkExecutor* executor = doc->executor()) {
283        SkRef(img);
284        doc->incrementJobCount();
285        executor->add([img, encodingQuality, doc, ref]() {
286            serialize_image(img, encodingQuality, doc, ref);
287            SkSafeUnref(img);
288            doc->signalJobComplete();
289        });
290        return ref;
291    }
292    serialize_image(img, encodingQuality, doc, ref);
293    return ref;
294}
295