Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkColorPriv.h" | 8 #include "SkColorPriv.h" |
| 9 #include "SkDeflateWStream.h" | 9 #include "SkDeflateWStream.h" |
| 10 #include "SkPDFBitmap.h" | 10 #include "SkPDFBitmap.h" |
| 11 #include "SkPDFCanon.h" | 11 #include "SkPDFCanon.h" |
| 12 #include "SkPDFCatalog.h" | 12 #include "SkPDFCatalog.h" |
| 13 #include "SkPDFDocument.h" | 13 #include "SkPDFDocument.h" |
| 14 #include "SkStream.h" | 14 #include "SkStream.h" |
| 15 #include "SkUnPreMultiply.h" | 15 #include "SkUnPreMultiply.h" |
| 16 | 16 |
| 17 //////////////////////////////////////////////////////////////////////////////// | 17 //////////////////////////////////////////////////////////////////////////////// |
| 18 | 18 |
| 19 static void pdf_stream_begin(SkWStream* stream) { | 19 static void pdf_stream_begin(SkWStream* stream) { |
| 20 static const char streamBegin[] = " stream\n"; | 20 static const char streamBegin[] = " stream\n"; |
| 21 stream->write(streamBegin, strlen(streamBegin)); | 21 stream->write(streamBegin, strlen(streamBegin)); |
| 22 } | 22 } |
| 23 | 23 |
| 24 static void pdf_stream_end(SkWStream* stream) { | 24 static void pdf_stream_end(SkWStream* stream) { |
| 25 static const char streamEnd[] = "\nendstream"; | 25 static const char streamEnd[] = "\nendstream"; |
| 26 stream->write(streamEnd, strlen(streamEnd)); | 26 stream->write(streamEnd, strlen(streamEnd)); |
| 27 } | 27 } |
| 28 | 28 |
| 29 static size_t pixel_count(const SkBitmap& bm) { | 29 //////////////////////////////////////////////////////////////////////////////// |
| 30 return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); | |
| 31 } | |
| 32 | 30 |
| 33 // write a single byte to a stream n times. | 31 // write a single byte to a stream n times. |
| 34 static void fill_stream(SkWStream* out, char value, size_t n) { | 32 static void fill_stream(SkWStream* out, char value, size_t n) { |
| 35 char buffer[4096]; | 33 char buffer[4096]; |
| 36 memset(buffer, value, sizeof(buffer)); | 34 memset(buffer, value, sizeof(buffer)); |
| 37 while (n) { | 35 while (n) { |
| 38 size_t k = SkTMin(n, sizeof(buffer)); | 36 size_t k = SkTMin(n, sizeof(buffer)); |
| 39 out->write(buffer, k); | 37 out->write(buffer, k); |
| 40 n -= k; | 38 n -= k; |
| 41 } | 39 } |
| 42 } | 40 } |
| 43 | 41 |
| 44 static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap, | 42 static void to_rgb24(SkPMColor color, U8CPU alpha, uint8_t* rgb) { |
|
mtklein
2015/02/23 14:26:52
This seems weird. Why do we pass alpha twice? It
hal.canary
2015/03/20 01:09:45
I didn't want to recalculate it, but that was too
| |
| 45 int xOrig, | 43 uint32_t s = SkUnPreMultiply::GetScale(alpha); |
| 46 int yOrig) { | 44 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); |
| 47 SkASSERT(kN32_SkColorType == bitmap.colorType()); | 45 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); |
| 48 SkASSERT(bitmap.getPixels()); | 46 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); |
| 49 uint8_t count = 0; | 47 } |
| 50 unsigned r = 0; | 48 |
| 51 unsigned g = 0; | 49 static void to_rgb24(SkPMColor color, uint8_t* rgb) { |
|
mtklein
2015/02/23 14:26:52
This is for the alpha == 0xFF case? Let's at leas
hal.canary
2015/03/20 01:09:45
this is removed.
| |
| 52 unsigned b = 0; | 50 rgb[0] = SkGetPackedR32(color); |
| 53 for (int y = yOrig - 1; y <= yOrig + 1; ++y) { | 51 rgb[1] = SkGetPackedG32(color); |
| 54 if (y < 0 || y >= bitmap.height()) { | 52 rgb[2] = SkGetPackedB32(color); |
| 55 continue; | 53 } |
| 56 } | 54 |
| 57 uint32_t* src = bitmap.getAddr32(0, y); | 55 static SkPMColor get_pmcolor(const SkBitmap& bm, int x, int y) { |
| 58 for (int x = xOrig - 1; x <= xOrig + 1; ++x) { | 56 switch (bm.colorType()) { |
| 59 if (x < 0 || x >= bitmap.width()) { | 57 case kARGB_4444_SkColorType: |
| 60 continue; | 58 return SkPixel4444ToPixel32(*(bm.getAddr16(x, y))); |
| 61 } | 59 case kN32_SkColorType: |
| 62 SkPMColor pmColor = src[x]; | 60 return *(bm.getAddr32(x, y)); |
| 61 default: | |
| 62 SkASSERT(false); | |
|
mtklein
2015/02/23 14:26:52
// We only use this to sample around transparent p
hal.canary
2015/03/20 01:09:46
Done.
| |
| 63 return 0; | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 /* It is necessary to average the color component of transparent | |
| 68 pixels with their surrounding neighbors since the PDF renderer may | |
| 69 separately re-sample the alpha and color channels when the image is | |
| 70 not displayed at its native resolution. Since an alpha of zero | |
| 71 gives no information about the color component, the pathological | |
| 72 case is a white image with sharp transparency bounds - the color | |
| 73 channel goes to black, and the should-be-transparent pixels are | |
| 74 rendered as grey because of the separate soft mask and color | |
| 75 resizing. e.g.: gm/bitmappremul.cpp */ | |
| 76 static SkPMColor get_neighbor_avg_color(const SkBitmap& bm, | |
| 77 int xOrig, | |
| 78 int yOrig) { | |
| 79 SkASSERT(kARGB_4444_SkColorType == bm.colorType() || | |
| 80 kN32_SkColorType == bm.colorType()); | |
| 81 uint8_t n = 0; | |
|
mtklein
2015/02/23 14:26:53
This seems... unnecessarily precise? unsigned see
hal.canary
2015/03/20 01:09:45
Done.
| |
| 82 unsigned r = 0, g = 0, b = 0; | |
| 83 int yrange[2] = {SkTMax(yOrig - 1, 0), SkTMin(bm.height(), yOrig + 2)}; | |
|
mtklein
2015/02/23 14:26:53
Might want to swap the orders of the arguments to
hal.canary
2015/03/20 01:09:45
<= is unexpected in C.
| |
| 84 int xrange[2] = {SkTMax(xOrig - 1, 0), SkTMin(bm.width(), xOrig + 2)}; | |
| 85 for (int y = yrange[0]; y < yrange[1]; ++y) { | |
| 86 for (int x = xrange[0]; x < xrange[1]; ++x) { | |
|
mtklein
2015/02/23 14:26:53
Seems like when there are a lot of transparent pix
hal.canary
2015/03/20 01:09:45
I thinks so. I tried to think of how to do this i
| |
| 87 SkPMColor pmColor = get_pmcolor(bm, x, y); | |
| 63 U8CPU alpha = SkGetPackedA32(pmColor); | 88 U8CPU alpha = SkGetPackedA32(pmColor); |
| 64 if (alpha != SK_AlphaTRANSPARENT) { | 89 if (alpha != SK_AlphaTRANSPARENT) { |
| 65 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 90 uint8_t rgb[3]; |
| 66 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); | 91 to_rgb24(pmColor, alpha, rgb); |
| 67 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); | 92 r += rgb[0]; |
| 68 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); | 93 g += rgb[1]; |
| 69 ++count; | 94 b += rgb[2]; |
| 70 } | 95 ++n; |
| 71 } | 96 } |
| 72 } | 97 } |
| 73 if (count == 0) { | 98 } |
| 74 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | 99 return (n > 0) ? SkPackARGB32NoCheck(SK_AlphaOPAQUE, r / n, g / n, b / n) |
| 100 : SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | |
| 101 } | |
| 102 | |
| 103 static void convert_pixel( | |
| 104 const SkBitmap& bm, int x, int y, SkPMColor color, uint8_t* dst) { | |
| 105 U8CPU alpha = SkGetPackedA32(color); | |
| 106 if (alpha != SK_AlphaTRANSPARENT) { | |
| 107 to_rgb24(color, alpha, dst); | |
| 75 } else { | 108 } else { |
| 76 return SkPackARGB32NoCheck( | 109 to_rgb24(get_neighbor_avg_color(bm, x, y), dst); |
| 77 SK_AlphaOPAQUE, r / count, g / count, b / count); | 110 } |
| 78 } | 111 } |
| 79 } | 112 |
| 80 | 113 static void convert_scanline(const SkBitmap& bm, int y, uint8_t* dst) { |
| 81 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) { | 114 switch (bm.colorType()) { |
| 82 SkASSERT(kN32_SkColorType == bm.colorType()); | 115 case kARGB_4444_SkColorType: { |
| 116 const uint16_t* src = bm.getAddr16(0, y); | |
| 117 for (int x = 0; x < bm.width(); ++x) { | |
| 118 convert_pixel(bm, x, y, SkPixel4444ToPixel32(*src++), dst); | |
| 119 dst += 3; | |
| 120 } | |
| 121 return; | |
| 122 } | |
| 123 case kN32_SkColorType: { | |
| 124 const SkPMColor* src = bm.getAddr32(0, y); | |
| 125 for (int x = 0; x < bm.width(); ++x) { | |
| 126 convert_pixel(bm, x, y, *src++, dst); | |
| 127 dst += 3; | |
| 128 } | |
| 129 return; | |
| 130 } | |
| 131 case kRGB_565_SkColorType: { | |
| 132 const uint16_t* src = bm.getAddr16(0, y); | |
| 133 for (int x = 0; x < bm.width(); ++x) { | |
| 134 U16CPU color565 = *src++; | |
| 135 *dst++ = SkPacked16ToR32(color565); | |
| 136 *dst++ = SkPacked16ToG32(color565); | |
| 137 *dst++ = SkPacked16ToB32(color565); | |
| 138 } | |
| 139 return; | |
| 140 } | |
| 141 default: | |
| 142 SkASSERT(false); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 static size_t pixel_count(const SkBitmap& bm) { | |
| 147 return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); | |
| 148 } | |
| 149 | |
| 150 static void bitmap_to_rgb24(const SkBitmap& bm, SkWStream* out) { | |
| 83 if (!bm.getPixels()) { | 151 if (!bm.getPixels()) { |
| 84 fill_stream(out, '\xFF', 3 * pixel_count(bm)); | 152 fill_stream(out, '\xFF', 3 * pixel_count(bm)); |
| 85 return; | 153 return; |
| 86 } | 154 } |
| 87 size_t scanlineLength = 3 * bm.width(); | 155 switch (bm.colorType()) { |
| 88 SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 156 case kN32_SkColorType: |
| 157 case kARGB_4444_SkColorType: | |
| 158 case kRGB_565_SkColorType: { | |
| 159 SkAutoTMalloc<uint8_t> scanline(3 * bm.width()); | |
| 160 for (int y = 0; y < bm.height(); ++y) { | |
| 161 convert_scanline(bm, y, scanline.get()); | |
| 162 out->write(scanline.get(), 3 * bm.width()); | |
| 163 } | |
| 164 return; | |
| 165 } | |
| 166 case kAlpha_8_SkColorType: | |
| 167 fill_stream(out, '\x00', 3 * pixel_count(bm)); | |
| 168 return; | |
| 169 case kIndex_8_SkColorType: | |
| 170 for (int y = 0; y < bm.height(); ++y) { | |
| 171 out->write(bm.getAddr8(0, y), bm.width()); | |
| 172 } // not actually rgb24 format. | |
|
mtklein
2015/02/23 14:26:53
Let's explain what it is rather than what it's not
hal.canary
2015/03/20 01:09:45
Done.
| |
| 173 return; | |
| 174 case kUnknown_SkColorType: | |
| 175 default: | |
| 176 SkASSERT(false); | |
| 177 return; | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 //////////////////////////////////////////////////////////////////////////////// | |
| 182 | |
| 183 static void convert_scanline_alpha(const SkBitmap& bm, int y, uint8_t* dst) { | |
| 184 switch (bm.colorType()) { | |
| 185 case kARGB_4444_SkColorType: { | |
| 186 const uint16_t* src = bm.getAddr16(0, y); | |
| 187 for (int x = 0; x < bm.width(); ++x) { | |
| 188 *dst++ = SkPacked4444ToA32(*src++); | |
| 189 } | |
| 190 return; | |
| 191 } | |
| 192 case kN32_SkColorType: { | |
| 193 const SkPMColor* src = bm.getAddr32(0, y); | |
| 194 for (int x = 0; x < bm.width(); ++x) { | |
| 195 *dst++ = SkGetPackedA32(*src++); | |
| 196 } | |
| 197 return; | |
| 198 } | |
| 199 default: | |
| 200 SkASSERT(false); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 static void index8_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { | |
| 205 SkASSERT(kIndex_8_SkColorType == bm.colorType()); | |
| 206 SkColorTable* ct = bm.getColorTable(); | |
| 207 if (!ct) { | |
| 208 fill_stream(out, '\x00', pixel_count(bm)); | |
|
mtklein
2015/02/23 14:26:53
It seems like it's worth promoting this up to an a
hal.canary
2015/03/20 01:09:45
Done.
| |
| 209 return; | |
| 210 } | |
| 211 SkAutoTMalloc<uint8_t> scanline(bm.width()); | |
| 89 for (int y = 0; y < bm.height(); ++y) { | 212 for (int y = 0; y < bm.height(); ++y) { |
| 90 uint8_t* dst = scanline.get(); | 213 uint8_t* dst = scanline.get(); |
| 91 const SkPMColor* src = bm.getAddr32(0, y); | 214 const uint8_t* src = bm.getAddr8(0, y); |
| 92 for (int x = 0; x < bm.width(); ++x) { | 215 for (int x = 0; x < bm.width(); ++x) { |
| 93 SkPMColor color = *src++; | 216 *dst++ = SkGetPackedA32((*ct)[*src++]); |
| 94 U8CPU alpha = SkGetPackedA32(color); | 217 } |
| 95 if (alpha != SK_AlphaTRANSPARENT) { | 218 out->write(scanline.get(), bm.width()); |
| 96 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 219 } |
| 97 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); | 220 } |
| 98 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); | 221 |
| 99 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); | 222 static void bitmap_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { |
| 100 } else { | |
| 101 /* It is necessary to average the color component of | |
| 102 transparent pixels with their surrounding neighbors | |
| 103 since the PDF renderer may separately re-sample the | |
| 104 alpha and color channels when the image is not | |
| 105 displayed at its native resolution. Since an alpha | |
| 106 of zero gives no information about the color | |
| 107 component, the pathological case is a white image | |
| 108 with sharp transparency bounds - the color channel | |
| 109 goes to black, and the should-be-transparent pixels | |
| 110 are rendered as grey because of the separate soft | |
| 111 mask and color resizing. e.g.: gm/bitmappremul.cpp */ | |
| 112 color = get_pmcolor_neighbor_avg_color(bm, x, y); | |
| 113 *dst++ = SkGetPackedR32(color); | |
| 114 *dst++ = SkGetPackedG32(color); | |
| 115 *dst++ = SkGetPackedB32(color); | |
| 116 } | |
| 117 } | |
| 118 out->write(scanline.get(), scanlineLength); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { | |
| 123 SkASSERT(kN32_SkColorType == bm.colorType()); | |
| 124 if (!bm.getPixels()) { | 223 if (!bm.getPixels()) { |
| 125 fill_stream(out, '\xFF', pixel_count(bm)); | 224 fill_stream(out, '\xFF', pixel_count(bm)); |
| 126 return; | 225 return; |
| 127 } | 226 } |
| 128 size_t scanlineLength = bm.width(); | 227 switch (bm.colorType()) { |
| 129 SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 228 case kN32_SkColorType: |
| 130 for (int y = 0; y < bm.height(); ++y) { | 229 case kARGB_4444_SkColorType: { |
| 131 uint8_t* dst = scanline.get(); | 230 SkAutoTMalloc<uint8_t> scanline(bm.width()); |
| 132 const SkPMColor* src = bm.getAddr32(0, y); | 231 for (int y = 0; y < bm.height(); ++y) { |
| 133 for (int x = 0; x < bm.width(); ++x) { | 232 convert_scanline_alpha(bm, y, scanline.get()); |
| 134 *dst++ = SkGetPackedA32(*src++); | 233 out->write(scanline.get(), bm.width()); |
| 135 } | 234 } |
| 136 out->write(scanline.get(), scanlineLength); | 235 return; |
| 137 } | 236 } |
| 138 } | 237 case kAlpha_8_SkColorType: |
| 139 | 238 for (int y = 0; y < bm.height(); ++y) { |
| 239 out->write(bm.getAddr8(0, y), bm.width()); | |
| 240 } | |
| 241 return; | |
| 242 case kIndex_8_SkColorType: | |
| 243 index8_alpha_to_a8(bm, out); | |
| 244 return; | |
| 245 case kRGB_565_SkColorType: | |
| 246 case kUnknown_SkColorType: | |
| 247 default: | |
| 248 SkASSERT(false); | |
| 249 return; | |
| 250 } | |
| 251 } | |
| 252 | |
| 140 //////////////////////////////////////////////////////////////////////////////// | 253 //////////////////////////////////////////////////////////////////////////////// |
| 141 | 254 |
| 142 namespace { | 255 namespace { |
| 143 // This SkPDFObject only outputs the alpha layer of the given bitmap. | 256 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
| 144 class PDFAlphaBitmap : public SkPDFObject { | 257 class PDFAlphaBitmap : public SkPDFObject { |
| 145 public: | 258 public: |
| 146 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | 259 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} |
| 147 ~PDFAlphaBitmap() {} | 260 ~PDFAlphaBitmap() {} |
| 148 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; | 261 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; |
| 149 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} | 262 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} |
| 150 | 263 |
| 151 private: | 264 private: |
| 152 const SkBitmap fBitmap; | 265 const SkBitmap fBitmap; |
| 153 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; | 266 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; |
| 154 }; | 267 }; |
| 155 | 268 |
| 156 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 269 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| 157 SkAutoLockPixels autoLockPixels(fBitmap); | 270 SkAutoLockPixels autoLockPixels(fBitmap); |
| 158 | 271 |
| 159 #ifndef SK_NO_FLATE | 272 #ifndef SK_NO_FLATE |
| 160 // Write to a temporary buffer to get the compressed length. | 273 // Write to a temporary buffer to get the compressed length. |
| 161 SkDynamicMemoryWStream buffer; | 274 SkDynamicMemoryWStream buffer; |
| 162 SkDeflateWStream deflateWStream(&buffer); | 275 SkDeflateWStream deflateWStream(&buffer); |
| 163 pmcolor_alpha_to_a8(fBitmap, &deflateWStream); | 276 bitmap_alpha_to_a8(fBitmap, &deflateWStream); |
| 164 deflateWStream.finalize(); // call before detachAsStream(). | 277 deflateWStream.finalize(); // call before detachAsStream(). |
| 165 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 278 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 166 | 279 |
| 167 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 280 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
| 168 pdf_stream_begin(stream); | 281 pdf_stream_begin(stream); |
| 169 stream->writeStream(asset.get(), asset->getLength()); | 282 stream->writeStream(asset.get(), asset->getLength()); |
| 170 pdf_stream_end(stream); | 283 pdf_stream_end(stream); |
| 171 #else | 284 #else |
| 172 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); | 285 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); |
| 173 pdf_stream_begin(stream); | 286 pdf_stream_begin(stream); |
| 174 pmcolor_alpha_to_a8(fBitmap, stream); | 287 bitmap_alpha_to_a8(fBitmap, stream); |
| 175 pdf_stream_end(stream); | 288 pdf_stream_end(stream); |
| 176 #endif // SK_NO_FLATE | 289 #endif // SK_NO_FLATE |
| 177 } | 290 } |
| 178 | 291 |
| 179 void PDFAlphaBitmap::emitDict(SkWStream* stream, | 292 void PDFAlphaBitmap::emitDict(SkWStream* stream, |
| 180 SkPDFCatalog* catalog, | 293 SkPDFCatalog* catalog, |
| 181 size_t length, | 294 size_t length, |
| 182 bool deflate) const { | 295 bool deflate) const { |
| 183 SkPDFDict pdfDict("XObject"); | 296 SkPDFDict pdfDict("XObject"); |
| 184 pdfDict.insertName("Subtype", "Image"); | 297 pdfDict.insertName("Subtype", "Image"); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 203 } | 316 } |
| 204 } | 317 } |
| 205 | 318 |
| 206 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 319 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| 207 SkAutoLockPixels autoLockPixels(fBitmap); | 320 SkAutoLockPixels autoLockPixels(fBitmap); |
| 208 | 321 |
| 209 #ifndef SK_NO_FLATE | 322 #ifndef SK_NO_FLATE |
| 210 // Write to a temporary buffer to get the compressed length. | 323 // Write to a temporary buffer to get the compressed length. |
| 211 SkDynamicMemoryWStream buffer; | 324 SkDynamicMemoryWStream buffer; |
| 212 SkDeflateWStream deflateWStream(&buffer); | 325 SkDeflateWStream deflateWStream(&buffer); |
| 213 pmcolor_to_rgb24(fBitmap, &deflateWStream); | 326 bitmap_to_rgb24(fBitmap, &deflateWStream); |
| 214 deflateWStream.finalize(); // call before detachAsStream(). | 327 deflateWStream.finalize(); // call before detachAsStream(). |
| 215 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 328 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| 216 | 329 |
| 217 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 330 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
| 218 pdf_stream_begin(stream); | 331 pdf_stream_begin(stream); |
| 219 stream->writeStream(asset.get(), asset->getLength()); | 332 stream->writeStream(asset.get(), asset->getLength()); |
| 220 pdf_stream_end(stream); | 333 pdf_stream_end(stream); |
| 221 #else | 334 #else |
| 222 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false) ; | 335 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false) ; |
| 223 pdf_stream_begin(stream); | 336 pdf_stream_begin(stream); |
| 224 pmcolor_to_rgb24(fBitmap, stream); | 337 bitmap_to_rgb24(fBitmap, stream); |
| 225 pdf_stream_end(stream); | 338 pdf_stream_end(stream); |
| 226 return; | 339 return; |
| 227 #endif // SK_NO_FLATE | 340 #endif // SK_NO_FLATE |
| 228 } | 341 } |
| 229 | 342 |
| 343 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { | |
| 344 SkPDFArray* result = SkNEW(SkPDFArray); | |
| 345 result->reserve(4); | |
| 346 result->appendName("Indexed"); | |
| 347 result->appendName("DeviceRGB"); | |
| 348 SkASSERT(table); | |
| 349 result->appendInt(table->count() - 1); | |
| 350 | |
| 351 // Potentially, this could be represented in fewer bytes with a stream. | |
| 352 // Max size as a string is 1.5k. | |
|
mtklein
2015/02/23 14:26:53
Some sort of assert here then?
hal.canary
2015/03/20 01:09:45
Done.
| |
| 353 SkString index; | |
| 354 for (int i = 0; i < table->count(); i++) { | |
| 355 SkPMColor color = (*table)[i]; | |
| 356 U8CPU alpha = SkGetPackedA32(color); | |
| 357 uint8_t rgb[3]; | |
| 358 to_rgb24(color, alpha, rgb); | |
| 359 index.append((char*)rgb, 3); | |
| 360 } | |
| 361 result->append(new SkPDFString(index))->unref(); | |
| 362 return result; | |
| 363 } | |
| 364 | |
| 230 void SkPDFBitmap::emitDict(SkWStream* stream, | 365 void SkPDFBitmap::emitDict(SkWStream* stream, |
| 231 SkPDFCatalog* catalog, | 366 SkPDFCatalog* catalog, |
| 232 size_t length, | 367 size_t length, |
| 233 bool deflate) const { | 368 bool deflate) const { |
| 234 SkPDFDict pdfDict("XObject"); | 369 SkPDFDict pdfDict("XObject"); |
| 235 pdfDict.insertName("Subtype", "Image"); | 370 pdfDict.insertName("Subtype", "Image"); |
| 236 pdfDict.insertInt("Width", fBitmap.width()); | 371 pdfDict.insertInt("Width", fBitmap.width()); |
| 237 pdfDict.insertInt("Height", fBitmap.height()); | 372 pdfDict.insertInt("Height", fBitmap.height()); |
| 238 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 373 if (fBitmap.colorType() != kIndex_8_SkColorType) { |
| 374 pdfDict.insertName("ColorSpace", "DeviceRGB"); | |
| 375 } else { | |
| 376 pdfDict.insert("ColorSpace", make_indexed_color_space( | |
| 377 fBitmap.getColorTable()))->unref(); | |
| 378 } | |
| 239 pdfDict.insertInt("BitsPerComponent", 8); | 379 pdfDict.insertInt("BitsPerComponent", 8); |
| 240 if (fSMask) { | 380 if (fSMask) { |
| 241 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); | 381 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); |
| 242 } | 382 } |
| 243 if (deflate) { | 383 if (deflate) { |
| 244 pdfDict.insertName("Filter", "FlateDecode"); | 384 pdfDict.insertName("Filter", "FlateDecode"); |
| 245 } | 385 } |
| 246 pdfDict.insertInt("Length", length); | 386 pdfDict.insertInt("Length", length); |
| 247 pdfDict.emitObject(stream, catalog); | 387 pdfDict.emitObject(stream, catalog); |
| 248 } | 388 } |
| 249 | 389 |
| 250 SkPDFBitmap::SkPDFBitmap(SkPDFCanon* canon, | 390 SkPDFBitmap::SkPDFBitmap(SkPDFCanon* canon, |
| 251 const SkBitmap& bm, | 391 const SkBitmap& bm, |
| 252 SkPDFObject* smask) | 392 SkPDFObject* smask) |
| 253 : fCanon(canon), fBitmap(bm), fSMask(smask) {} | 393 : fCanon(canon), fBitmap(bm), fSMask(smask) {} |
| 254 | 394 |
| 255 SkPDFBitmap::~SkPDFBitmap() { fCanon->removeBitmap(this); } | 395 SkPDFBitmap::~SkPDFBitmap() { fCanon->removeBitmap(this); } |
| 256 | 396 |
| 257 //////////////////////////////////////////////////////////////////////////////// | 397 //////////////////////////////////////////////////////////////////////////////// |
| 258 static bool is_transparent(const SkBitmap& bm) { | 398 |
| 259 SkAutoLockPixels autoLockPixels(bm); | 399 static bool is_transparent_pixels(const SkBitmap& bm) { |
|
mtklein
2015/02/23 14:26:52
pixels_are_transparent?
hal.canary
2015/03/20 01:09:45
I've stoped this check. Why lock the bitmap unnec
| |
| 260 if (NULL == bm.getPixels()) { | |
| 261 return true; | |
| 262 } | |
| 263 SkASSERT(kN32_SkColorType == bm.colorType()); | |
| 264 for (int y = 0; y < bm.height(); ++y) { | 400 for (int y = 0; y < bm.height(); ++y) { |
| 265 U8CPU alpha = 0; | 401 U8CPU alpha = 0; |
| 266 const SkPMColor* src = bm.getAddr32(0, y); | 402 switch (bm.colorType()) { |
| 267 for (int x = 0; x < bm.width(); ++x) { | 403 case kN32_SkColorType: { |
| 268 alpha |= SkGetPackedA32(*src++); | 404 const SkPMColor* src = bm.getAddr32(0, y); |
| 405 for (int x = 0; x < bm.width(); ++x) { | |
| 406 alpha |= SkGetPackedA32(*src++); | |
|
mtklein
2015/02/23 14:26:53
Why don't we just bail out the first time we see a
hal.canary
2015/03/20 01:09:45
Acknowledged.
| |
| 407 } | |
| 408 break; | |
| 409 } | |
| 410 case kARGB_4444_SkColorType: { | |
| 411 const uint16_t* src = bm.getAddr16(0, y); | |
| 412 for (int x = 0; x < bm.width(); ++x) { | |
| 413 alpha |= SkPacked4444ToA32(*src++); | |
| 414 } | |
| 415 break; | |
| 416 } | |
| 417 case kAlpha_8_SkColorType: { | |
| 418 const uint8_t* src = bm.getAddr8(0, y); | |
| 419 for (int x = 0; x < bm.width(); ++x) { | |
| 420 alpha |= *src++; | |
| 421 } | |
| 422 break; | |
| 423 } | |
| 424 default: | |
| 425 SkASSERT(false); | |
| 269 } | 426 } |
| 270 if (alpha) { | 427 if (alpha) { |
| 271 return false; | 428 return false; |
| 272 } | 429 } |
| 273 } | 430 } |
| 274 return true; | 431 return true; |
| 275 } | 432 } |
| 276 | 433 |
| 434 static bool is_transparent(const SkBitmap& bm) { | |
| 435 SkAutoLockPixels autoLockPixels(bm); | |
| 436 if (NULL == bm.getPixels()) { | |
| 437 return true; | |
| 438 } | |
| 439 switch (bm.colorType()) { | |
| 440 case kN32_SkColorType: | |
| 441 case kARGB_4444_SkColorType: | |
| 442 case kAlpha_8_SkColorType: | |
| 443 return is_transparent_pixels(bm); | |
| 444 case kIndex_8_SkColorType: { | |
| 445 const SkColorTable* ct = bm.getColorTable(); | |
| 446 if (!ct) { | |
| 447 return true; | |
| 448 } | |
| 449 for (int y = 0; y < bm.height(); ++y) { | |
| 450 U8CPU alpha = 0; | |
| 451 const uint8_t* src = bm.getAddr8(0, y); | |
| 452 for (int x = 0; x < bm.width(); ++x) { | |
| 453 alpha |= SkGetPackedA32((*ct)[*src++]); | |
|
mtklein
2015/02/23 14:26:52
Same question?
hal.canary
2015/03/20 01:09:45
Acknowledged.
| |
| 454 } | |
| 455 if (alpha) { | |
| 456 return false; | |
| 457 } | |
| 458 } | |
| 459 return true; | |
| 460 } | |
| 461 default: | |
| 462 SkASSERT(false); | |
| 463 return false; | |
| 464 } | |
| 465 } | |
| 466 | |
| 277 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, | 467 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, |
| 278 const SkBitmap& bitmap, | 468 const SkBitmap& bitmap, |
| 279 const SkIRect& subset) { | 469 const SkIRect& subset) { |
| 280 SkASSERT(canon); | 470 SkASSERT(canon); |
| 281 if (kN32_SkColorType != bitmap.colorType()) { | 471 switch (bitmap.colorType()) { |
| 282 // TODO(halcanary): support other colortypes. | 472 case kN32_SkColorType: |
| 283 return NULL; | 473 case kRGB_565_SkColorType: |
| 474 case kARGB_4444_SkColorType: | |
| 475 case kAlpha_8_SkColorType: | |
| 476 case kIndex_8_SkColorType: | |
| 477 break; | |
| 478 case kUnknown_SkColorType: | |
| 479 default: | |
| 480 return NULL; | |
| 284 } | 481 } |
| 285 SkBitmap bm; | 482 SkBitmap bm; |
| 286 // Should extractSubset be done by the SkPDFDevice? | 483 // Should extractSubset be done by the SkPDFDevice? |
| 287 if (!bitmap.extractSubset(&bm, subset)) { | 484 if (!bitmap.extractSubset(&bm, subset)) { |
| 288 return NULL; | 485 return NULL; |
| 289 } | 486 } |
| 290 if (bm.drawsNothing()) { | 487 if (bm.drawsNothing()) { |
| 291 return NULL; | 488 return NULL; |
| 292 } | 489 } |
| 293 if (!bm.isImmutable()) { | 490 if (!bm.isImmutable()) { |
| 294 SkBitmap copy; | 491 SkBitmap copy; |
| 295 if (!bm.copyTo(©)) { | 492 if (!bm.copyTo(©)) { |
| 296 return NULL; | 493 return NULL; |
| 297 } | 494 } |
| 298 copy.setImmutable(); | 495 copy.setImmutable(); |
| 299 bm = copy; | 496 bm = copy; |
| 300 } | 497 } |
| 301 | 498 |
| 302 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm); | 499 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm); |
| 303 if (pdfBitmap) { | 500 if (pdfBitmap) { |
| 304 return SkRef(pdfBitmap); | 501 return SkRef(pdfBitmap); |
| 305 } | 502 } |
| 306 SkPDFObject* smask = NULL; | 503 SkPDFObject* smask = NULL; |
| 307 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 504 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { |
| 308 if (is_transparent(bm)) { | 505 if (is_transparent(bm)) { |
|
mtklein
2015/02/23 14:26:53
Just curious, do we really see fully transparent b
hal.canary
2015/03/20 01:09:46
Done.
| |
| 309 return NULL; | 506 return NULL; |
| 310 } | 507 } |
| 311 // PDFAlphaBitmaps do not get directly canonicalized (they | 508 // PDFAlphaBitmaps do not get directly canonicalized (they |
| 312 // are refed by the SkPDFBitmap). | 509 // are refed by the SkPDFBitmap). |
| 313 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); | 510 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); |
| 314 } | 511 } |
| 315 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (canon, bm, smask)); | 512 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (canon, bm, smask)); |
| 316 canon->addBitmap(pdfBitmap); | 513 canon->addBitmap(pdfBitmap); |
| 317 return pdfBitmap; | 514 return pdfBitmap; |
| 318 } | 515 } |
| OLD | NEW |