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 return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags); |
| 35 } |
| 36 |
| 37 static bool is_better_compressed(const SkStreamAsset* asset, |
| 38 const SkBitmap& bitmap) { |
| 39 return asset->getLength() + strlen("/Filter /FlateDecode\n") |
| 40 <= pixel_count(bitmap); |
| 41 } |
| 42 |
| 43 |
| 44 |
| 45 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) { |
| 46 SkASSERT(kN32_SkColorType == bm.colorType()); |
| 47 size_t scanlineLength = 3 * bm.width(); |
| 48 SkAutoTMalloc<uint8_t> scanline(scanlineLength); |
| 49 for (int y = 0; y < bm.height(); ++y) { |
| 50 uint8_t* dst = scanline.get(); |
| 51 const SkPMColor* src = bm.getAddr32(0, y); |
| 52 for (int x = 0; x < bm.width(); ++x) { |
| 53 SkPMColor pmColor = *src++; |
| 54 uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor)); |
| 55 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); |
| 56 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); |
| 57 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); |
| 58 } |
| 59 out->write(scanline.get(), scanlineLength); |
| 60 } |
| 61 } |
| 62 |
| 63 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { |
| 64 SkASSERT(kN32_SkColorType == bm.colorType()); |
| 65 size_t scanlineLength = bm.width(); |
| 66 SkAutoTMalloc<uint8_t> scanline(scanlineLength); |
| 67 for (int y = 0; y < bm.height(); ++y) { |
| 68 uint8_t* dst = scanline.get(); |
| 69 const SkPMColor* src = bm.getAddr32(0, y); |
| 70 for (int x = 0; x < bm.width(); ++x) { |
| 71 *dst++ = SkGetPackedA32(*src++); |
| 72 } |
| 73 out->write(scanline.get(), scanlineLength); |
| 74 } |
| 75 } |
| 76 |
| 77 //////////////////////////////////////////////////////////////////////////////// |
| 78 |
| 79 namespace { |
| 80 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
| 81 class PDFAlphaBitmap : public SkPDFObject { |
| 82 public: |
| 83 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} |
| 84 ~PDFAlphaBitmap() {} |
| 85 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; |
| 86 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} |
| 87 |
| 88 private: |
| 89 const SkBitmap fBitmap; |
| 90 void emitFast(SkWStream*, SkPDFCatalog*) const; |
| 91 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; |
| 92 }; |
| 93 |
| 94 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| 95 SkASSERT(kN32_SkColorType == fBitmap.colorType()); |
| 96 SkAutoLockPixels autoLockPixels(fBitmap); |
| 97 SkASSERT(fBitmap.getPixels()); |
| 98 |
| 99 #ifdef SK_HAS_ZLIB |
| 100 if (skip_compression(catalog->getDocumentFlags())) { |
| 101 this->emitFast(stream, catalog); |
| 102 return; |
| 103 } |
| 104 |
| 105 // Write to a temporary buffer to get the compressed length. |
| 106 SkDynamicMemoryWStream buffer; |
| 107 SkDeflateWStream deflateWStream(&buffer); |
| 108 pmcolor_alpha_to_a8(fBitmap, &deflateWStream); |
| 109 deflateWStream.finalize(); // call before detachAsStream(). |
| 110 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 111 |
| 112 if (is_better_compressed(asset, fBitmap)) { |
| 113 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
| 114 pdf_stream_begin(stream); |
| 115 stream->writeStream(asset.get(), asset->getLength()); |
| 116 pdf_stream_end(stream); |
| 117 } else { |
| 118 this->emitFast(stream, catalog); |
| 119 } |
| 120 #else |
| 121 this->emitFast(stream, catalog); |
| 122 #endif |
| 123 } |
| 124 void PDFAlphaBitmap::emitFast(SkWStream* stream, |
| 125 SkPDFCatalog* catalog) const { |
| 126 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); |
| 127 pdf_stream_begin(stream); |
| 128 pmcolor_alpha_to_a8(fBitmap, stream); |
| 129 pdf_stream_end(stream); |
| 130 } |
| 131 |
| 132 void PDFAlphaBitmap::emitDict(SkWStream* stream, |
| 133 SkPDFCatalog* catalog, |
| 134 size_t length, |
| 135 bool deflate) const { |
| 136 SkPDFDict pdfDict("Xobject"); |
| 137 pdfDict.insertName("Subtype", "Image"); |
| 138 pdfDict.insertInt("Width", fBitmap.width()); |
| 139 pdfDict.insertInt("Height", fBitmap.height()); |
| 140 pdfDict.insertName("ColorSpace", "DeviceGray"); |
| 141 pdfDict.insertInt("BitsPerComponent", 8); |
| 142 pdfDict.insertInt("Length", length); |
| 143 if (deflate) { |
| 144 pdfDict.insertName("Filter", "FlateDecode"); |
| 145 } |
| 146 pdfDict.emitObject(stream, catalog); |
| 147 } |
| 148 } // namespace |
| 149 |
| 150 //////////////////////////////////////////////////////////////////////////////// |
| 151 |
| 152 void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet, |
| 153 SkPDFCatalog* catalog) const { |
| 154 if (fSMask.get()) { |
| 155 resourceSet->add(fSMask.get()); |
| 156 } |
| 157 } |
| 158 |
| 159 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| 160 SkASSERT(kN32_SkColorType == fBitmap.colorType()); |
| 161 SkAutoLockPixels autoLockPixels(fBitmap); |
| 162 SkASSERT(fBitmap.getPixels()); |
| 163 |
| 164 #ifdef SK_HAS_ZLIB |
| 165 if (skip_compression(catalog->getDocumentFlags())) { |
| 166 this->emitFast(stream, catalog); |
| 167 return; |
| 168 } |
| 169 // Write to a temporary buffer to get the compressed length. |
| 170 SkDynamicMemoryWStream buffer; |
| 171 SkDeflateWStream deflateWStream(&buffer); |
| 172 pmcolor_to_rgb24(fBitmap, &deflateWStream); |
| 173 deflateWStream.finalize(); // call before detachAsStream(). |
| 174 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 175 |
| 176 if (is_better_compressed(asset, fBitmap)) { |
| 177 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
| 178 pdf_stream_begin(stream); |
| 179 stream->writeStream(asset.get(), asset->getLength()); |
| 180 pdf_stream_end(stream); |
| 181 } else { |
| 182 // probably a very small bitmap: output_pmcolor() is cheap. |
| 183 this->emitFast(stream, catalog); |
| 184 } |
| 185 #else |
| 186 this->emitFast(stream, catalog); |
| 187 #endif |
| 188 } |
| 189 |
| 190 void SkPDFBitmap::emitFast(SkWStream* stream, SkPDFCatalog* catalog) const { |
| 191 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), |
| 192 /*deflate=*/false); |
| 193 pdf_stream_begin(stream); |
| 194 pmcolor_to_rgb24(fBitmap, stream); |
| 195 pdf_stream_end(stream); |
| 196 } |
| 197 |
| 198 void SkPDFBitmap::emitDict(SkWStream* stream, |
| 199 SkPDFCatalog* catalog, |
| 200 size_t length, |
| 201 bool deflate) const { |
| 202 SkPDFDict pdfDict("Xobject"); |
| 203 pdfDict.insertName("Subtype", "Image"); |
| 204 pdfDict.insertInt("Width", fBitmap.width()); |
| 205 pdfDict.insertInt("Height", fBitmap.height()); |
| 206 pdfDict.insertName("ColorSpace", "DeviceRGB"); |
| 207 pdfDict.insertInt("BitsPerComponent", 8); |
| 208 if (fSMask) { |
| 209 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); |
| 210 } |
| 211 pdfDict.insertInt("Length", length); |
| 212 if (deflate) { |
| 213 pdfDict.insertName("Filter", "FlateDecode"); |
| 214 } |
| 215 pdfDict.emitObject(stream, catalog); |
| 216 } |
| 217 |
| 218 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask) |
| 219 : fBitmap(bm), fSMask(smask) {} |
| 220 |
| 221 SkPDFBitmap::~SkPDFBitmap() { |
| 222 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex()); |
| 223 SkPDFCanon::GetCanon().removeBitmap(this); |
| 224 } |
| 225 |
| 226 //////////////////////////////////////////////////////////////////////////////// |
| 227 |
| 228 // TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter. |
| 229 SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap, |
| 230 const SkIRect& subset) { |
| 231 if (kN32_SkColorType != bitmap.colorType()) { |
| 232 // TODO(halcanary): support other colortypes. |
| 233 return NULL; |
| 234 } |
| 235 SkBitmap bm; |
| 236 // Should extractSubset be done by the SkPDFDevice? |
| 237 if (!bitmap.extractSubset(&bm, subset)) { |
| 238 return NULL; |
| 239 } |
| 240 if (bm.drawsNothing()) { |
| 241 return NULL; |
| 242 } |
| 243 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex()); |
| 244 SkPDFCanon& canon = SkPDFCanon::GetCanon(); |
| 245 SkPDFBitmap* pdfBitmap = canon.findBitmap(bm); |
| 246 if (pdfBitmap) { |
| 247 return SkRef(pdfBitmap); |
| 248 } |
| 249 SkPDFObject* smask = NULL; |
| 250 if (!bitmap.isOpaque() && !SkBitmap::ComputeIsOpaque(bitmap)) { |
| 251 // PDFAlphaBitmap do not get directly canonicalized (they |
| 252 // are refed by the SkPDFBitmap). |
| 253 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); |
| 254 } |
| 255 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); |
| 256 canon.addBitmap(pdfBitmap); |
| 257 return pdfBitmap; |
| 258 } |
OLD | NEW |