Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2010 The Android Open Source Project | 2 * Copyright 2010 The Android Open Source Project |
| 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 "SkPDFImage.h" | 8 #include "SkPDFImage.h" |
| 9 | 9 |
| 10 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
| 11 #include "SkColor.h" | 11 #include "SkColor.h" |
| 12 #include "SkColorPriv.h" | 12 #include "SkColorPriv.h" |
| 13 #include "SkData.h" | |
| 14 #include "SkFlate.h" | |
| 13 #include "SkPDFCatalog.h" | 15 #include "SkPDFCatalog.h" |
| 14 #include "SkRect.h" | 16 #include "SkRect.h" |
| 15 #include "SkStream.h" | 17 #include "SkStream.h" |
| 16 #include "SkString.h" | 18 #include "SkString.h" |
| 17 #include "SkUnPreMultiply.h" | 19 #include "SkUnPreMultiply.h" |
| 18 | 20 |
| 19 namespace { | 21 static const int kNoColorTransform = 0; |
| 20 | 22 |
| 21 void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, | 23 static bool skip_compression(SkPDFCatalog* catalog) { |
| 22 SkStream** imageData, SkStream** alphaData) { | 24 return SkToBool(catalog->getDocumentFlags() & |
| 23 SkMemoryStream* image = NULL; | 25 SkPDFDocument::kFavorSpeedOverSize_Flags); |
| 24 SkMemoryStream* alpha = NULL; | 26 } |
| 27 | |
| 28 static size_t get_row_bytes(const SkBitmap& bitmap, | |
| 29 const SkIRect& srcRect) { | |
| 30 switch (bitmap.getConfig()) { | |
| 31 case SkBitmap::kIndex8_Config: | |
| 32 return srcRect.width(); | |
| 33 case SkBitmap::kARGB_4444_Config: | |
| 34 return (srcRect.width() * 3 + 1) / 2; | |
| 35 case SkBitmap::kRGB_565_Config: | |
| 36 return srcRect.width() * 3; | |
| 37 case SkBitmap::kARGB_8888_Config: | |
| 38 return srcRect.width() * 3; | |
| 39 case SkBitmap::kA1_Config: | |
| 40 case SkBitmap::kA8_Config: | |
| 41 return 1; | |
| 42 default: | |
| 43 SkASSERT(false); | |
| 44 return 0; | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 static size_t get_uncompressed_size(const SkBitmap& bitmap, | |
| 49 const SkIRect& srcRect) { | |
| 50 switch (bitmap.getConfig()) { | |
| 51 case SkBitmap::kIndex8_Config: | |
| 52 case SkBitmap::kARGB_4444_Config: | |
| 53 case SkBitmap::kRGB_565_Config: | |
| 54 case SkBitmap::kARGB_8888_Config: | |
| 55 return get_row_bytes(bitmap, srcRect) * srcRect.height(); | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
Here and extract_index8_image are the only place t
ducky
2013/08/23 06:59:08
Done.
| |
| 56 case SkBitmap::kA1_Config: | |
| 57 case SkBitmap::kA8_Config: | |
| 58 return 1; | |
| 59 default: | |
| 60 SkASSERT(false); | |
| 61 return 0; | |
| 62 } | |
| 63 } | |
| 64 | |
| 65 static SkStream* extract_index8_image(const SkBitmap& bitmap, | |
| 66 const SkIRect& srcRect) { | |
| 67 const int rowBytes = get_row_bytes(bitmap, srcRect); | |
| 68 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
| 69 (rowBytes * srcRect.height())); | |
| 70 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
| 71 | |
| 72 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 73 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | |
| 74 dst += rowBytes; | |
| 75 } | |
| 76 return stream; | |
| 77 } | |
| 78 | |
| 79 static SkStream* extract_argb4444_data(const SkBitmap& bitmap, | |
| 80 const SkIRect& srcRect, | |
| 81 bool extractAlpha, | |
| 82 bool* hasAlpha, | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
If it doesn't have alpha, or is completely transpa
ducky
2013/08/23 06:59:08
But they're necessary for the optimizations: you n
vandebo (ex-Chrome)
2013/08/23 15:45:35
Ok, but you don't need both of them. I would sugge
ducky
2013/08/23 17:59:50
Both are here so that the final check if transpare
| |
| 83 bool* isTransparent) { | |
| 84 SkStream* stream; | |
| 85 uint8_t* dst = NULL; | |
| 86 if (extractAlpha) { | |
| 87 const int alphaRowBytes = (srcRect.width() + 1) / 2; | |
| 88 stream = SkNEW_ARGS(SkMemoryStream, | |
| 89 (alphaRowBytes * srcRect.height())); | |
| 90 } else { | |
| 91 stream = SkNEW_ARGS(SkMemoryStream, | |
| 92 (get_uncompressed_size(bitmap, srcRect))); | |
| 93 } | |
| 94 dst = (uint8_t*)stream->getMemoryBase(); | |
| 95 | |
| 96 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 97 uint16_t* src = bitmap.getAddr16(0, y); | |
| 98 int x; | |
| 99 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | |
| 100 if (extractAlpha) { | |
| 101 dst[0] = (SkGetPackedA4444(src[x]) << 4) | | |
| 102 SkGetPackedA4444(src[x + 1]); | |
| 103 if (dst[0] != SK_AlphaOPAQUE) { | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
This can probably made more streamlined....
hasAl
ducky
2013/08/23 06:59:08
Nice. Will do.
| |
| 104 *hasAlpha = true; | |
| 105 } | |
| 106 if (dst[0] != SK_AlphaTRANSPARENT) { | |
| 107 *isTransparent = false; | |
| 108 } | |
| 109 dst++; | |
| 110 } else { | |
| 111 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
| 112 SkGetPackedG4444(src[x]); | |
| 113 dst[1] = (SkGetPackedB4444(src[x]) << 4) | | |
| 114 SkGetPackedR4444(src[x + 1]); | |
| 115 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | |
| 116 SkGetPackedB4444(src[x + 1]); | |
| 117 dst += 3; | |
| 118 } | |
| 119 } | |
| 120 if (srcRect.width() & 1) { | |
| 121 if (extractAlpha) { | |
| 122 dst[0] = (SkGetPackedA4444(src[x]) << 4); | |
| 123 if (dst[0] != (SK_AlphaOPAQUE & 0xF0)) { | |
| 124 *hasAlpha = true; | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
A better name for hasAlpha is probably the opposit
ducky
2013/08/23 06:59:08
Ok. It does become more consistent with SkBitmap t
| |
| 125 } | |
| 126 if (dst[0] != (SK_AlphaTRANSPARENT & 0xF0)) { | |
| 127 *isTransparent = false; | |
| 128 } | |
| 129 dst++; | |
| 130 | |
| 131 } else { | |
| 132 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
| 133 SkGetPackedG4444(src[x]); | |
| 134 dst[1] = (SkGetPackedB4444(src[x]) << 4); | |
| 135 dst += 2; | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 return stream; | |
| 140 } | |
| 141 | |
| 142 static SkStream* extract_rgb565_image(const SkBitmap& bitmap, | |
| 143 const SkIRect& srcRect) { | |
| 144 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
| 145 (get_uncompressed_size(bitmap, | |
| 146 srcRect))); | |
| 147 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
| 148 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 149 uint16_t* src = bitmap.getAddr16(0, y); | |
| 150 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 151 dst[0] = SkGetPackedR16(src[x]); | |
| 152 dst[1] = SkGetPackedG16(src[x]); | |
| 153 dst[2] = SkGetPackedB16(src[x]); | |
| 154 dst += 3; | |
| 155 } | |
| 156 } | |
| 157 return stream; | |
| 158 } | |
| 159 | |
| 160 static SkStream* extract_argb8888_data(const SkBitmap& bitmap, | |
| 161 const SkIRect& srcRect, | |
| 162 bool extractAlpha, | |
| 163 bool* hasAlpha, | |
| 164 bool* isTransparent) { | |
| 165 SkStream* stream; | |
| 166 if (extractAlpha) { | |
| 167 stream = SkNEW_ARGS(SkMemoryStream, | |
| 168 (srcRect.width() * srcRect.height())); | |
| 169 } else { | |
| 170 stream = SkNEW_ARGS(SkMemoryStream, | |
| 171 (get_uncompressed_size(bitmap, srcRect))); | |
| 172 } | |
| 173 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
| 174 | |
| 175 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 176 uint32_t* src = bitmap.getAddr32(0, y); | |
| 177 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 178 if (extractAlpha) { | |
| 179 dst[0] = SkGetPackedA32(src[x]); | |
| 180 if (dst[0] != SK_AlphaOPAQUE) { | |
| 181 *hasAlpha = true; | |
| 182 } | |
| 183 if (dst[0] != SK_AlphaTRANSPARENT) { | |
| 184 *isTransparent = false; | |
| 185 } | |
| 186 dst++; | |
| 187 } else { | |
| 188 dst[0] = SkGetPackedR32(src[x]); | |
| 189 dst[1] = SkGetPackedG32(src[x]); | |
| 190 dst[2] = SkGetPackedB32(src[x]); | |
| 191 dst += 3; | |
| 192 } | |
| 193 } | |
| 194 } | |
| 195 return stream; | |
| 196 } | |
| 197 | |
| 198 static SkStream* extract_a1_alpha(const SkBitmap& bitmap, | |
| 199 const SkIRect& srcRect, | |
| 200 bool* hasAlpha, | |
| 201 bool* isTransparent) { | |
| 202 const int alphaRowBytes = (srcRect.width() + 7) / 8; | |
| 203 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
| 204 (alphaRowBytes * srcRect.height())); | |
| 205 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); | |
| 206 | |
| 207 int offset1 = srcRect.fLeft % 8; | |
| 208 int offset2 = 8 - offset1; | |
| 209 | |
| 210 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 211 uint8_t* src = bitmap.getAddr1(0, y); | |
| 212 // This may read up to one byte after src, but the | |
| 213 // potentially invalid bits are never used for computation. | |
| 214 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { | |
| 215 if (offset1) { | |
| 216 alphaDst[0] = src[x / 8] << offset1 | | |
| 217 src[x / 8 + 1] >> offset2; | |
| 218 } else { | |
| 219 alphaDst[0] = src[x / 8]; | |
| 220 } | |
| 221 if (x + 7 < srcRect.fRight && alphaDst[0] != SK_AlphaOPAQUE) { | |
| 222 *hasAlpha = true; | |
| 223 } | |
| 224 if (x + 7 < srcRect.fRight && alphaDst[0] != SK_AlphaTRANSPARENT) { | |
| 225 *isTransparent = false; | |
| 226 } | |
| 227 alphaDst++; | |
| 228 } | |
| 229 // Calculate the mask of bits we're interested in within the | |
| 230 // last byte of alphaDst. | |
| 231 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE | |
| 232 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); | |
| 233 if (srcRect.width() % 8 && | |
| 234 (alphaDst[-1] & mask) != (SK_AlphaOPAQUE & mask)) { | |
| 235 *hasAlpha = true; | |
| 236 } | |
| 237 if (srcRect.width() % 8 && | |
| 238 (alphaDst[-1] & mask) != (SK_AlphaTRANSPARENT & mask)) { | |
| 239 *isTransparent = false; | |
| 240 } | |
| 241 } | |
| 242 return stream; | |
| 243 } | |
| 244 | |
| 245 static SkStream* extract_a8_alpha(const SkBitmap& bitmap, | |
| 246 const SkIRect& srcRect, | |
| 247 bool* hasAlpha, | |
| 248 bool* isTransparent) { | |
| 249 const int alphaRowBytes = srcRect.width(); | |
| 250 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
| 251 (alphaRowBytes * srcRect.height())); | |
| 252 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); | |
| 253 | |
| 254 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 255 uint8_t* src = bitmap.getAddr8(0, y); | |
| 256 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 257 alphaDst[0] = src[x]; | |
| 258 if (alphaDst[0] != SK_AlphaOPAQUE) { | |
| 259 *hasAlpha = true; | |
| 260 } | |
| 261 if (alphaDst[0] != SK_AlphaTRANSPARENT) { | |
| 262 *isTransparent = false; | |
| 263 } | |
| 264 alphaDst++; | |
| 265 } | |
| 266 } | |
| 267 return stream; | |
| 268 } | |
| 269 | |
| 270 static SkStream* create_black_image() { | |
| 271 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); | |
| 272 ((uint8_t*)stream->getMemoryBase())[0] = 0; | |
| 273 return stream; | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Extract image data to a SkStream. Interlaced alpha is separated into alpha | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
Interlaced usually refers to lines/horizontal stri
ducky
2013/08/23 06:59:08
Done.
| |
| 278 * and image streams. | |
| 279 * @param bitmap Bitmap to extract data from. | |
| 280 * @param srcRect Region in the bitmap to extract. | |
| 281 * @param extractAlpha Set to true to extract the alpha data, or false to | |
| 282 * extract the color data. | |
| 283 * @param transparent Whether the alpha is completely transparent. Only valid | |
| 284 * when extractAlpha == true. | |
| 285 * @return Unencoded image data, or NULL if data was not available | |
| 286 * OR alpha data was requested but the image was entirely | |
| 287 * transparent or opaque. | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
If Null is returned when the image is transparent,
ducky
2013/08/23 06:59:08
Necessary to differentiate from opaque case (where
| |
| 288 */ | |
| 289 static SkStream* extract_image_data(const SkBitmap& bitmap, | |
| 290 const SkIRect& srcRect, | |
| 291 bool extractAlpha, bool* isTransparent) { | |
| 292 SkStream* stream = NULL; | |
| 25 bool hasAlpha = false; | 293 bool hasAlpha = false; |
| 26 bool isTransparent = false; | 294 *isTransparent = true; |
| 27 | 295 |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
Probably easier to have an early return for the al
ducky
2013/08/23 06:59:08
Ehhhhh - the current style uses consistent mechani
vandebo (ex-Chrome)
2013/08/23 15:45:35
Disagree. Not sure what you mean by consistent me
ducky
2013/08/23 17:59:50
I see - done.
On 2013/08/23 15:45:35, vandebo wro
| |
| 28 bitmap.lockPixels(); | 296 bitmap.lockPixels(); |
| 29 switch (bitmap.getConfig()) { | 297 switch (bitmap.getConfig()) { |
| 30 case SkBitmap::kIndex8_Config: { | 298 case SkBitmap::kIndex8_Config: |
| 31 const int rowBytes = srcRect.width(); | 299 if (!extractAlpha) { |
| 32 image = new SkMemoryStream(rowBytes * srcRect.height()); | 300 stream = extract_index8_image(bitmap, srcRect); |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
Use of functions makes this one easier to follow.
ducky
2013/08/23 06:59:08
Yeah, the old one was getting super bloated...
| |
| 33 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | 301 } else { |
| 34 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 302 *isTransparent = false; |
| 35 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | 303 } |
| 36 dst += rowBytes; | 304 break; |
| 37 } | 305 case SkBitmap::kARGB_4444_Config: |
| 38 break; | 306 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, |
| 39 } | 307 &hasAlpha, isTransparent); |
| 40 case SkBitmap::kARGB_4444_Config: { | 308 break; |
| 41 isTransparent = true; | 309 case SkBitmap::kRGB_565_Config: |
| 42 const int rowBytes = (srcRect.width() * 3 + 1) / 2; | 310 if (!extractAlpha) { |
| 43 const int alphaRowBytes = (srcRect.width() + 1) / 2; | 311 stream = extract_rgb565_image(bitmap, srcRect); |
| 44 image = new SkMemoryStream(rowBytes * srcRect.height()); | 312 } else { |
| 45 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | 313 *isTransparent = false; |
| 46 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | 314 } |
| 47 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | 315 break; |
| 48 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 316 case SkBitmap::kARGB_8888_Config: |
| 49 uint16_t* src = bitmap.getAddr16(0, y); | 317 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, |
| 50 int x; | 318 &hasAlpha, isTransparent); |
| 51 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | 319 break; |
| 52 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | 320 case SkBitmap::kA1_Config: |
| 53 SkGetPackedG4444(src[x]); | 321 if (extractAlpha) { |
| 54 dst[1] = (SkGetPackedB4444(src[x]) << 4) | | 322 stream = extract_a1_alpha(bitmap, srcRect, |
| 55 SkGetPackedR4444(src[x + 1]); | 323 &hasAlpha, isTransparent); |
| 56 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | 324 } else { |
| 57 SkGetPackedB4444(src[x + 1]); | 325 stream = create_black_image(); |
| 58 dst += 3; | 326 } |
| 59 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | | 327 break; |
| 60 SkGetPackedA4444(src[x + 1]); | 328 case SkBitmap::kA8_Config: |
| 61 if (alphaDst[0] != 0xFF) { | 329 if (extractAlpha) { |
| 62 hasAlpha = true; | 330 stream = extract_a8_alpha(bitmap, srcRect, |
| 63 } | 331 &hasAlpha, isTransparent); |
| 64 if (alphaDst[0]) { | 332 } else { |
| 65 isTransparent = false; | 333 stream = create_black_image(); |
| 66 } | 334 } |
| 67 alphaDst++; | 335 break; |
| 68 } | |
| 69 if (srcRect.width() & 1) { | |
| 70 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
| 71 SkGetPackedG4444(src[x]); | |
| 72 dst[1] = (SkGetPackedB4444(src[x]) << 4); | |
| 73 dst += 2; | |
| 74 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); | |
| 75 if (alphaDst[0] != 0xF0) { | |
| 76 hasAlpha = true; | |
| 77 } | |
| 78 if (alphaDst[0] & 0xF0) { | |
| 79 isTransparent = false; | |
| 80 } | |
| 81 alphaDst++; | |
| 82 } | |
| 83 } | |
| 84 break; | |
| 85 } | |
| 86 case SkBitmap::kRGB_565_Config: { | |
| 87 const int rowBytes = srcRect.width() * 3; | |
| 88 image = new SkMemoryStream(rowBytes * srcRect.height()); | |
| 89 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | |
| 90 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 91 uint16_t* src = bitmap.getAddr16(0, y); | |
| 92 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 93 dst[0] = SkGetPackedR16(src[x]); | |
| 94 dst[1] = SkGetPackedG16(src[x]); | |
| 95 dst[2] = SkGetPackedB16(src[x]); | |
| 96 dst += 3; | |
| 97 } | |
| 98 } | |
| 99 break; | |
| 100 } | |
| 101 case SkBitmap::kARGB_8888_Config: { | |
| 102 isTransparent = true; | |
| 103 const int rowBytes = srcRect.width() * 3; | |
| 104 image = new SkMemoryStream(rowBytes * srcRect.height()); | |
| 105 alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); | |
| 106 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | |
| 107 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
| 108 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 109 uint32_t* src = bitmap.getAddr32(0, y); | |
| 110 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 111 dst[0] = SkGetPackedR32(src[x]); | |
| 112 dst[1] = SkGetPackedG32(src[x]); | |
| 113 dst[2] = SkGetPackedB32(src[x]); | |
| 114 dst += 3; | |
| 115 alphaDst[0] = SkGetPackedA32(src[x]); | |
| 116 if (alphaDst[0] != 0xFF) { | |
| 117 hasAlpha = true; | |
| 118 } | |
| 119 if (alphaDst[0]) { | |
| 120 isTransparent = false; | |
| 121 } | |
| 122 alphaDst++; | |
| 123 } | |
| 124 } | |
| 125 break; | |
| 126 } | |
| 127 case SkBitmap::kA1_Config: { | |
| 128 isTransparent = true; | |
| 129 image = new SkMemoryStream(1); | |
| 130 ((uint8_t*)image->getMemoryBase())[0] = 0; | |
| 131 | |
| 132 const int alphaRowBytes = (srcRect.width() + 7) / 8; | |
| 133 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | |
| 134 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
| 135 int offset1 = srcRect.fLeft % 8; | |
| 136 int offset2 = 8 - offset1; | |
| 137 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 138 uint8_t* src = bitmap.getAddr1(0, y); | |
| 139 // This may read up to one byte after src, but the potentially | |
| 140 // invalid bits are never used for computation. | |
| 141 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { | |
| 142 if (offset1) { | |
| 143 alphaDst[0] = src[x / 8] << offset1 | | |
| 144 src[x / 8 + 1] >> offset2; | |
| 145 } else { | |
| 146 alphaDst[0] = src[x / 8]; | |
| 147 } | |
| 148 if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) { | |
| 149 hasAlpha = true; | |
| 150 } | |
| 151 if (x + 7 < srcRect.fRight && alphaDst[0]) { | |
| 152 isTransparent = false; | |
| 153 } | |
| 154 alphaDst++; | |
| 155 } | |
| 156 // Calculate the mask of bits we're interested in within the | |
| 157 // last byte of alphaDst. | |
| 158 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE | |
| 159 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); | |
| 160 if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) { | |
| 161 hasAlpha = true; | |
| 162 } | |
| 163 if (srcRect.width() % 8 && (alphaDst[-1] & mask)) { | |
| 164 isTransparent = false; | |
| 165 } | |
| 166 } | |
| 167 break; | |
| 168 } | |
| 169 case SkBitmap::kA8_Config: { | |
| 170 isTransparent = true; | |
| 171 image = new SkMemoryStream(1); | |
| 172 ((uint8_t*)image->getMemoryBase())[0] = 0; | |
| 173 | |
| 174 const int alphaRowBytes = srcRect.width(); | |
| 175 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | |
| 176 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
| 177 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
| 178 uint8_t* src = bitmap.getAddr8(0, y); | |
| 179 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
| 180 alphaDst[0] = src[x]; | |
| 181 if (alphaDst[0] != 0xFF) { | |
| 182 hasAlpha = true; | |
| 183 } | |
| 184 if (alphaDst[0]) { | |
| 185 isTransparent = false; | |
| 186 } | |
| 187 alphaDst++; | |
| 188 } | |
| 189 } | |
| 190 break; | |
| 191 } | |
| 192 default: | 336 default: |
| 193 SkASSERT(false); | 337 SkASSERT(false); |
| 194 } | 338 } |
| 195 bitmap.unlockPixels(); | 339 bitmap.unlockPixels(); |
| 196 | 340 |
| 197 if (isTransparent) { | 341 if (extractAlpha && (*isTransparent || !hasAlpha)) { |
| 198 SkSafeUnref(image); | 342 SkSafeUnref(stream); |
| 199 } else { | 343 return NULL; |
| 200 *imageData = image; | 344 } |
| 201 } | 345 return stream; |
| 202 | 346 } |
| 203 if (isTransparent || !hasAlpha) { | 347 |
| 204 SkSafeUnref(alpha); | 348 static SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
static functions should have hacker case: make_ind
ducky
2013/08/23 06:59:08
Missed updating this one... done.
| |
| 205 } else { | |
| 206 *alphaData = alpha; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { | |
| 211 SkPDFArray* result = new SkPDFArray(); | 349 SkPDFArray* result = new SkPDFArray(); |
| 212 result->reserve(4); | 350 result->reserve(4); |
| 213 result->appendName("Indexed"); | 351 result->appendName("Indexed"); |
| 214 result->appendName("DeviceRGB"); | 352 result->appendName("DeviceRGB"); |
| 215 result->appendInt(table->count() - 1); | 353 result->appendInt(table->count() - 1); |
| 216 | 354 |
| 217 // Potentially, this could be represented in fewer bytes with a stream. | 355 // Potentially, this could be represented in fewer bytes with a stream. |
| 218 // Max size as a string is 1.5k. | 356 // Max size as a string is 1.5k. |
| 219 SkString index; | 357 SkString index; |
| 220 for (int i = 0; i < table->count(); i++) { | 358 for (int i = 0; i < table->count(); i++) { |
| 221 char buf[3]; | 359 char buf[3]; |
| 222 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); | 360 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); |
| 223 buf[0] = SkGetPackedR32(color); | 361 buf[0] = SkGetPackedR32(color); |
| 224 buf[1] = SkGetPackedG32(color); | 362 buf[1] = SkGetPackedG32(color); |
| 225 buf[2] = SkGetPackedB32(color); | 363 buf[2] = SkGetPackedB32(color); |
| 226 index.append(buf, 3); | 364 index.append(buf, 3); |
| 227 } | 365 } |
| 228 result->append(new SkPDFString(index))->unref(); | 366 result->append(new SkPDFString(index))->unref(); |
| 229 return result; | 367 return result; |
| 230 } | 368 } |
| 231 | 369 |
| 232 }; // namespace | |
| 233 | |
| 234 // static | 370 // static |
| 235 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, | 371 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
| 236 const SkIRect& srcRect, | 372 const SkIRect& srcRect, |
| 237 EncodeToDCTStream encoder) { | 373 EncodeToDCTStream encoder) { |
| 238 if (bitmap.getConfig() == SkBitmap::kNo_Config) { | 374 if (bitmap.getConfig() == SkBitmap::kNo_Config) { |
| 239 return NULL; | 375 return NULL; |
| 240 } | 376 } |
| 241 | 377 |
| 242 SkStream* imageData = NULL; | 378 bool isTransparent; |
| 243 SkStream* alphaData = NULL; | 379 SkAutoTUnref<SkStream> alphaData( |
|
vandebo (ex-Chrome)
2013/08/23 05:45:10
nit: if bitmap.isOpaque() is set, then you can ski
ducky
2013/08/23 06:59:08
Done, along with cautionary comment.
Are there any
| |
| 244 extractImageData(bitmap, srcRect, &imageData, &alphaData); | 380 extract_image_data(bitmap, srcRect, true, &isTransparent)); |
| 245 SkAutoUnref unrefImageData(imageData); | 381 if (isTransparent) { |
| 246 SkAutoUnref unrefAlphaData(alphaData); | |
| 247 if (!imageData) { | |
| 248 SkASSERT(!alphaData); | |
| 249 return NULL; | 382 return NULL; |
| 250 } | 383 } |
| 251 | 384 |
| 252 SkPDFImage* image = | 385 SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (bitmap, srcRect, encoder)); |
| 253 SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder)); | 386 if (alphaData.get() != NULL) { |
| 387 SkAutoTUnref<SkPDFImage> mask( | |
| 388 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, srcRect))); | |
| 389 image->addSMask(mask); | |
| 390 } | |
| 254 | 391 |
| 255 if (alphaData != NULL) { | |
| 256 // Don't try to use DCT compression with alpha because alpha is small | |
| 257 // anyway and it could lead to artifacts. | |
| 258 image->addSMask(SkNEW_ARGS(SkPDFImage, (alphaData, bitmap, srcRect, true , NULL)))->unref(); | |
| 259 } | |
| 260 return image; | 392 return image; |
| 261 } | 393 } |
| 262 | 394 |
| 263 SkPDFImage::~SkPDFImage() { | 395 SkPDFImage::~SkPDFImage() { |
| 264 fResources.unrefAll(); | 396 fResources.unrefAll(); |
| 265 } | 397 } |
| 266 | 398 |
| 267 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { | 399 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { |
| 268 fResources.push(mask); | 400 fResources.push(mask); |
| 269 mask->ref(); | 401 mask->ref(); |
| 270 insert("SMask", new SkPDFObjRef(mask))->unref(); | 402 insert("SMask", new SkPDFObjRef(mask))->unref(); |
| 271 return mask; | 403 return mask; |
| 272 } | 404 } |
| 273 | 405 |
| 274 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 406 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
| 275 SkTSet<SkPDFObject*>* newResourceObjects) { | 407 SkTSet<SkPDFObject*>* newResourceObjects) { |
| 276 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); | 408 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
| 277 } | 409 } |
| 278 | 410 |
| 279 SkPDFImage::SkPDFImage(SkStream* imageData, | 411 SkPDFImage::SkPDFImage(const SkBitmap& bitmap, |
| 280 const SkBitmap& bitmap, | |
| 281 const SkIRect& srcRect, | 412 const SkIRect& srcRect, |
| 282 bool doingAlpha, | |
| 283 EncodeToDCTStream encoder) | 413 EncodeToDCTStream encoder) |
| 284 : SkPDFImageStream(imageData, bitmap, srcRect, encoder) { | 414 : fBitmap(bitmap), |
| 285 SkBitmap::Config config = bitmap.getConfig(); | 415 fSrcRect(srcRect), |
| 286 bool alphaOnly = (config == SkBitmap::kA1_Config || | 416 fEncoder(encoder), |
| 287 config == SkBitmap::kA8_Config); | 417 fStreamValid(false) { |
| 418 initImageParams(false); | |
| 419 } | |
| 420 | |
| 421 SkPDFImage::SkPDFImage(SkStream* stream, const SkBitmap& bitmap, | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
This constructor is effectively the same as the pr
ducky
2013/08/23 06:59:08
Done - it is cleaner in many regards. I've clarifi
| |
| 422 const SkIRect& srcRect) | |
| 423 : fBitmap(bitmap), | |
| 424 fSrcRect(srcRect), | |
| 425 fEncoder(NULL), | |
| 426 fStreamValid(true) { | |
| 427 setData(stream); | |
| 428 initImageParams(true); | |
| 429 } | |
| 430 | |
| 431 SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) | |
| 432 : SkPDFStream(pdfImage), | |
| 433 fBitmap(pdfImage.fBitmap), | |
| 434 fSrcRect(pdfImage.fSrcRect), | |
| 435 fEncoder(pdfImage.fEncoder), | |
| 436 fStreamValid(pdfImage.fStreamValid) { | |
| 437 // Nothing to do here - the image params are already copied in SkPDFStream's | |
| 438 // constructor, and the bitmap will be regenerated and re-encoded in | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
"...regenerated and re-encoded..." => encoded
ducky
2013/08/23 06:59:08
Done.
| |
| 439 // populate. | |
| 440 } | |
| 441 | |
| 442 void SkPDFImage::initImageParams(bool isAlpha) { | |
| 443 SkBitmap::Config config = fBitmap.getConfig(); | |
| 288 | 444 |
| 289 insertName("Type", "XObject"); | 445 insertName("Type", "XObject"); |
| 290 insertName("Subtype", "Image"); | 446 insertName("Subtype", "Image"); |
| 291 | 447 |
| 292 if (!doingAlpha && alphaOnly) { | 448 bool alphaOnly = (config == SkBitmap::kA1_Config || |
| 449 config == SkBitmap::kA8_Config); | |
| 450 | |
| 451 if (!isAlpha && alphaOnly) { | |
| 293 // For alpha only images, we stretch a single pixel of black for | 452 // For alpha only images, we stretch a single pixel of black for |
| 294 // the color/shape part. | 453 // the color/shape part. |
| 295 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); | 454 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); |
| 296 insert("Width", one.get()); | 455 insert("Width", one.get()); |
| 297 insert("Height", one.get()); | 456 insert("Height", one.get()); |
| 298 } else { | 457 } else { |
| 299 insertInt("Width", srcRect.width()); | 458 insertInt("Width", fSrcRect.width()); |
| 300 insertInt("Height", srcRect.height()); | 459 insertInt("Height", fSrcRect.height()); |
| 301 } | 460 } |
| 302 | 461 |
| 303 // if (!image mask) { | 462 if (isAlpha || alphaOnly) { |
| 304 if (doingAlpha || alphaOnly) { | |
| 305 insertName("ColorSpace", "DeviceGray"); | 463 insertName("ColorSpace", "DeviceGray"); |
| 306 } else if (config == SkBitmap::kIndex8_Config) { | 464 } else if (config == SkBitmap::kIndex8_Config) { |
| 307 SkAutoLockPixels alp(bitmap); | 465 SkAutoLockPixels alp(fBitmap); |
| 308 insert("ColorSpace", | 466 insert("ColorSpace", |
| 309 makeIndexedColorSpace(bitmap.getColorTable()))->unref(); | 467 makeIndexedColorSpace(fBitmap.getColorTable()))->unref(); |
| 310 } else { | 468 } else { |
| 311 insertName("ColorSpace", "DeviceRGB"); | 469 insertName("ColorSpace", "DeviceRGB"); |
| 312 } | 470 } |
| 313 // } | |
| 314 | 471 |
| 315 int bitsPerComp = 8; | 472 int bitsPerComp = 8; |
| 316 if (config == SkBitmap::kARGB_4444_Config) { | 473 if (config == SkBitmap::kARGB_4444_Config) { |
| 317 bitsPerComp = 4; | 474 bitsPerComp = 4; |
| 318 } else if (doingAlpha && config == SkBitmap::kA1_Config) { | 475 } else if (isAlpha && config == SkBitmap::kA1_Config) { |
| 319 bitsPerComp = 1; | 476 bitsPerComp = 1; |
| 320 } | 477 } |
| 321 insertInt("BitsPerComponent", bitsPerComp); | 478 insertInt("BitsPerComponent", bitsPerComp); |
| 322 | 479 |
| 323 if (config == SkBitmap::kRGB_565_Config) { | 480 if (config == SkBitmap::kRGB_565_Config) { |
| 481 SkASSERT(!isAlpha); | |
| 324 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); | 482 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
| 325 SkAutoTUnref<SkPDFScalar> scale5Val( | 483 SkAutoTUnref<SkPDFScalar> scale5Val( |
| 326 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 | 484 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 |
| 327 SkAutoTUnref<SkPDFScalar> scale6Val( | 485 SkAutoTUnref<SkPDFScalar> scale6Val( |
| 328 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 | 486 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 |
| 329 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); | 487 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); |
| 330 decodeValue->reserve(6); | 488 decodeValue->reserve(6); |
| 331 decodeValue->append(zeroVal.get()); | 489 decodeValue->append(zeroVal.get()); |
| 332 decodeValue->append(scale5Val.get()); | 490 decodeValue->append(scale5Val.get()); |
| 333 decodeValue->append(zeroVal.get()); | 491 decodeValue->append(zeroVal.get()); |
| 334 decodeValue->append(scale6Val.get()); | 492 decodeValue->append(scale6Val.get()); |
| 335 decodeValue->append(zeroVal.get()); | 493 decodeValue->append(zeroVal.get()); |
| 336 decodeValue->append(scale5Val.get()); | 494 decodeValue->append(scale5Val.get()); |
| 337 insert("Decode", decodeValue.get()); | 495 insert("Decode", decodeValue.get()); |
| 338 } | 496 } |
| 339 } | 497 } |
| 498 | |
| 499 bool SkPDFImage::populate(SkPDFCatalog* catalog) { | |
| 500 if (getState() == kUnused_State) { | |
| 501 // Initializing image data for the first time. | |
| 502 SkDynamicMemoryWStream dctCompressedWStream; | |
| 503 if (!skip_compression(catalog) && fEncoder && | |
| 504 get_uncompressed_size(fBitmap, fSrcRect) > 1 && | |
| 505 fEncoder(&dctCompressedWStream, fBitmap, fSrcRect) && | |
| 506 dctCompressedWStream.getOffset() < | |
| 507 get_uncompressed_size(fBitmap, fSrcRect)) { | |
| 508 SkAutoTUnref<SkData> data(dctCompressedWStream.copyToData()); | |
| 509 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data))); | |
| 510 setData(stream.get()); | |
| 511 | |
| 512 insertName("Filter", "DCTDecode"); | |
| 513 insertInt("ColorTransform", kNoColorTransform); | |
| 514 insertInt("Length", getData()->getLength()); | |
| 515 setState(kCompressed_State); | |
| 516 return true; | |
| 517 } | |
| 518 // Fallback method | |
| 519 if (!fStreamValid) { | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
Should this be a debug check? You always expect t
ducky
2013/08/23 06:59:08
No - when the alpha channel is generated, the stre
| |
| 520 bool dummy; | |
|
vandebo (ex-Chrome)
2013/08/23 05:25:20
dummy -> no_used, but generally I don't think you
ducky
2013/08/23 06:59:08
Done. Updated code and comments to reflect that th
vandebo (ex-Chrome)
2013/08/23 15:45:35
It should alway be able to be NULL.
ducky
2013/08/23 17:59:50
Done.
| |
| 521 SkAutoTUnref<SkStream> stream( | |
| 522 extract_image_data(fBitmap, fSrcRect, false, &dummy)); | |
| 523 setData(stream); | |
| 524 fStreamValid = true; | |
| 525 } | |
| 526 return INHERITED::populate(catalog); | |
| 527 } else if (getState() == kNoCompression_State && | |
| 528 !skip_compression(catalog) && | |
| 529 (SkFlate::HaveFlate() || fEncoder)) { | |
| 530 // Compression has not been requested when the stream was first created, | |
| 531 // but the new catalog wants it compressed. | |
| 532 if (!getSubstitute()) { | |
| 533 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); | |
| 534 setSubstitute(substitute); | |
| 535 catalog->setSubstitute(this, substitute); | |
| 536 } | |
| 537 return false; | |
| 538 } | |
| 539 return true; | |
| 540 } | |
| OLD | NEW |