Line data Source code
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 "SkColorPriv.h"
9 : #include "SkData.h"
10 : #include "SkDeflate.h"
11 : #include "SkImage_Base.h"
12 : #include "SkJpegInfo.h"
13 : #include "SkPDFBitmap.h"
14 : #include "SkPDFCanon.h"
15 : #include "SkPDFTypes.h"
16 : #include "SkStream.h"
17 : #include "SkUnPreMultiply.h"
18 :
19 0 : void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) {
20 0 : SkColorSpace* legacyColorSpace = nullptr;
21 0 : if(as_IB(image)->getROPixels(dst, legacyColorSpace)
22 0 : && dst->dimensions() == image->dimensions()) {
23 0 : if (dst->colorType() != kIndex_8_SkColorType) {
24 0 : return;
25 : }
26 : // We must check to see if the bitmap has a color table.
27 0 : SkAutoLockPixels autoLockPixels(*dst);
28 0 : if (!dst->getColorTable()) {
29 : // We can't use an indexed bitmap with no colortable.
30 0 : dst->reset();
31 : } else {
32 0 : return;
33 : }
34 : }
35 : // no pixels or wrong size: fill with zeros.
36 0 : dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()));
37 : }
38 :
39 0 : bool image_compute_is_opaque(const SkImage* image) {
40 0 : if (image->isOpaque()) {
41 0 : return true;
42 : }
43 : // keep output PDF small at cost of possible resource use.
44 0 : SkBitmap bm;
45 0 : image_get_ro_pixels(image, &bm);
46 0 : return SkBitmap::ComputeIsOpaque(bm);
47 : }
48 :
49 : ////////////////////////////////////////////////////////////////////////////////
50 :
51 0 : static void pdf_stream_begin(SkWStream* stream) {
52 : static const char streamBegin[] = " stream\n";
53 0 : stream->write(streamBegin, strlen(streamBegin));
54 0 : }
55 :
56 0 : static void pdf_stream_end(SkWStream* stream) {
57 : static const char streamEnd[] = "\nendstream";
58 0 : stream->write(streamEnd, strlen(streamEnd));
59 0 : }
60 :
61 : ////////////////////////////////////////////////////////////////////////////////
62 :
63 : // write a single byte to a stream n times.
64 0 : static void fill_stream(SkWStream* out, char value, size_t n) {
65 : char buffer[4096];
66 0 : memset(buffer, value, sizeof(buffer));
67 0 : for (size_t i = 0; i < n / sizeof(buffer); ++i) {
68 0 : out->write(buffer, sizeof(buffer));
69 : }
70 0 : out->write(buffer, n % sizeof(buffer));
71 0 : }
72 :
73 : // TODO(reed@): Decide if these five functions belong in SkColorPriv.h
74 0 : static bool SkIsBGRA(SkColorType ct) {
75 0 : SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
76 0 : return kBGRA_8888_SkColorType == ct;
77 : }
78 :
79 : // Interpret value as the given 4-byte SkColorType (BGRA_8888 or
80 : // RGBA_8888) and return the appropriate component. Each component
81 : // should be interpreted according to the associated SkAlphaType and
82 : // SkColorProfileType.
83 0 : static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
84 0 : return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
85 : }
86 0 : static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
87 0 : return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
88 : }
89 0 : static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
90 0 : return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
91 : }
92 0 : static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
93 0 : return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
94 : }
95 :
96 : // unpremultiply and extract R, G, B components.
97 0 : static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
98 0 : uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
99 0 : rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
100 0 : rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
101 0 : rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
102 0 : }
103 :
104 : /* It is necessary to average the color component of transparent
105 : pixels with their surrounding neighbors since the PDF renderer may
106 : separately re-sample the alpha and color channels when the image is
107 : not displayed at its native resolution. Since an alpha of zero
108 : gives no information about the color component, the pathological
109 : case is a white image with sharp transparency bounds - the color
110 : channel goes to black, and the should-be-transparent pixels are
111 : rendered as grey because of the separate soft mask and color
112 : resizing. e.g.: gm/bitmappremul.cpp */
113 0 : static void get_neighbor_avg_color(const SkBitmap& bm,
114 : int xOrig,
115 : int yOrig,
116 : uint8_t rgb[3],
117 : SkColorType ct) {
118 0 : unsigned a = 0, r = 0, g = 0, b = 0;
119 : // Clamp the range to the edge of the bitmap.
120 0 : int ymin = SkTMax(0, yOrig - 1);
121 0 : int ymax = SkTMin(yOrig + 1, bm.height() - 1);
122 0 : int xmin = SkTMax(0, xOrig - 1);
123 0 : int xmax = SkTMin(xOrig + 1, bm.width() - 1);
124 0 : for (int y = ymin; y <= ymax; ++y) {
125 0 : uint32_t* scanline = bm.getAddr32(0, y);
126 0 : for (int x = xmin; x <= xmax; ++x) {
127 0 : uint32_t color = scanline[x];
128 0 : a += SkGetA32Component(color, ct);
129 0 : r += SkGetR32Component(color, ct);
130 0 : g += SkGetG32Component(color, ct);
131 0 : b += SkGetB32Component(color, ct);
132 : }
133 : }
134 0 : if (a > 0) {
135 0 : rgb[0] = SkToU8(255 * r / a);
136 0 : rgb[1] = SkToU8(255 * g / a);
137 0 : rgb[2] = SkToU8(255 * b / a);
138 : } else {
139 0 : rgb[0] = rgb[1] = rgb[2] = 0;
140 : }
141 0 : }
142 :
143 0 : static size_t pixel_count(const SkBitmap& bm) {
144 0 : return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
145 : }
146 :
147 0 : static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
148 0 : if (input.colorType() != kARGB_4444_SkColorType) {
149 0 : return input;
150 : }
151 : // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
152 0 : SkAssertResult(input.copyTo(copy, kN32_SkColorType));
153 0 : copy->setImmutable();
154 0 : return *copy;
155 : }
156 :
157 0 : static size_t pdf_color_component_count(SkColorType ct) {
158 0 : switch (ct) {
159 : case kRGB_565_SkColorType:
160 : case kARGB_4444_SkColorType:
161 : case kRGBA_8888_SkColorType:
162 : case kBGRA_8888_SkColorType:
163 0 : return 3;
164 : case kAlpha_8_SkColorType:
165 : case kIndex_8_SkColorType:
166 : case kGray_8_SkColorType:
167 0 : return 1;
168 : case kUnknown_SkColorType:
169 : default:
170 0 : SkDEBUGFAIL("unexpected color type");
171 0 : return 0;
172 : }
173 : }
174 :
175 0 : static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
176 0 : if (!bitmap.getPixels()) {
177 0 : size_t size = pixel_count(bitmap) *
178 0 : pdf_color_component_count(bitmap.colorType());
179 0 : fill_stream(out, '\x00', size);
180 0 : return;
181 : }
182 0 : SkBitmap copy;
183 0 : const SkBitmap& bm = not4444(bitmap, ©);
184 0 : SkAutoLockPixels autoLockPixels(bm);
185 0 : SkColorType colorType = bm.colorType();
186 0 : SkAlphaType alphaType = bm.alphaType();
187 0 : switch (colorType) {
188 : case kRGBA_8888_SkColorType:
189 : case kBGRA_8888_SkColorType: {
190 0 : SkASSERT(3 == pdf_color_component_count(colorType));
191 0 : SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
192 0 : for (int y = 0; y < bm.height(); ++y) {
193 0 : const uint32_t* src = bm.getAddr32(0, y);
194 0 : uint8_t* dst = scanline.get();
195 0 : for (int x = 0; x < bm.width(); ++x) {
196 0 : if (alphaType == kPremul_SkAlphaType) {
197 0 : uint32_t color = *src++;
198 0 : U8CPU alpha = SkGetA32Component(color, colorType);
199 0 : if (alpha != SK_AlphaTRANSPARENT) {
200 0 : pmcolor_to_rgb24(color, dst, colorType);
201 : } else {
202 0 : get_neighbor_avg_color(bm, x, y, dst, colorType);
203 : }
204 0 : dst += 3;
205 : } else {
206 0 : uint32_t color = *src++;
207 0 : *dst++ = SkGetR32Component(color, colorType);
208 0 : *dst++ = SkGetG32Component(color, colorType);
209 0 : *dst++ = SkGetB32Component(color, colorType);
210 : }
211 : }
212 0 : out->write(scanline.get(), 3 * bm.width());
213 : }
214 0 : return;
215 : }
216 : case kRGB_565_SkColorType: {
217 0 : SkASSERT(3 == pdf_color_component_count(colorType));
218 0 : SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
219 0 : for (int y = 0; y < bm.height(); ++y) {
220 0 : const uint16_t* src = bm.getAddr16(0, y);
221 0 : uint8_t* dst = scanline.get();
222 0 : for (int x = 0; x < bm.width(); ++x) {
223 0 : U16CPU color565 = *src++;
224 0 : *dst++ = SkPacked16ToR32(color565);
225 0 : *dst++ = SkPacked16ToG32(color565);
226 0 : *dst++ = SkPacked16ToB32(color565);
227 : }
228 0 : out->write(scanline.get(), 3 * bm.width());
229 : }
230 0 : return;
231 : }
232 : case kAlpha_8_SkColorType:
233 0 : SkASSERT(1 == pdf_color_component_count(colorType));
234 0 : fill_stream(out, '\x00', pixel_count(bm));
235 0 : return;
236 : case kGray_8_SkColorType:
237 : case kIndex_8_SkColorType:
238 0 : SkASSERT(1 == pdf_color_component_count(colorType));
239 : // these two formats need no transformation to serialize.
240 0 : for (int y = 0; y < bm.height(); ++y) {
241 0 : out->write(bm.getAddr8(0, y), bm.width());
242 : }
243 0 : return;
244 : case kUnknown_SkColorType:
245 : case kARGB_4444_SkColorType:
246 : default:
247 0 : SkDEBUGFAIL("unexpected color type");
248 : }
249 : }
250 :
251 : ////////////////////////////////////////////////////////////////////////////////
252 :
253 0 : static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
254 0 : if (!bitmap.getPixels()) {
255 0 : fill_stream(out, '\xFF', pixel_count(bitmap));
256 0 : return;
257 : }
258 0 : SkBitmap copy;
259 0 : const SkBitmap& bm = not4444(bitmap, ©);
260 0 : SkAutoLockPixels autoLockPixels(bm);
261 0 : SkColorType colorType = bm.colorType();
262 0 : switch (colorType) {
263 : case kRGBA_8888_SkColorType:
264 : case kBGRA_8888_SkColorType: {
265 0 : SkAutoTMalloc<uint8_t> scanline(bm.width());
266 0 : for (int y = 0; y < bm.height(); ++y) {
267 0 : uint8_t* dst = scanline.get();
268 0 : const SkPMColor* src = bm.getAddr32(0, y);
269 0 : for (int x = 0; x < bm.width(); ++x) {
270 0 : *dst++ = SkGetA32Component(*src++, colorType);
271 : }
272 0 : out->write(scanline.get(), bm.width());
273 : }
274 0 : return;
275 : }
276 : case kAlpha_8_SkColorType:
277 0 : for (int y = 0; y < bm.height(); ++y) {
278 0 : out->write(bm.getAddr8(0, y), bm.width());
279 : }
280 0 : return;
281 : case kIndex_8_SkColorType: {
282 0 : SkColorTable* ct = bm.getColorTable();
283 0 : SkASSERT(ct);
284 0 : SkAutoTMalloc<uint8_t> scanline(bm.width());
285 0 : for (int y = 0; y < bm.height(); ++y) {
286 0 : uint8_t* dst = scanline.get();
287 0 : const uint8_t* src = bm.getAddr8(0, y);
288 0 : for (int x = 0; x < bm.width(); ++x) {
289 0 : *dst++ = SkGetPackedA32((*ct)[*src++]);
290 : }
291 0 : out->write(scanline.get(), bm.width());
292 : }
293 0 : return;
294 : }
295 : case kRGB_565_SkColorType:
296 : case kGray_8_SkColorType:
297 0 : SkDEBUGFAIL("color type has no alpha");
298 0 : return;
299 : case kARGB_4444_SkColorType:
300 0 : SkDEBUGFAIL("4444 color type should have been converted to N32");
301 0 : return;
302 : case kUnknown_SkColorType:
303 : default:
304 0 : SkDEBUGFAIL("unexpected color type");
305 : }
306 : }
307 :
308 0 : static sk_sp<SkPDFArray> make_indexed_color_space(
309 : const SkColorTable* table,
310 : SkAlphaType alphaType) {
311 0 : auto result = sk_make_sp<SkPDFArray>();
312 0 : result->reserve(4);
313 0 : result->appendName("Indexed");
314 0 : result->appendName("DeviceRGB");
315 0 : SkASSERT(table);
316 0 : if (table->count() < 1) {
317 0 : result->appendInt(0);
318 0 : char shortTableArray[3] = {0, 0, 0};
319 0 : SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
320 0 : result->appendString(tableString);
321 0 : return result;
322 : }
323 0 : result->appendInt(table->count() - 1); // maximum color index.
324 :
325 : // Potentially, this could be represented in fewer bytes with a stream.
326 : // Max size as a string is 1.5k.
327 : char tableArray[256 * 3];
328 0 : SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
329 0 : uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
330 0 : const SkPMColor* colors = table->readColors();
331 0 : for (int i = 0; i < table->count(); i++) {
332 0 : if (alphaType == kPremul_SkAlphaType) {
333 0 : pmcolor_to_rgb24(colors[i], tablePtr, kN32_SkColorType);
334 0 : tablePtr += 3;
335 : } else {
336 0 : *tablePtr++ = SkGetR32Component(colors[i], kN32_SkColorType);
337 0 : *tablePtr++ = SkGetG32Component(colors[i], kN32_SkColorType);
338 0 : *tablePtr++ = SkGetB32Component(colors[i], kN32_SkColorType);
339 : }
340 : }
341 0 : SkString tableString(tableArray, 3 * table->count());
342 0 : result->appendString(tableString);
343 0 : return result;
344 : }
345 :
346 0 : static void emit_image_xobject(SkWStream* stream,
347 : const SkImage* image,
348 : bool alpha,
349 : const sk_sp<SkPDFObject>& smask,
350 : const SkPDFObjNumMap& objNumMap) {
351 0 : SkBitmap bitmap;
352 0 : image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
353 0 : SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
354 :
355 : // Write to a temporary buffer to get the compressed length.
356 0 : SkDynamicMemoryWStream buffer;
357 0 : SkDeflateWStream deflateWStream(&buffer);
358 0 : if (alpha) {
359 0 : bitmap_alpha_to_a8(bitmap, &deflateWStream);
360 : } else {
361 0 : bitmap_to_pdf_pixels(bitmap, &deflateWStream);
362 : }
363 0 : deflateWStream.finalize(); // call before detachAsStream().
364 0 : std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
365 :
366 0 : SkPDFDict pdfDict("XObject");
367 0 : pdfDict.insertName("Subtype", "Image");
368 0 : pdfDict.insertInt("Width", bitmap.width());
369 0 : pdfDict.insertInt("Height", bitmap.height());
370 0 : if (alpha) {
371 0 : pdfDict.insertName("ColorSpace", "DeviceGray");
372 0 : } else if (bitmap.colorType() == kIndex_8_SkColorType) {
373 0 : SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
374 0 : pdfDict.insertObject("ColorSpace",
375 0 : make_indexed_color_space(bitmap.getColorTable(),
376 0 : bitmap.alphaType()));
377 0 : } else if (1 == pdf_color_component_count(bitmap.colorType())) {
378 0 : pdfDict.insertName("ColorSpace", "DeviceGray");
379 : } else {
380 0 : pdfDict.insertName("ColorSpace", "DeviceRGB");
381 : }
382 0 : if (smask) {
383 0 : pdfDict.insertObjRef("SMask", smask);
384 : }
385 0 : pdfDict.insertInt("BitsPerComponent", 8);
386 0 : pdfDict.insertName("Filter", "FlateDecode");
387 0 : pdfDict.insertInt("Length", asset->getLength());
388 0 : pdfDict.emitObject(stream, objNumMap);
389 :
390 0 : pdf_stream_begin(stream);
391 0 : stream->writeStream(asset.get(), asset->getLength());
392 0 : pdf_stream_end(stream);
393 0 : }
394 :
395 : ////////////////////////////////////////////////////////////////////////////////
396 :
397 : namespace {
398 : // This SkPDFObject only outputs the alpha layer of the given bitmap.
399 0 : class PDFAlphaBitmap final : public SkPDFObject {
400 : public:
401 0 : PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
402 0 : void emitObject(SkWStream* stream,
403 : const SkPDFObjNumMap& objNumMap) const override {
404 0 : SkASSERT(fImage);
405 0 : emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
406 0 : }
407 0 : void drop() override { fImage = nullptr; }
408 :
409 : private:
410 : sk_sp<SkImage> fImage;
411 : };
412 :
413 : } // namespace
414 :
415 : ////////////////////////////////////////////////////////////////////////////////
416 :
417 : namespace {
418 0 : class PDFDefaultBitmap final : public SkPDFObject {
419 : public:
420 0 : void emitObject(SkWStream* stream,
421 : const SkPDFObjNumMap& objNumMap) const override {
422 0 : SkASSERT(fImage);
423 0 : emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
424 0 : }
425 0 : void addResources(SkPDFObjNumMap* catalog) const override {
426 0 : catalog->addObjectRecursively(fSMask.get());
427 0 : }
428 0 : void drop() override { fImage = nullptr; fSMask = nullptr; }
429 0 : PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
430 0 : : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
431 :
432 : private:
433 : sk_sp<SkImage> fImage;
434 : sk_sp<SkPDFObject> fSMask;
435 : };
436 : } // namespace
437 :
438 : ////////////////////////////////////////////////////////////////////////////////
439 :
440 : namespace {
441 : /**
442 : * This PDFObject assumes that its constructor was handed YUV or
443 : * Grayscale JFIF Jpeg-encoded data that can be directly embedded
444 : * into a PDF.
445 : */
446 0 : class PDFJpegBitmap final : public SkPDFObject {
447 : public:
448 : SkISize fSize;
449 : sk_sp<SkData> fData;
450 : bool fIsYUV;
451 0 : PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
452 0 : : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
453 : void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
454 0 : void drop() override { fData = nullptr; }
455 : };
456 :
457 0 : void PDFJpegBitmap::emitObject(SkWStream* stream,
458 : const SkPDFObjNumMap& objNumMap) const {
459 0 : SkASSERT(fData);
460 0 : SkPDFDict pdfDict("XObject");
461 0 : pdfDict.insertName("Subtype", "Image");
462 0 : pdfDict.insertInt("Width", fSize.width());
463 0 : pdfDict.insertInt("Height", fSize.height());
464 0 : if (fIsYUV) {
465 0 : pdfDict.insertName("ColorSpace", "DeviceRGB");
466 : } else {
467 0 : pdfDict.insertName("ColorSpace", "DeviceGray");
468 : }
469 0 : pdfDict.insertInt("BitsPerComponent", 8);
470 0 : pdfDict.insertName("Filter", "DCTDecode");
471 0 : pdfDict.insertInt("ColorTransform", 0);
472 0 : pdfDict.insertInt("Length", SkToInt(fData->size()));
473 0 : pdfDict.emitObject(stream, objNumMap);
474 0 : pdf_stream_begin(stream);
475 0 : stream->write(fData->data(), fData->size());
476 0 : pdf_stream_end(stream);
477 0 : }
478 : } // namespace
479 :
480 : ////////////////////////////////////////////////////////////////////////////////
481 :
482 0 : sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
483 : SkPixelSerializer* pixelSerializer) {
484 0 : SkASSERT(image);
485 0 : sk_sp<SkData> data(image->refEncoded());
486 : SkJFIFInfo info;
487 0 : if (data && SkIsJFIF(data.get(), &info) &&
488 0 : (!pixelSerializer ||
489 0 : pixelSerializer->useEncodedData(data->data(), data->size()))) {
490 : // If there is a SkPixelSerializer, give it a chance to
491 : // re-encode the JPEG with more compression by returning false
492 : // from useEncodedData.
493 0 : bool yuv = info.fType == SkJFIFInfo::kYCbCr;
494 0 : if (info.fSize == image->dimensions()) { // Sanity check.
495 : // hold on to data, not image.
496 : #ifdef SK_PDF_IMAGE_STATS
497 : gJpegImageObjects.fetch_add(1);
498 : #endif
499 0 : return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
500 : }
501 : }
502 :
503 0 : if (pixelSerializer) {
504 0 : SkBitmap bm;
505 0 : SkAutoPixmapUnlock apu;
506 0 : SkColorSpace* legacyColorSpace = nullptr;
507 0 : if (as_IB(image.get())->getROPixels(&bm, legacyColorSpace) &&
508 0 : bm.requestLock(&apu)) {
509 0 : data.reset(pixelSerializer->encode(apu.pixmap()));
510 0 : if (data && SkIsJFIF(data.get(), &info)) {
511 0 : bool yuv = info.fType == SkJFIFInfo::kYCbCr;
512 0 : if (info.fSize == image->dimensions()) { // Sanity check.
513 0 : return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
514 : }
515 : }
516 : }
517 : }
518 :
519 0 : sk_sp<SkPDFObject> smask;
520 0 : if (!image_compute_is_opaque(image.get())) {
521 0 : smask = sk_make_sp<PDFAlphaBitmap>(image);
522 : }
523 : #ifdef SK_PDF_IMAGE_STATS
524 : gRegularImageObjects.fetch_add(1);
525 : #endif
526 0 : return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
527 : }
|