| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 "SkDeflateWStream.h" | 
|  | 10 #include "SkPDFBitmap.h" | 
|  | 11 #include "SkPDFCanon.h" | 
|  | 12 #include "SkPDFCatalog.h" | 
|  | 13 #include "SkPDFDocument.h" | 
|  | 14 #include "SkStream.h" | 
|  | 15 #include "SkUnPreMultiply.h" | 
|  | 16 | 
|  | 17 //////////////////////////////////////////////////////////////////////////////// | 
|  | 18 | 
|  | 19 static void pdf_stream_begin(SkWStream* stream) { | 
|  | 20     static const char streamBegin[] = " stream\n"; | 
|  | 21     stream->write(streamBegin, strlen(streamBegin)); | 
|  | 22 } | 
|  | 23 | 
|  | 24 static void pdf_stream_end(SkWStream* stream) { | 
|  | 25     static const char streamEnd[] = "\nendstream"; | 
|  | 26     stream->write(streamEnd, strlen(streamEnd)); | 
|  | 27 } | 
|  | 28 | 
|  | 29 static size_t pixel_count(const SkBitmap& bm) { | 
|  | 30     return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); | 
|  | 31 } | 
|  | 32 | 
|  | 33 static bool skip_compression(SkPDFDocument::Flags flag) { | 
|  | 34 #ifndef SK_NO_FLATE | 
|  | 35     return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags); | 
|  | 36 #else | 
|  | 37     return true; | 
|  | 38 #endif  // SK_NO_FLATE | 
|  | 39 } | 
|  | 40 | 
|  | 41 // write a single byte to a stream n times. | 
|  | 42 static void fill_stream(SkWStream* out, char value, size_t n) { | 
|  | 43     char buffer[4096]; | 
|  | 44     memset(buffer, value, sizeof(buffer)); | 
|  | 45     while (n) { | 
|  | 46         size_t k = SkTMin(n, sizeof(buffer)); | 
|  | 47         out->write(buffer, k); | 
|  | 48         n -= k; | 
|  | 49     } | 
|  | 50 } | 
|  | 51 | 
|  | 52 static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap, | 
|  | 53                                                 int xOrig, | 
|  | 54                                                 int yOrig) { | 
|  | 55     SkASSERT(kN32_SkColorType == bitmap.colorType()); | 
|  | 56     SkASSERT(bitmap.getPixels()); | 
|  | 57     uint8_t count = 0; | 
|  | 58     unsigned r = 0; | 
|  | 59     unsigned g = 0; | 
|  | 60     unsigned b = 0; | 
|  | 61     for (int y = yOrig - 1; y <= yOrig + 1; ++y) { | 
|  | 62         if (y < 0 || y >= bitmap.height()) { | 
|  | 63             continue; | 
|  | 64         } | 
|  | 65         uint32_t* src = bitmap.getAddr32(0, y); | 
|  | 66         for (int x = xOrig - 1; x <= xOrig + 1; ++x) { | 
|  | 67             if (x < 0 || x >= bitmap.width()) { | 
|  | 68                 continue; | 
|  | 69             } | 
|  | 70             SkPMColor pmColor = src[x]; | 
|  | 71             U8CPU alpha = SkGetPackedA32(pmColor); | 
|  | 72             if (alpha != SK_AlphaTRANSPARENT) { | 
|  | 73                 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 
|  | 74                 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); | 
|  | 75                 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); | 
|  | 76                 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); | 
|  | 77                 ++count; | 
|  | 78             } | 
|  | 79         } | 
|  | 80     } | 
|  | 81     if (count == 0) { | 
|  | 82         return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | 
|  | 83     } else { | 
|  | 84         return SkPackARGB32NoCheck( | 
|  | 85                 SK_AlphaOPAQUE, r / count, g / count, b / count); | 
|  | 86     } | 
|  | 87 } | 
|  | 88 | 
|  | 89 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) { | 
|  | 90     SkASSERT(kN32_SkColorType == bm.colorType()); | 
|  | 91     if (!bm.getPixels()) { | 
|  | 92         fill_stream(out, '\xFF', 3 * pixel_count(bm)); | 
|  | 93         return; | 
|  | 94     } | 
|  | 95     size_t scanlineLength = 3 * bm.width(); | 
|  | 96     SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 
|  | 97     for (int y = 0; y < bm.height(); ++y) { | 
|  | 98         uint8_t* dst = scanline.get(); | 
|  | 99         const SkPMColor* src = bm.getAddr32(0, y); | 
|  | 100         for (int x = 0; x < bm.width(); ++x) { | 
|  | 101             SkPMColor color = *src++; | 
|  | 102             U8CPU alpha = SkGetPackedA32(color); | 
|  | 103             if (alpha != SK_AlphaTRANSPARENT) { | 
|  | 104                 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 
|  | 105                 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); | 
|  | 106                 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); | 
|  | 107                 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); | 
|  | 108             } else { | 
|  | 109                 /* It is necessary to average the color component of | 
|  | 110                    transparent pixels with their surrounding neighbors | 
|  | 111                    since the PDF renderer may separately re-sample the | 
|  | 112                    alpha and color channels when the image is not | 
|  | 113                    displayed at its native resolution. Since an alpha | 
|  | 114                    of zero gives no information about the color | 
|  | 115                    component, the pathological case is a white image | 
|  | 116                    with sharp transparency bounds - the color channel | 
|  | 117                    goes to black, and the should-be-transparent pixels | 
|  | 118                    are rendered as grey because of the separate soft | 
|  | 119                    mask and color resizing. e.g.: gm/bitmappremul.cpp */ | 
|  | 120                 color = get_pmcolor_neighbor_avg_color(bm, x, y); | 
|  | 121                 *dst++ = SkGetPackedR32(color); | 
|  | 122                 *dst++ = SkGetPackedG32(color); | 
|  | 123                 *dst++ = SkGetPackedB32(color); | 
|  | 124             } | 
|  | 125         } | 
|  | 126         out->write(scanline.get(), scanlineLength); | 
|  | 127     } | 
|  | 128 } | 
|  | 129 | 
|  | 130 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { | 
|  | 131     SkASSERT(kN32_SkColorType == bm.colorType()); | 
|  | 132     if (!bm.getPixels()) { | 
|  | 133         fill_stream(out, '\xFF', pixel_count(bm)); | 
|  | 134         return; | 
|  | 135     } | 
|  | 136     size_t scanlineLength = bm.width(); | 
|  | 137     SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 
|  | 138     for (int y = 0; y < bm.height(); ++y) { | 
|  | 139         uint8_t* dst = scanline.get(); | 
|  | 140         const SkPMColor* src = bm.getAddr32(0, y); | 
|  | 141         for (int x = 0; x < bm.width(); ++x) { | 
|  | 142             *dst++ = SkGetPackedA32(*src++); | 
|  | 143         } | 
|  | 144         out->write(scanline.get(), scanlineLength); | 
|  | 145     } | 
|  | 146 } | 
|  | 147 | 
|  | 148 //////////////////////////////////////////////////////////////////////////////// | 
|  | 149 | 
|  | 150 namespace { | 
|  | 151 // This SkPDFObject only outputs the alpha layer of the given bitmap. | 
|  | 152 class PDFAlphaBitmap : public SkPDFObject { | 
|  | 153 public: | 
|  | 154     PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | 
|  | 155     ~PDFAlphaBitmap() {} | 
|  | 156     void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; | 
|  | 157     void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} | 
|  | 158 | 
|  | 159 private: | 
|  | 160     const SkBitmap fBitmap; | 
|  | 161     void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; | 
|  | 162 }; | 
|  | 163 | 
|  | 164 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 
|  | 165     SkAutoLockPixels autoLockPixels(fBitmap); | 
|  | 166 | 
|  | 167     if (skip_compression(catalog->getDocumentFlags())) { | 
|  | 168         this->emitDict(stream, catalog, pixel_count(fBitmap), | 
|  | 169                        /*deflate=*/false); | 
|  | 170         pdf_stream_begin(stream); | 
|  | 171         pmcolor_alpha_to_a8(fBitmap, stream); | 
|  | 172         pdf_stream_end(stream); | 
|  | 173         return; | 
|  | 174     } | 
|  | 175 #ifndef SK_NO_FLATE | 
|  | 176     // Write to a temporary buffer to get the compressed length. | 
|  | 177     SkDynamicMemoryWStream buffer; | 
|  | 178     SkDeflateWStream deflateWStream(&buffer); | 
|  | 179     pmcolor_alpha_to_a8(fBitmap, &deflateWStream); | 
|  | 180     deflateWStream.finalize();  // call before detachAsStream(). | 
|  | 181     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 
|  | 182 | 
|  | 183     this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 
|  | 184     pdf_stream_begin(stream); | 
|  | 185     stream->writeStream(asset.get(), asset->getLength()); | 
|  | 186     pdf_stream_end(stream); | 
|  | 187 #endif  // SK_NO_FLATE | 
|  | 188 } | 
|  | 189 | 
|  | 190 void PDFAlphaBitmap::emitDict(SkWStream* stream, | 
|  | 191                               SkPDFCatalog* catalog, | 
|  | 192                               size_t length, | 
|  | 193                               bool deflate) const { | 
|  | 194     SkPDFDict pdfDict("XObject"); | 
|  | 195     pdfDict.insertName("Subtype", "Image"); | 
|  | 196     pdfDict.insertInt("Width", fBitmap.width()); | 
|  | 197     pdfDict.insertInt("Height", fBitmap.height()); | 
|  | 198     pdfDict.insertName("ColorSpace", "DeviceGray"); | 
|  | 199     pdfDict.insertInt("BitsPerComponent", 8); | 
|  | 200     if (deflate) { | 
|  | 201         pdfDict.insertName("Filter", "FlateDecode"); | 
|  | 202     } | 
|  | 203     pdfDict.insertInt("Length", length); | 
|  | 204     pdfDict.emitObject(stream, catalog); | 
|  | 205 } | 
|  | 206 }  // namespace | 
|  | 207 | 
|  | 208 //////////////////////////////////////////////////////////////////////////////// | 
|  | 209 | 
|  | 210 void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet, | 
|  | 211                                SkPDFCatalog* catalog) const { | 
|  | 212     if (fSMask.get()) { | 
|  | 213         resourceSet->add(fSMask.get()); | 
|  | 214     } | 
|  | 215 } | 
|  | 216 | 
|  | 217 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 
|  | 218     SkAutoLockPixels autoLockPixels(fBitmap); | 
|  | 219 | 
|  | 220     if (skip_compression(catalog->getDocumentFlags())) { | 
|  | 221         this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), | 
|  | 222                        /*deflate=*/false); | 
|  | 223         pdf_stream_begin(stream); | 
|  | 224         pmcolor_to_rgb24(fBitmap, stream); | 
|  | 225         pdf_stream_end(stream); | 
|  | 226         return; | 
|  | 227     } | 
|  | 228 #ifndef SK_NO_FLATE | 
|  | 229     // Write to a temporary buffer to get the compressed length. | 
|  | 230     SkDynamicMemoryWStream buffer; | 
|  | 231     SkDeflateWStream deflateWStream(&buffer); | 
|  | 232     pmcolor_to_rgb24(fBitmap, &deflateWStream); | 
|  | 233     deflateWStream.finalize();  // call before detachAsStream(). | 
|  | 234     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 
|  | 235 | 
|  | 236     this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 
|  | 237     pdf_stream_begin(stream); | 
|  | 238     stream->writeStream(asset.get(), asset->getLength()); | 
|  | 239     pdf_stream_end(stream); | 
|  | 240 #endif  // SK_NO_FLATE | 
|  | 241 } | 
|  | 242 | 
|  | 243 void SkPDFBitmap::emitDict(SkWStream* stream, | 
|  | 244                            SkPDFCatalog* catalog, | 
|  | 245                            size_t length, | 
|  | 246                            bool deflate) const { | 
|  | 247     SkPDFDict pdfDict("XObject"); | 
|  | 248     pdfDict.insertName("Subtype", "Image"); | 
|  | 249     pdfDict.insertInt("Width", fBitmap.width()); | 
|  | 250     pdfDict.insertInt("Height", fBitmap.height()); | 
|  | 251     pdfDict.insertName("ColorSpace", "DeviceRGB"); | 
|  | 252     pdfDict.insertInt("BitsPerComponent", 8); | 
|  | 253     if (fSMask) { | 
|  | 254         pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); | 
|  | 255     } | 
|  | 256     if (deflate) { | 
|  | 257         pdfDict.insertName("Filter", "FlateDecode"); | 
|  | 258     } | 
|  | 259     pdfDict.insertInt("Length", length); | 
|  | 260     pdfDict.emitObject(stream, catalog); | 
|  | 261 } | 
|  | 262 | 
|  | 263 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask) | 
|  | 264     : fBitmap(bm), fSMask(smask) {} | 
|  | 265 | 
|  | 266 SkPDFBitmap::~SkPDFBitmap() { | 
|  | 267     SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex()); | 
|  | 268     SkPDFCanon::GetCanon().removeBitmap(this); | 
|  | 269 } | 
|  | 270 | 
|  | 271 //////////////////////////////////////////////////////////////////////////////// | 
|  | 272 static bool is_transparent(const SkBitmap& bm) { | 
|  | 273     SkAutoLockPixels autoLockPixels(bm); | 
|  | 274     if (NULL == bm.getPixels()) { | 
|  | 275         return true; | 
|  | 276     } | 
|  | 277     SkASSERT(kN32_SkColorType == bm.colorType()); | 
|  | 278     for (int y = 0; y < bm.height(); ++y) { | 
|  | 279         U8CPU alpha = 0; | 
|  | 280         const SkPMColor* src = bm.getAddr32(0, y); | 
|  | 281         for (int x = 0; x < bm.width(); ++x) { | 
|  | 282             alpha |= SkGetPackedA32(*src++); | 
|  | 283         } | 
|  | 284         if (alpha) { | 
|  | 285             return false; | 
|  | 286         } | 
|  | 287     } | 
|  | 288     return true; | 
|  | 289 } | 
|  | 290 | 
|  | 291 // TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter. | 
|  | 292 SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap, | 
|  | 293                                  const SkIRect& subset) { | 
|  | 294     if (kN32_SkColorType != bitmap.colorType()) { | 
|  | 295         // TODO(halcanary): support other colortypes. | 
|  | 296         return NULL; | 
|  | 297     } | 
|  | 298     SkBitmap bm; | 
|  | 299     // Should extractSubset be done by the SkPDFDevice? | 
|  | 300     if (!bitmap.extractSubset(&bm, subset)) { | 
|  | 301         return NULL; | 
|  | 302     } | 
|  | 303     if (bm.drawsNothing()) { | 
|  | 304         return NULL; | 
|  | 305     } | 
|  | 306     if (!bm.isImmutable()) { | 
|  | 307         SkBitmap copy; | 
|  | 308         if (!bm.copyTo(©)) { | 
|  | 309             return NULL; | 
|  | 310         } | 
|  | 311         copy.setImmutable(); | 
|  | 312         bm = copy; | 
|  | 313     } | 
|  | 314 | 
|  | 315     SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex()); | 
|  | 316     SkPDFCanon& canon = SkPDFCanon::GetCanon(); | 
|  | 317     SkPDFBitmap* pdfBitmap = canon.findBitmap(bm); | 
|  | 318     if (pdfBitmap) { | 
|  | 319         return SkRef(pdfBitmap); | 
|  | 320     } | 
|  | 321     SkPDFObject* smask = NULL; | 
|  | 322     if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 
|  | 323         if (is_transparent(bm)) { | 
|  | 324             return NULL; | 
|  | 325         } | 
|  | 326         // PDFAlphaBitmaps do not get directly canonicalized (they | 
|  | 327         // are refed by the SkPDFBitmap). | 
|  | 328         smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); | 
|  | 329     } | 
|  | 330     pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); | 
|  | 331     canon.addBitmap(pdfBitmap); | 
|  | 332     return pdfBitmap; | 
|  | 333 } | 
| OLD | NEW | 
|---|