blob: 38e5327610da9d7ccf0dceb28dae95ba49e4d12c [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"
Hal Canaryc640d0d2018-06-13 09:59:02 -040019#include "SkTo.h"
halcanary1b5c6042015-02-18 11:29:56 -080020#include "SkUnPreMultiply.h"
21
halcanary7a14b312015-10-01 07:28:13 -070022bool image_compute_is_opaque(const SkImage* image) {
23 if (image->isOpaque()) {
24 return true;
25 }
26 // keep output PDF small at cost of possible resource use.
27 SkBitmap bm;
Hal Canary4f29c202017-07-18 10:28:31 -040028 // if image can not be read, treat as transparent.
29 return SkPDFUtils::ToBitmap(image, &bm) && SkBitmap::ComputeIsOpaque(bm);
halcanary7a14b312015-10-01 07:28:13 -070030}
31
halcanary1b5c6042015-02-18 11:29:56 -080032////////////////////////////////////////////////////////////////////////////////
33
Hal Canaryc172f9b2017-05-27 20:29:44 -040034static const char kStreamBegin[] = " stream\n";
halcanary1b5c6042015-02-18 11:29:56 -080035
Hal Canaryc172f9b2017-05-27 20:29:44 -040036static const char kStreamEnd[] = "\nendstream";
halcanary1b5c6042015-02-18 11:29:56 -080037
halcanarydb0dcc72015-03-20 12:31:52 -070038////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080039
halcanary1b5c6042015-02-18 11:29:56 -080040// write a single byte to a stream n times.
41static void fill_stream(SkWStream* out, char value, size_t n) {
42 char buffer[4096];
43 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070044 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
45 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080046 }
halcanarydb0dcc72015-03-20 12:31:52 -070047 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080048}
49
Cary Clarka4083c92017-09-15 11:59:23 -040050// TODO(reed@): Decide if these five functions belong in SkColorData.h
halcanaryaa4ba902015-11-06 07:27:23 -080051static bool SkIsBGRA(SkColorType ct) {
52 SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
53 return kBGRA_8888_SkColorType == ct;
54}
55
56// Interpret value as the given 4-byte SkColorType (BGRA_8888 or
57// RGBA_8888) and return the appropriate component. Each component
58// should be interpreted according to the associated SkAlphaType and
59// SkColorProfileType.
60static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
61 return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
62}
63static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
64 return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
65}
66static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
67 return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
68}
69static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
70 return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
71}
72
halcanarydb0dcc72015-03-20 12:31:52 -070073// unpremultiply and extract R, G, B components.
halcanaryaa4ba902015-11-06 07:27:23 -080074static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
Hal Canary5b474d32017-05-25 16:21:06 -040075 SkPMColorAssert(color);
halcanaryaa4ba902015-11-06 07:27:23 -080076 uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
77 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
78 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
79 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
halcanarydb0dcc72015-03-20 12:31:52 -070080}
81
82/* It is necessary to average the color component of transparent
83 pixels with their surrounding neighbors since the PDF renderer may
84 separately re-sample the alpha and color channels when the image is
85 not displayed at its native resolution. Since an alpha of zero
86 gives no information about the color component, the pathological
87 case is a white image with sharp transparency bounds - the color
88 channel goes to black, and the should-be-transparent pixels are
89 rendered as grey because of the separate soft mask and color
90 resizing. e.g.: gm/bitmappremul.cpp */
91static void get_neighbor_avg_color(const SkBitmap& bm,
92 int xOrig,
93 int yOrig,
halcanaryaa4ba902015-11-06 07:27:23 -080094 uint8_t rgb[3],
95 SkColorType ct) {
halcanarydb0dcc72015-03-20 12:31:52 -070096 unsigned a = 0, r = 0, g = 0, b = 0;
97 // Clamp the range to the edge of the bitmap.
98 int ymin = SkTMax(0, yOrig - 1);
99 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
100 int xmin = SkTMax(0, xOrig - 1);
101 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
102 for (int y = ymin; y <= ymax; ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800103 uint32_t* scanline = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700104 for (int x = xmin; x <= xmax; ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800105 uint32_t color = scanline[x];
Hal Canary5b474d32017-05-25 16:21:06 -0400106 SkPMColorAssert(color);
halcanaryaa4ba902015-11-06 07:27:23 -0800107 a += SkGetA32Component(color, ct);
108 r += SkGetR32Component(color, ct);
109 g += SkGetG32Component(color, ct);
110 b += SkGetB32Component(color, ct);
halcanary1b5c6042015-02-18 11:29:56 -0800111 }
112 }
halcanarydb0dcc72015-03-20 12:31:52 -0700113 if (a > 0) {
114 rgb[0] = SkToU8(255 * r / a);
115 rgb[1] = SkToU8(255 * g / a);
116 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800117 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700118 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800119 }
120}
121
halcanarydb0dcc72015-03-20 12:31:52 -0700122static size_t pixel_count(const SkBitmap& bm) {
123 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
124}
125
Hal Canary3f467182017-07-20 14:15:25 -0400126static const SkBitmap& supported_colortype(const SkBitmap& input, SkBitmap* copy) {
127 switch (input.colorType()) {
128 case kUnknown_SkColorType:
129 SkDEBUGFAIL("kUnknown_SkColorType");
130 case kAlpha_8_SkColorType:
131 case kRGB_565_SkColorType:
132 case kRGBA_8888_SkColorType:
133 case kBGRA_8888_SkColorType:
134 case kGray_8_SkColorType:
135 return input; // supported
136 default:
137 // if other colortypes are introduced in the future,
138 // they will hit this code.
139 break;
halcanarydb0dcc72015-03-20 12:31:52 -0700140 }
Hal Canary3f467182017-07-20 14:15:25 -0400141 // Fallback for rarely used ARGB_4444 and ARGB_F16: do a wasteful tmp copy.
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400142 copy->allocPixels(input.info().makeColorType(kN32_SkColorType));
143 SkAssertResult(input.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0));
halcanarydb0dcc72015-03-20 12:31:52 -0700144 copy->setImmutable();
145 return *copy;
146}
147
148static size_t pdf_color_component_count(SkColorType ct) {
149 switch (ct) {
Hal Canary3f467182017-07-20 14:15:25 -0400150 case kUnknown_SkColorType:
151 SkDEBUGFAIL("kUnknown_SkColorType");
halcanarydb0dcc72015-03-20 12:31:52 -0700152 case kAlpha_8_SkColorType:
halcanarydb0dcc72015-03-20 12:31:52 -0700153 case kGray_8_SkColorType:
154 return 1;
Hal Canary3f467182017-07-20 14:15:25 -0400155 case kRGB_565_SkColorType:
156 case kRGBA_8888_SkColorType:
157 case kBGRA_8888_SkColorType:
158 default: // converted to N32
159 return 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700160 }
161}
162
163static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
164 if (!bitmap.getPixels()) {
165 size_t size = pixel_count(bitmap) *
166 pdf_color_component_count(bitmap.colorType());
167 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800168 return;
169 }
halcanarydb0dcc72015-03-20 12:31:52 -0700170 SkBitmap copy;
Hal Canary3f467182017-07-20 14:15:25 -0400171 const SkBitmap& bm = supported_colortype(bitmap, &copy);
halcanaryaa4ba902015-11-06 07:27:23 -0800172 SkColorType colorType = bm.colorType();
halcanary7b9eabb2016-06-03 08:57:03 -0700173 SkAlphaType alphaType = bm.alphaType();
halcanaryaa4ba902015-11-06 07:27:23 -0800174 switch (colorType) {
175 case kRGBA_8888_SkColorType:
176 case kBGRA_8888_SkColorType: {
177 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700178 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
179 for (int y = 0; y < bm.height(); ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800180 const uint32_t* src = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700181 uint8_t* dst = scanline.get();
182 for (int x = 0; x < bm.width(); ++x) {
halcanary7b9eabb2016-06-03 08:57:03 -0700183 if (alphaType == kPremul_SkAlphaType) {
184 uint32_t color = *src++;
185 U8CPU alpha = SkGetA32Component(color, colorType);
186 if (alpha != SK_AlphaTRANSPARENT) {
187 pmcolor_to_rgb24(color, dst, colorType);
188 } else {
189 get_neighbor_avg_color(bm, x, y, dst, colorType);
190 }
191 dst += 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700192 } else {
halcanary7b9eabb2016-06-03 08:57:03 -0700193 uint32_t color = *src++;
194 *dst++ = SkGetR32Component(color, colorType);
195 *dst++ = SkGetG32Component(color, colorType);
196 *dst++ = SkGetB32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700197 }
halcanarydb0dcc72015-03-20 12:31:52 -0700198 }
199 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800200 }
halcanarydb0dcc72015-03-20 12:31:52 -0700201 return;
halcanary1b5c6042015-02-18 11:29:56 -0800202 }
halcanarydb0dcc72015-03-20 12:31:52 -0700203 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800204 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700205 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
206 for (int y = 0; y < bm.height(); ++y) {
207 const uint16_t* src = bm.getAddr16(0, y);
208 uint8_t* dst = scanline.get();
209 for (int x = 0; x < bm.width(); ++x) {
210 U16CPU color565 = *src++;
211 *dst++ = SkPacked16ToR32(color565);
212 *dst++ = SkPacked16ToG32(color565);
213 *dst++ = SkPacked16ToB32(color565);
214 }
215 out->write(scanline.get(), 3 * bm.width());
216 }
217 return;
218 }
219 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800220 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700221 fill_stream(out, '\x00', pixel_count(bm));
222 return;
223 case kGray_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800224 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700225 // these two formats need no transformation to serialize.
226 for (int y = 0; y < bm.height(); ++y) {
227 out->write(bm.getAddr8(0, y), bm.width());
228 }
229 return;
230 case kUnknown_SkColorType:
231 case kARGB_4444_SkColorType:
232 default:
233 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800234 }
235}
236
halcanarydb0dcc72015-03-20 12:31:52 -0700237////////////////////////////////////////////////////////////////////////////////
238
239static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
240 if (!bitmap.getPixels()) {
241 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800242 return;
243 }
halcanarydb0dcc72015-03-20 12:31:52 -0700244 SkBitmap copy;
Hal Canary3f467182017-07-20 14:15:25 -0400245 const SkBitmap& bm = supported_colortype(bitmap, &copy);
halcanaryaa4ba902015-11-06 07:27:23 -0800246 SkColorType colorType = bm.colorType();
247 switch (colorType) {
248 case kRGBA_8888_SkColorType:
249 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700250 SkAutoTMalloc<uint8_t> scanline(bm.width());
251 for (int y = 0; y < bm.height(); ++y) {
252 uint8_t* dst = scanline.get();
253 const SkPMColor* src = bm.getAddr32(0, y);
254 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800255 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700256 }
257 out->write(scanline.get(), bm.width());
258 }
259 return;
halcanary1b5c6042015-02-18 11:29:56 -0800260 }
halcanarydb0dcc72015-03-20 12:31:52 -0700261 case kAlpha_8_SkColorType:
262 for (int y = 0; y < bm.height(); ++y) {
263 out->write(bm.getAddr8(0, y), bm.width());
264 }
265 return;
halcanarydb0dcc72015-03-20 12:31:52 -0700266 case kRGB_565_SkColorType:
267 case kGray_8_SkColorType:
268 SkDEBUGFAIL("color type has no alpha");
269 return;
270 case kARGB_4444_SkColorType:
271 SkDEBUGFAIL("4444 color type should have been converted to N32");
272 return;
273 case kUnknown_SkColorType:
274 default:
275 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800276 }
277}
278
halcanary7a14b312015-10-01 07:28:13 -0700279static void emit_image_xobject(SkWStream* stream,
280 const SkImage* image,
281 bool alpha,
halcanary8103a342016-03-08 15:10:16 -0800282 const sk_sp<SkPDFObject>& smask,
halcanary530032a2016-08-18 14:22:52 -0700283 const SkPDFObjNumMap& objNumMap) {
halcanary7a14b312015-10-01 07:28:13 -0700284 SkBitmap bitmap;
Hal Canary4f29c202017-07-18 10:28:31 -0400285 if (!SkPDFUtils::ToBitmap(image, &bitmap)) {
286 // no pixels or wrong size: fill with zeros.
287 bitmap.setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()));
288 }
halcanary37c46ca2015-03-31 12:30:20 -0700289
290 // Write to a temporary buffer to get the compressed length.
291 SkDynamicMemoryWStream buffer;
292 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700293 if (alpha) {
294 bitmap_alpha_to_a8(bitmap, &deflateWStream);
295 } else {
296 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
297 }
Hal Canary6fee59b2017-05-23 11:47:05 -0400298 deflateWStream.finalize(); // call before buffer.bytesWritten().
halcanary37c46ca2015-03-31 12:30:20 -0700299
halcanary1b5c6042015-02-18 11:29:56 -0800300 SkPDFDict pdfDict("XObject");
301 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700302 pdfDict.insertInt("Width", bitmap.width());
303 pdfDict.insertInt("Height", bitmap.height());
304 if (alpha) {
305 pdfDict.insertName("ColorSpace", "DeviceGray");
halcanary7a14b312015-10-01 07:28:13 -0700306 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700307 pdfDict.insertName("ColorSpace", "DeviceGray");
308 } else {
309 pdfDict.insertName("ColorSpace", "DeviceRGB");
310 }
halcanary7a14b312015-10-01 07:28:13 -0700311 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800312 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800313 }
halcanary7a14b312015-10-01 07:28:13 -0700314 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700315 pdfDict.insertName("Filter", "FlateDecode");
Hal Canary6fee59b2017-05-23 11:47:05 -0400316 pdfDict.insertInt("Length", buffer.bytesWritten());
halcanary530032a2016-08-18 14:22:52 -0700317 pdfDict.emitObject(stream, objNumMap);
halcanary37c46ca2015-03-31 12:30:20 -0700318
Hal Canaryc172f9b2017-05-27 20:29:44 -0400319 stream->writeText(kStreamBegin);
Hal Canary6fee59b2017-05-23 11:47:05 -0400320 buffer.writeToAndReset(stream);
Hal Canaryc172f9b2017-05-27 20:29:44 -0400321 stream->writeText(kStreamEnd);
halcanary1b5c6042015-02-18 11:29:56 -0800322}
323
halcanary1b5c6042015-02-18 11:29:56 -0800324////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700325
halcanary7a14b312015-10-01 07:28:13 -0700326namespace {
327// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800328class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700329public:
halcanarya50151d2016-03-25 11:57:49 -0700330 PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700331 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700332 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700333 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700334 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
halcanary1b5c6042015-02-18 11:29:56 -0800335 }
halcanarybae235e2016-03-21 10:05:23 -0700336 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700337
338private:
halcanarya50151d2016-03-25 11:57:49 -0700339 sk_sp<SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700340};
341
342} // namespace
343
344////////////////////////////////////////////////////////////////////////////////
345
346namespace {
halcanary70d15542015-11-22 12:55:04 -0800347class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700348public:
halcanary34422612015-10-12 10:11:18 -0700349 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700350 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700351 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700352 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
halcanary7a14b312015-10-01 07:28:13 -0700353 }
halcanary530032a2016-08-18 14:22:52 -0700354 void addResources(SkPDFObjNumMap* catalog) const override {
355 catalog->addObjectRecursively(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700356 }
halcanarybae235e2016-03-21 10:05:23 -0700357 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanarya50151d2016-03-25 11:57:49 -0700358 PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
359 : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700360
361private:
halcanarya50151d2016-03-25 11:57:49 -0700362 sk_sp<SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700363 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700364};
365} // namespace
366
367////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800368
halcanarya8448bc2015-04-17 13:27:24 -0700369namespace {
370/**
halcanary7a14b312015-10-01 07:28:13 -0700371 * This PDFObject assumes that its constructor was handed YUV or
372 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
373 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700374 */
halcanary70d15542015-11-22 12:55:04 -0800375class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700376public:
halcanary7a14b312015-10-01 07:28:13 -0700377 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800378 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700379 bool fIsYUV;
Hal Canary83e0f1b2018-04-05 16:58:41 -0400380 PDFJpegBitmap(SkISize size, sk_sp<SkData> data, bool isYUV)
381 : fSize(size), fData(std::move(data)), fIsYUV(isYUV) { SkASSERT(fData); }
halcanary530032a2016-08-18 14:22:52 -0700382 void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700383 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700384};
385
386void PDFJpegBitmap::emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700387 const SkPDFObjNumMap& objNumMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700388 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700389 SkPDFDict pdfDict("XObject");
390 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700391 pdfDict.insertInt("Width", fSize.width());
392 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700393 if (fIsYUV) {
394 pdfDict.insertName("ColorSpace", "DeviceRGB");
395 } else {
396 pdfDict.insertName("ColorSpace", "DeviceGray");
397 }
halcanarya8448bc2015-04-17 13:27:24 -0700398 pdfDict.insertInt("BitsPerComponent", 8);
399 pdfDict.insertName("Filter", "DCTDecode");
400 pdfDict.insertInt("ColorTransform", 0);
401 pdfDict.insertInt("Length", SkToInt(fData->size()));
halcanary530032a2016-08-18 14:22:52 -0700402 pdfDict.emitObject(stream, objNumMap);
Hal Canaryc172f9b2017-05-27 20:29:44 -0400403 stream->writeText(kStreamBegin);
halcanarya8448bc2015-04-17 13:27:24 -0700404 stream->write(fData->data(), fData->size());
Hal Canaryc172f9b2017-05-27 20:29:44 -0400405 stream->writeText(kStreamEnd);
halcanarya8448bc2015-04-17 13:27:24 -0700406}
407} // namespace
408
409////////////////////////////////////////////////////////////////////////////////
Hal Canary83e0f1b2018-04-05 16:58:41 -0400410sk_sp<PDFJpegBitmap> make_jpeg_bitmap(sk_sp<SkData> data, SkISize size) {
411 SkISize jpegSize;
412 SkEncodedInfo::Color jpegColorType;
413 SkEncodedOrigin exifOrientation;
414 if (data && SkGetJpegInfo(data->data(), data->size(), &jpegSize,
415 &jpegColorType, &exifOrientation)) {
416 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
417 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
418 if (jpegSize == size // Sanity check.
419 && goodColorType
420 && kTopLeft_SkEncodedOrigin == exifOrientation) {
halcanary7a14b312015-10-01 07:28:13 -0700421 // hold on to data, not image.
422 #ifdef SK_PDF_IMAGE_STATS
423 gJpegImageObjects.fetch_add(1);
424 #endif
Hal Canary83e0f1b2018-04-05 16:58:41 -0400425 return sk_make_sp<PDFJpegBitmap>(jpegSize, std::move(data), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700426 }
427 }
Hal Canary83e0f1b2018-04-05 16:58:41 -0400428 return nullptr;
429}
430
431sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image, int encodingQuality) {
432 SkASSERT(image);
433 SkASSERT(encodingQuality >= 0);
434 SkISize dimensions = image->dimensions();
435 sk_sp<SkData> data = image->refEncodedData();
436 if (auto jpeg = make_jpeg_bitmap(std::move(data), dimensions)) {
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400437 return std::move(jpeg);
Hal Canary83e0f1b2018-04-05 16:58:41 -0400438 }
halcanary712fdf72015-12-10 08:59:43 -0800439
Mike Reeda4daf192017-12-14 13:25:04 -0500440 const bool isOpaque = image_compute_is_opaque(image.get());
441
442 if (encodingQuality <= 100 && isOpaque) {
443 data = image->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
Hal Canary83e0f1b2018-04-05 16:58:41 -0400444 if (auto jpeg = make_jpeg_bitmap(std::move(data), dimensions)) {
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400445 return std::move(jpeg);
Mike Reeda4daf192017-12-14 13:25:04 -0500446 }
447 }
halcanary712fdf72015-12-10 08:59:43 -0800448
halcanarya50151d2016-03-25 11:57:49 -0700449 sk_sp<SkPDFObject> smask;
Mike Reeda4daf192017-12-14 13:25:04 -0500450 if (!isOpaque) {
halcanarya50151d2016-03-25 11:57:49 -0700451 smask = sk_make_sp<PDFAlphaBitmap>(image);
452 }
halcanary7a14b312015-10-01 07:28:13 -0700453 #ifdef SK_PDF_IMAGE_STATS
454 gRegularImageObjects.fetch_add(1);
455 #endif
halcanarya50151d2016-03-25 11:57:49 -0700456 return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800457}