blob: f97eeff484388036eca4cd8ddc6994fc87f61ebc [file] [log] [blame]
halcanary1b5c6042015-02-18 11:29:56 -08001/*
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
Hal Canary4f29c202017-07-18 10:28:31 -04008#include "SkPDFBitmap.h"
9
Cary Clarka4083c92017-09-15 11:59:23 -040010#include "SkColorData.h"
halcanarya8448bc2015-04-17 13:27:24 -070011#include "SkData.h"
halcanaryd9e57152015-08-12 11:24:40 -070012#include "SkDeflate.h"
Hal Canary4f29c202017-07-18 10:28:31 -040013#include "SkImage.h"
halcanary96287f72015-05-07 11:46:59 -070014#include "SkJpegInfo.h"
halcanary1b5c6042015-02-18 11:29:56 -080015#include "SkPDFCanon.h"
martina.kollarovab8d6af12016-06-29 05:12:31 -070016#include "SkPDFTypes.h"
Hal Canary4f29c202017-07-18 10:28:31 -040017#include "SkPDFUtils.h"
halcanary1b5c6042015-02-18 11:29:56 -080018#include "SkStream.h"
19#include "SkUnPreMultiply.h"
20
halcanary7a14b312015-10-01 07:28:13 -070021bool image_compute_is_opaque(const SkImage* image) {
22 if (image->isOpaque()) {
23 return true;
24 }
25 // keep output PDF small at cost of possible resource use.
26 SkBitmap bm;
Hal Canary4f29c202017-07-18 10:28:31 -040027 // if image can not be read, treat as transparent.
28 return SkPDFUtils::ToBitmap(image, &bm) && SkBitmap::ComputeIsOpaque(bm);
halcanary7a14b312015-10-01 07:28:13 -070029}
30
halcanary1b5c6042015-02-18 11:29:56 -080031////////////////////////////////////////////////////////////////////////////////
32
Hal Canaryc172f9b2017-05-27 20:29:44 -040033static const char kStreamBegin[] = " stream\n";
halcanary1b5c6042015-02-18 11:29:56 -080034
Hal Canaryc172f9b2017-05-27 20:29:44 -040035static const char kStreamEnd[] = "\nendstream";
halcanary1b5c6042015-02-18 11:29:56 -080036
halcanarydb0dcc72015-03-20 12:31:52 -070037////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080038
halcanary1b5c6042015-02-18 11:29:56 -080039// write a single byte to a stream n times.
40static void fill_stream(SkWStream* out, char value, size_t n) {
41 char buffer[4096];
42 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070043 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
44 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080045 }
halcanarydb0dcc72015-03-20 12:31:52 -070046 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080047}
48
Cary Clarka4083c92017-09-15 11:59:23 -040049// TODO(reed@): Decide if these five functions belong in SkColorData.h
halcanaryaa4ba902015-11-06 07:27:23 -080050static bool SkIsBGRA(SkColorType ct) {
51 SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
52 return kBGRA_8888_SkColorType == ct;
53}
54
55// Interpret value as the given 4-byte SkColorType (BGRA_8888 or
56// RGBA_8888) and return the appropriate component. Each component
57// should be interpreted according to the associated SkAlphaType and
58// SkColorProfileType.
59static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
60 return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
61}
62static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
63 return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
64}
65static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
66 return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
67}
68static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
69 return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
70}
71
halcanarydb0dcc72015-03-20 12:31:52 -070072// unpremultiply and extract R, G, B components.
halcanaryaa4ba902015-11-06 07:27:23 -080073static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
Hal Canary5b474d32017-05-25 16:21:06 -040074 SkPMColorAssert(color);
halcanaryaa4ba902015-11-06 07:27:23 -080075 uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
76 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
77 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
78 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
halcanarydb0dcc72015-03-20 12:31:52 -070079}
80
81/* It is necessary to average the color component of transparent
82 pixels with their surrounding neighbors since the PDF renderer may
83 separately re-sample the alpha and color channels when the image is
84 not displayed at its native resolution. Since an alpha of zero
85 gives no information about the color component, the pathological
86 case is a white image with sharp transparency bounds - the color
87 channel goes to black, and the should-be-transparent pixels are
88 rendered as grey because of the separate soft mask and color
89 resizing. e.g.: gm/bitmappremul.cpp */
90static void get_neighbor_avg_color(const SkBitmap& bm,
91 int xOrig,
92 int yOrig,
halcanaryaa4ba902015-11-06 07:27:23 -080093 uint8_t rgb[3],
94 SkColorType ct) {
halcanarydb0dcc72015-03-20 12:31:52 -070095 unsigned a = 0, r = 0, g = 0, b = 0;
96 // Clamp the range to the edge of the bitmap.
97 int ymin = SkTMax(0, yOrig - 1);
98 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
99 int xmin = SkTMax(0, xOrig - 1);
100 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
101 for (int y = ymin; y <= ymax; ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800102 uint32_t* scanline = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700103 for (int x = xmin; x <= xmax; ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800104 uint32_t color = scanline[x];
Hal Canary5b474d32017-05-25 16:21:06 -0400105 SkPMColorAssert(color);
halcanaryaa4ba902015-11-06 07:27:23 -0800106 a += SkGetA32Component(color, ct);
107 r += SkGetR32Component(color, ct);
108 g += SkGetG32Component(color, ct);
109 b += SkGetB32Component(color, ct);
halcanary1b5c6042015-02-18 11:29:56 -0800110 }
111 }
halcanarydb0dcc72015-03-20 12:31:52 -0700112 if (a > 0) {
113 rgb[0] = SkToU8(255 * r / a);
114 rgb[1] = SkToU8(255 * g / a);
115 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800116 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700117 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800118 }
119}
120
halcanarydb0dcc72015-03-20 12:31:52 -0700121static size_t pixel_count(const SkBitmap& bm) {
122 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
123}
124
Hal Canary3f467182017-07-20 14:15:25 -0400125static const SkBitmap& supported_colortype(const SkBitmap& input, SkBitmap* copy) {
126 switch (input.colorType()) {
127 case kUnknown_SkColorType:
128 SkDEBUGFAIL("kUnknown_SkColorType");
129 case kAlpha_8_SkColorType:
130 case kRGB_565_SkColorType:
131 case kRGBA_8888_SkColorType:
132 case kBGRA_8888_SkColorType:
133 case kGray_8_SkColorType:
134 return input; // supported
135 default:
136 // if other colortypes are introduced in the future,
137 // they will hit this code.
138 break;
halcanarydb0dcc72015-03-20 12:31:52 -0700139 }
Hal Canary3f467182017-07-20 14:15:25 -0400140 // Fallback for rarely used ARGB_4444 and ARGB_F16: do a wasteful tmp copy.
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400141 copy->allocPixels(input.info().makeColorType(kN32_SkColorType));
142 SkAssertResult(input.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0));
halcanarydb0dcc72015-03-20 12:31:52 -0700143 copy->setImmutable();
144 return *copy;
145}
146
147static size_t pdf_color_component_count(SkColorType ct) {
148 switch (ct) {
Hal Canary3f467182017-07-20 14:15:25 -0400149 case kUnknown_SkColorType:
150 SkDEBUGFAIL("kUnknown_SkColorType");
halcanarydb0dcc72015-03-20 12:31:52 -0700151 case kAlpha_8_SkColorType:
halcanarydb0dcc72015-03-20 12:31:52 -0700152 case kGray_8_SkColorType:
153 return 1;
Hal Canary3f467182017-07-20 14:15:25 -0400154 case kRGB_565_SkColorType:
155 case kRGBA_8888_SkColorType:
156 case kBGRA_8888_SkColorType:
157 default: // converted to N32
158 return 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700159 }
160}
161
162static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
163 if (!bitmap.getPixels()) {
164 size_t size = pixel_count(bitmap) *
165 pdf_color_component_count(bitmap.colorType());
166 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800167 return;
168 }
halcanarydb0dcc72015-03-20 12:31:52 -0700169 SkBitmap copy;
Hal Canary3f467182017-07-20 14:15:25 -0400170 const SkBitmap& bm = supported_colortype(bitmap, &copy);
halcanaryaa4ba902015-11-06 07:27:23 -0800171 SkColorType colorType = bm.colorType();
halcanary7b9eabb2016-06-03 08:57:03 -0700172 SkAlphaType alphaType = bm.alphaType();
halcanaryaa4ba902015-11-06 07:27:23 -0800173 switch (colorType) {
174 case kRGBA_8888_SkColorType:
175 case kBGRA_8888_SkColorType: {
176 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700177 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
178 for (int y = 0; y < bm.height(); ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800179 const uint32_t* src = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700180 uint8_t* dst = scanline.get();
181 for (int x = 0; x < bm.width(); ++x) {
halcanary7b9eabb2016-06-03 08:57:03 -0700182 if (alphaType == kPremul_SkAlphaType) {
183 uint32_t color = *src++;
184 U8CPU alpha = SkGetA32Component(color, colorType);
185 if (alpha != SK_AlphaTRANSPARENT) {
186 pmcolor_to_rgb24(color, dst, colorType);
187 } else {
188 get_neighbor_avg_color(bm, x, y, dst, colorType);
189 }
190 dst += 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700191 } else {
halcanary7b9eabb2016-06-03 08:57:03 -0700192 uint32_t color = *src++;
193 *dst++ = SkGetR32Component(color, colorType);
194 *dst++ = SkGetG32Component(color, colorType);
195 *dst++ = SkGetB32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700196 }
halcanarydb0dcc72015-03-20 12:31:52 -0700197 }
198 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800199 }
halcanarydb0dcc72015-03-20 12:31:52 -0700200 return;
halcanary1b5c6042015-02-18 11:29:56 -0800201 }
halcanarydb0dcc72015-03-20 12:31:52 -0700202 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800203 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700204 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
205 for (int y = 0; y < bm.height(); ++y) {
206 const uint16_t* src = bm.getAddr16(0, y);
207 uint8_t* dst = scanline.get();
208 for (int x = 0; x < bm.width(); ++x) {
209 U16CPU color565 = *src++;
210 *dst++ = SkPacked16ToR32(color565);
211 *dst++ = SkPacked16ToG32(color565);
212 *dst++ = SkPacked16ToB32(color565);
213 }
214 out->write(scanline.get(), 3 * bm.width());
215 }
216 return;
217 }
218 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800219 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700220 fill_stream(out, '\x00', pixel_count(bm));
221 return;
222 case kGray_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800223 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700224 // these two formats need no transformation to serialize.
225 for (int y = 0; y < bm.height(); ++y) {
226 out->write(bm.getAddr8(0, y), bm.width());
227 }
228 return;
229 case kUnknown_SkColorType:
230 case kARGB_4444_SkColorType:
231 default:
232 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800233 }
234}
235
halcanarydb0dcc72015-03-20 12:31:52 -0700236////////////////////////////////////////////////////////////////////////////////
237
238static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
239 if (!bitmap.getPixels()) {
240 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800241 return;
242 }
halcanarydb0dcc72015-03-20 12:31:52 -0700243 SkBitmap copy;
Hal Canary3f467182017-07-20 14:15:25 -0400244 const SkBitmap& bm = supported_colortype(bitmap, &copy);
halcanaryaa4ba902015-11-06 07:27:23 -0800245 SkColorType colorType = bm.colorType();
246 switch (colorType) {
247 case kRGBA_8888_SkColorType:
248 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700249 SkAutoTMalloc<uint8_t> scanline(bm.width());
250 for (int y = 0; y < bm.height(); ++y) {
251 uint8_t* dst = scanline.get();
252 const SkPMColor* src = bm.getAddr32(0, y);
253 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800254 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700255 }
256 out->write(scanline.get(), bm.width());
257 }
258 return;
halcanary1b5c6042015-02-18 11:29:56 -0800259 }
halcanarydb0dcc72015-03-20 12:31:52 -0700260 case kAlpha_8_SkColorType:
261 for (int y = 0; y < bm.height(); ++y) {
262 out->write(bm.getAddr8(0, y), bm.width());
263 }
264 return;
halcanarydb0dcc72015-03-20 12:31:52 -0700265 case kRGB_565_SkColorType:
266 case kGray_8_SkColorType:
267 SkDEBUGFAIL("color type has no alpha");
268 return;
269 case kARGB_4444_SkColorType:
270 SkDEBUGFAIL("4444 color type should have been converted to N32");
271 return;
272 case kUnknown_SkColorType:
273 default:
274 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800275 }
276}
277
halcanary7a14b312015-10-01 07:28:13 -0700278static void emit_image_xobject(SkWStream* stream,
279 const SkImage* image,
280 bool alpha,
halcanary8103a342016-03-08 15:10:16 -0800281 const sk_sp<SkPDFObject>& smask,
halcanary530032a2016-08-18 14:22:52 -0700282 const SkPDFObjNumMap& objNumMap) {
halcanary7a14b312015-10-01 07:28:13 -0700283 SkBitmap bitmap;
Hal Canary4f29c202017-07-18 10:28:31 -0400284 if (!SkPDFUtils::ToBitmap(image, &bitmap)) {
285 // no pixels or wrong size: fill with zeros.
286 bitmap.setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()));
287 }
halcanary37c46ca2015-03-31 12:30:20 -0700288
289 // Write to a temporary buffer to get the compressed length.
290 SkDynamicMemoryWStream buffer;
291 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700292 if (alpha) {
293 bitmap_alpha_to_a8(bitmap, &deflateWStream);
294 } else {
295 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
296 }
Hal Canary6fee59b2017-05-23 11:47:05 -0400297 deflateWStream.finalize(); // call before buffer.bytesWritten().
halcanary37c46ca2015-03-31 12:30:20 -0700298
halcanary1b5c6042015-02-18 11:29:56 -0800299 SkPDFDict pdfDict("XObject");
300 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700301 pdfDict.insertInt("Width", bitmap.width());
302 pdfDict.insertInt("Height", bitmap.height());
303 if (alpha) {
304 pdfDict.insertName("ColorSpace", "DeviceGray");
halcanary7a14b312015-10-01 07:28:13 -0700305 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700306 pdfDict.insertName("ColorSpace", "DeviceGray");
307 } else {
308 pdfDict.insertName("ColorSpace", "DeviceRGB");
309 }
halcanary7a14b312015-10-01 07:28:13 -0700310 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800311 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800312 }
halcanary7a14b312015-10-01 07:28:13 -0700313 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700314 pdfDict.insertName("Filter", "FlateDecode");
Hal Canary6fee59b2017-05-23 11:47:05 -0400315 pdfDict.insertInt("Length", buffer.bytesWritten());
halcanary530032a2016-08-18 14:22:52 -0700316 pdfDict.emitObject(stream, objNumMap);
halcanary37c46ca2015-03-31 12:30:20 -0700317
Hal Canaryc172f9b2017-05-27 20:29:44 -0400318 stream->writeText(kStreamBegin);
Hal Canary6fee59b2017-05-23 11:47:05 -0400319 buffer.writeToAndReset(stream);
Hal Canaryc172f9b2017-05-27 20:29:44 -0400320 stream->writeText(kStreamEnd);
halcanary1b5c6042015-02-18 11:29:56 -0800321}
322
halcanary1b5c6042015-02-18 11:29:56 -0800323////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700324
halcanary7a14b312015-10-01 07:28:13 -0700325namespace {
326// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800327class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700328public:
halcanarya50151d2016-03-25 11:57:49 -0700329 PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700330 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700331 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700332 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700333 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
halcanary1b5c6042015-02-18 11:29:56 -0800334 }
halcanarybae235e2016-03-21 10:05:23 -0700335 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700336
337private:
halcanarya50151d2016-03-25 11:57:49 -0700338 sk_sp<SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700339};
340
341} // namespace
342
343////////////////////////////////////////////////////////////////////////////////
344
345namespace {
halcanary70d15542015-11-22 12:55:04 -0800346class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700347public:
halcanary34422612015-10-12 10:11:18 -0700348 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700349 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700350 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700351 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
halcanary7a14b312015-10-01 07:28:13 -0700352 }
halcanary530032a2016-08-18 14:22:52 -0700353 void addResources(SkPDFObjNumMap* catalog) const override {
354 catalog->addObjectRecursively(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700355 }
halcanarybae235e2016-03-21 10:05:23 -0700356 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanarya50151d2016-03-25 11:57:49 -0700357 PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
358 : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700359
360private:
halcanarya50151d2016-03-25 11:57:49 -0700361 sk_sp<SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700362 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700363};
364} // namespace
365
366////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800367
halcanarya8448bc2015-04-17 13:27:24 -0700368namespace {
369/**
halcanary7a14b312015-10-01 07:28:13 -0700370 * This PDFObject assumes that its constructor was handed YUV or
371 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
372 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700373 */
halcanary70d15542015-11-22 12:55:04 -0800374class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700375public:
halcanary7a14b312015-10-01 07:28:13 -0700376 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800377 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700378 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700379 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
halcanarybae235e2016-03-21 10:05:23 -0700380 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
halcanary530032a2016-08-18 14:22:52 -0700381 void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700382 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700383};
384
385void PDFJpegBitmap::emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700386 const SkPDFObjNumMap& objNumMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700387 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700388 SkPDFDict pdfDict("XObject");
389 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700390 pdfDict.insertInt("Width", fSize.width());
391 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700392 if (fIsYUV) {
393 pdfDict.insertName("ColorSpace", "DeviceRGB");
394 } else {
395 pdfDict.insertName("ColorSpace", "DeviceGray");
396 }
halcanarya8448bc2015-04-17 13:27:24 -0700397 pdfDict.insertInt("BitsPerComponent", 8);
398 pdfDict.insertName("Filter", "DCTDecode");
399 pdfDict.insertInt("ColorTransform", 0);
400 pdfDict.insertInt("Length", SkToInt(fData->size()));
halcanary530032a2016-08-18 14:22:52 -0700401 pdfDict.emitObject(stream, objNumMap);
Hal Canaryc172f9b2017-05-27 20:29:44 -0400402 stream->writeText(kStreamBegin);
halcanarya8448bc2015-04-17 13:27:24 -0700403 stream->write(fData->data(), fData->size());
Hal Canaryc172f9b2017-05-27 20:29:44 -0400404 stream->writeText(kStreamEnd);
halcanarya8448bc2015-04-17 13:27:24 -0700405}
406} // namespace
407
408////////////////////////////////////////////////////////////////////////////////
409
halcanarya50151d2016-03-25 11:57:49 -0700410sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
Mike Reeda4daf192017-12-14 13:25:04 -0500411#ifdef SK_SUPPORT_LEGACY_PDF_PIXELSERIALIZER
412 SkPixelSerializer* pixelSerializer,
413#endif
414 int encodingQuality) {
halcanarya50151d2016-03-25 11:57:49 -0700415 SkASSERT(image);
Mike Reeda4daf192017-12-14 13:25:04 -0500416 SkASSERT(encodingQuality >= 0);
Mike Reed6409f842017-07-11 16:03:13 -0400417 sk_sp<SkData> data = image->refEncodedData();
halcanary7a14b312015-10-01 07:28:13 -0700418 SkJFIFInfo info;
Mike Reeda4daf192017-12-14 13:25:04 -0500419 if (data && SkIsJFIF(data.get(), &info)) {
halcanary7a14b312015-10-01 07:28:13 -0700420 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
421 if (info.fSize == image->dimensions()) { // Sanity check.
422 // hold on to data, not image.
423 #ifdef SK_PDF_IMAGE_STATS
424 gJpegImageObjects.fetch_add(1);
425 #endif
halcanarya50151d2016-03-25 11:57:49 -0700426 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700427 }
428 }
halcanary712fdf72015-12-10 08:59:43 -0800429
Mike Reeda4daf192017-12-14 13:25:04 -0500430#ifdef SK_SUPPORT_LEGACY_PDF_PIXELSERIALIZER
halcanary712fdf72015-12-10 08:59:43 -0800431 if (pixelSerializer) {
432 SkBitmap bm;
Mike Reed4edb5d22017-04-17 11:02:51 -0400433 SkPixmap pmap;
Hal Canary4f29c202017-07-18 10:28:31 -0400434 if (SkPDFUtils::ToBitmap(image.get(), &bm) && bm.peekPixels(&pmap)) {
Mike Reed6409f842017-07-11 16:03:13 -0400435 data = pixelSerializer->encodeToData(pmap);
halcanaryfcad44b2016-03-06 14:47:10 -0800436 if (data && SkIsJFIF(data.get(), &info)) {
halcanary712fdf72015-12-10 08:59:43 -0800437 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
438 if (info.fSize == image->dimensions()) { // Sanity check.
halcanarya50151d2016-03-25 11:57:49 -0700439 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanary712fdf72015-12-10 08:59:43 -0800440 }
441 }
442 }
443 }
Mike Reeda4daf192017-12-14 13:25:04 -0500444#endif
445
446 const bool isOpaque = image_compute_is_opaque(image.get());
447
448 if (encodingQuality <= 100 && isOpaque) {
449 data = image->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
450 if (data && SkIsJFIF(data.get(), &info)) {
451 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
452 if (info.fSize == image->dimensions()) { // Sanity check.
453 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
454 }
455 }
456 }
halcanary712fdf72015-12-10 08:59:43 -0800457
halcanarya50151d2016-03-25 11:57:49 -0700458 sk_sp<SkPDFObject> smask;
Mike Reeda4daf192017-12-14 13:25:04 -0500459 if (!isOpaque) {
halcanarya50151d2016-03-25 11:57:49 -0700460 smask = sk_make_sp<PDFAlphaBitmap>(image);
461 }
halcanary7a14b312015-10-01 07:28:13 -0700462 #ifdef SK_PDF_IMAGE_STATS
463 gRegularImageObjects.fetch_add(1);
464 #endif
halcanarya50151d2016-03-25 11:57:49 -0700465 return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800466}