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