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 |