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 } |
25 bool hasAlpha = false; | 27 |
26 bool isTransparent = false; | 28 static size_t get_uncompressed_size(const SkBitmap& bitmap, |
27 | 29 const SkIRect& srcRect) { |
28 bitmap.lockPixels(); | |
29 switch (bitmap.getConfig()) { | 30 switch (bitmap.getConfig()) { |
30 case SkBitmap::kIndex8_Config: { | 31 case SkBitmap::kIndex8_Config: |
31 const int rowBytes = srcRect.width(); | 32 return srcRect.width() * srcRect.height(); |
32 image = new SkMemoryStream(rowBytes * srcRect.height()); | 33 case SkBitmap::kARGB_4444_Config: |
33 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | 34 return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); |
34 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 35 case SkBitmap::kRGB_565_Config: |
35 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | 36 return srcRect.width() * 3 * srcRect.height(); |
36 dst += rowBytes; | 37 case SkBitmap::kARGB_8888_Config: |
37 } | 38 return srcRect.width() * 3 * srcRect.height(); |
38 break; | 39 case SkBitmap::kA1_Config: |
39 } | 40 case SkBitmap::kA8_Config: |
40 case SkBitmap::kARGB_4444_Config: { | 41 return 1; |
41 isTransparent = true; | |
42 const int rowBytes = (srcRect.width() * 3 + 1) / 2; | |
43 const int alphaRowBytes = (srcRect.width() + 1) / 2; | |
44 image = new SkMemoryStream(rowBytes * srcRect.height()); | |
45 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | |
46 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | |
47 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
48 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
49 uint16_t* src = bitmap.getAddr16(0, y); | |
50 int x; | |
51 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | |
52 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
53 SkGetPackedG4444(src[x]); | |
54 dst[1] = (SkGetPackedB4444(src[x]) << 4) | | |
55 SkGetPackedR4444(src[x + 1]); | |
56 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | |
57 SkGetPackedB4444(src[x + 1]); | |
58 dst += 3; | |
59 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | | |
60 SkGetPackedA4444(src[x + 1]); | |
61 if (alphaDst[0] != 0xFF) { | |
62 hasAlpha = true; | |
63 } | |
64 if (alphaDst[0]) { | |
65 isTransparent = false; | |
66 } | |
67 alphaDst++; | |
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: | 42 default: |
193 SkASSERT(false); | 43 SkASSERT(false); |
| 44 return 0; |
| 45 } |
| 46 } |
| 47 |
| 48 static SkStream* extract_index8_image(const SkBitmap& bitmap, |
| 49 const SkIRect& srcRect) { |
| 50 const int rowBytes = srcRect.width(); |
| 51 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 52 (get_uncompressed_size(bitmap, srcRect))); |
| 53 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 54 |
| 55 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 56 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); |
| 57 dst += rowBytes; |
| 58 } |
| 59 return stream; |
| 60 } |
| 61 |
| 62 static SkStream* extract_argb4444_data(const SkBitmap& bitmap, |
| 63 const SkIRect& srcRect, |
| 64 bool extractAlpha, |
| 65 bool* isOpaque, |
| 66 bool* isTransparent) { |
| 67 SkStream* stream; |
| 68 uint8_t* dst = NULL; |
| 69 if (extractAlpha) { |
| 70 const int alphaRowBytes = (srcRect.width() + 1) / 2; |
| 71 stream = SkNEW_ARGS(SkMemoryStream, |
| 72 (alphaRowBytes * srcRect.height())); |
| 73 } else { |
| 74 stream = SkNEW_ARGS(SkMemoryStream, |
| 75 (get_uncompressed_size(bitmap, srcRect))); |
| 76 } |
| 77 dst = (uint8_t*)stream->getMemoryBase(); |
| 78 |
| 79 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 80 uint16_t* src = bitmap.getAddr16(0, y); |
| 81 int x; |
| 82 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { |
| 83 if (extractAlpha) { |
| 84 dst[0] = (SkGetPackedA4444(src[x]) << 4) | |
| 85 SkGetPackedA4444(src[x + 1]); |
| 86 *isOpaque &= dst[0] == SK_AlphaOPAQUE; |
| 87 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; |
| 88 dst++; |
| 89 } else { |
| 90 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
| 91 SkGetPackedG4444(src[x]); |
| 92 dst[1] = (SkGetPackedB4444(src[x]) << 4) | |
| 93 SkGetPackedR4444(src[x + 1]); |
| 94 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | |
| 95 SkGetPackedB4444(src[x + 1]); |
| 96 dst += 3; |
| 97 } |
| 98 } |
| 99 if (srcRect.width() & 1) { |
| 100 if (extractAlpha) { |
| 101 dst[0] = (SkGetPackedA4444(src[x]) << 4); |
| 102 *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0); |
| 103 *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0); |
| 104 dst++; |
| 105 |
| 106 } else { |
| 107 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
| 108 SkGetPackedG4444(src[x]); |
| 109 dst[1] = (SkGetPackedB4444(src[x]) << 4); |
| 110 dst += 2; |
| 111 } |
| 112 } |
| 113 } |
| 114 return stream; |
| 115 } |
| 116 |
| 117 static SkStream* extract_rgb565_image(const SkBitmap& bitmap, |
| 118 const SkIRect& srcRect) { |
| 119 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 120 (get_uncompressed_size(bitmap, |
| 121 srcRect))); |
| 122 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 123 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 124 uint16_t* src = bitmap.getAddr16(0, y); |
| 125 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 126 dst[0] = SkGetPackedR16(src[x]); |
| 127 dst[1] = SkGetPackedG16(src[x]); |
| 128 dst[2] = SkGetPackedB16(src[x]); |
| 129 dst += 3; |
| 130 } |
| 131 } |
| 132 return stream; |
| 133 } |
| 134 |
| 135 static SkStream* extract_argb8888_data(const SkBitmap& bitmap, |
| 136 const SkIRect& srcRect, |
| 137 bool extractAlpha, |
| 138 bool* isOpaque, |
| 139 bool* isTransparent) { |
| 140 SkStream* stream; |
| 141 if (extractAlpha) { |
| 142 stream = SkNEW_ARGS(SkMemoryStream, |
| 143 (srcRect.width() * srcRect.height())); |
| 144 } else { |
| 145 stream = SkNEW_ARGS(SkMemoryStream, |
| 146 (get_uncompressed_size(bitmap, srcRect))); |
| 147 } |
| 148 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 149 |
| 150 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 151 uint32_t* src = bitmap.getAddr32(0, y); |
| 152 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 153 if (extractAlpha) { |
| 154 dst[0] = SkGetPackedA32(src[x]); |
| 155 *isOpaque &= dst[0] == SK_AlphaOPAQUE; |
| 156 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; |
| 157 dst++; |
| 158 } else { |
| 159 dst[0] = SkGetPackedR32(src[x]); |
| 160 dst[1] = SkGetPackedG32(src[x]); |
| 161 dst[2] = SkGetPackedB32(src[x]); |
| 162 dst += 3; |
| 163 } |
| 164 } |
| 165 } |
| 166 return stream; |
| 167 } |
| 168 |
| 169 static SkStream* extract_a1_alpha(const SkBitmap& bitmap, |
| 170 const SkIRect& srcRect, |
| 171 bool* isOpaque, |
| 172 bool* isTransparent) { |
| 173 const int alphaRowBytes = (srcRect.width() + 7) / 8; |
| 174 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 175 (alphaRowBytes * srcRect.height())); |
| 176 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); |
| 177 |
| 178 int offset1 = srcRect.fLeft % 8; |
| 179 int offset2 = 8 - offset1; |
| 180 |
| 181 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 182 uint8_t* src = bitmap.getAddr1(0, y); |
| 183 // This may read up to one byte after src, but the |
| 184 // potentially invalid bits are never used for computation. |
| 185 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { |
| 186 if (offset1) { |
| 187 alphaDst[0] = src[x / 8] << offset1 | |
| 188 src[x / 8 + 1] >> offset2; |
| 189 } else { |
| 190 alphaDst[0] = src[x / 8]; |
| 191 } |
| 192 if (x + 7 < srcRect.fRight) { |
| 193 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; |
| 194 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; |
| 195 } |
| 196 alphaDst++; |
| 197 } |
| 198 // Calculate the mask of bits we're interested in within the |
| 199 // last byte of alphaDst. |
| 200 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE |
| 201 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); |
| 202 if (srcRect.width() % 8) { |
| 203 *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask); |
| 204 *isTransparent &= |
| 205 (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask); |
| 206 } |
| 207 } |
| 208 return stream; |
| 209 } |
| 210 |
| 211 static SkStream* extract_a8_alpha(const SkBitmap& bitmap, |
| 212 const SkIRect& srcRect, |
| 213 bool* isOpaque, |
| 214 bool* isTransparent) { |
| 215 const int alphaRowBytes = srcRect.width(); |
| 216 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 217 (alphaRowBytes * srcRect.height())); |
| 218 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); |
| 219 |
| 220 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 221 uint8_t* src = bitmap.getAddr8(0, y); |
| 222 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 223 alphaDst[0] = src[x]; |
| 224 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; |
| 225 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; |
| 226 alphaDst++; |
| 227 } |
| 228 } |
| 229 return stream; |
| 230 } |
| 231 |
| 232 static SkStream* create_black_image() { |
| 233 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); |
| 234 ((uint8_t*)stream->getMemoryBase())[0] = 0; |
| 235 return stream; |
| 236 } |
| 237 |
| 238 /** |
| 239 * Extract either the color or image data from a SkBitmap into a SkStream. |
| 240 * @param bitmap Bitmap to extract data from. |
| 241 * @param srcRect Region in the bitmap to extract. |
| 242 * @param extractAlpha Set to true to extract the alpha data or false to |
| 243 * extract the color data. |
| 244 * @param isTransparent Pointer to a bool to output whether the alpha is |
| 245 * completely transparent. May be NULL. Only valid when |
| 246 * extractAlpha == true. |
| 247 * @return Unencoded image data, or NULL if either data was not |
| 248 * available or alpha data was requested but the image was |
| 249 * entirely transparent or opaque. |
| 250 */ |
| 251 static SkStream* extract_image_data(const SkBitmap& bitmap, |
| 252 const SkIRect& srcRect, |
| 253 bool extractAlpha, bool* isTransparent) { |
| 254 SkBitmap::Config config = bitmap.config(); |
| 255 if (extractAlpha && (config == SkBitmap::kIndex8_Config || |
| 256 config == SkBitmap::kRGB_565_Config)) { |
| 257 if (isTransparent != NULL) { |
| 258 *isTransparent = false; |
| 259 } |
| 260 return NULL; |
| 261 } |
| 262 bool isOpaque = true; |
| 263 bool transparent = extractAlpha; |
| 264 SkStream* stream = NULL; |
| 265 |
| 266 bitmap.lockPixels(); |
| 267 switch (config) { |
| 268 case SkBitmap::kIndex8_Config: |
| 269 if (!extractAlpha) { |
| 270 stream = extract_index8_image(bitmap, srcRect); |
| 271 } |
| 272 break; |
| 273 case SkBitmap::kARGB_4444_Config: |
| 274 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, |
| 275 &isOpaque, &transparent); |
| 276 break; |
| 277 case SkBitmap::kRGB_565_Config: |
| 278 if (!extractAlpha) { |
| 279 stream = extract_rgb565_image(bitmap, srcRect); |
| 280 } |
| 281 break; |
| 282 case SkBitmap::kARGB_8888_Config: |
| 283 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, |
| 284 &isOpaque, &transparent); |
| 285 break; |
| 286 case SkBitmap::kA1_Config: |
| 287 if (!extractAlpha) { |
| 288 stream = create_black_image(); |
| 289 } else { |
| 290 stream = extract_a1_alpha(bitmap, srcRect, |
| 291 &isOpaque, &transparent); |
| 292 } |
| 293 break; |
| 294 case SkBitmap::kA8_Config: |
| 295 if (!extractAlpha) { |
| 296 stream = create_black_image(); |
| 297 } else { |
| 298 stream = extract_a8_alpha(bitmap, srcRect, |
| 299 &isOpaque, &transparent); |
| 300 } |
| 301 break; |
| 302 default: |
| 303 SkASSERT(false); |
194 } | 304 } |
195 bitmap.unlockPixels(); | 305 bitmap.unlockPixels(); |
196 | 306 |
197 if (isTransparent) { | 307 if (isTransparent != NULL) { |
198 SkSafeUnref(image); | 308 *isTransparent = transparent; |
199 } else { | 309 } |
200 *imageData = image; | 310 if (extractAlpha && (transparent || isOpaque)) { |
201 } | 311 SkSafeUnref(stream); |
202 | 312 return NULL; |
203 if (isTransparent || !hasAlpha) { | 313 } |
204 SkSafeUnref(alpha); | 314 return stream; |
205 } else { | 315 } |
206 *alphaData = alpha; | 316 |
207 } | 317 static SkPDFArray* make_indexed_color_space(SkColorTable* table) { |
208 } | |
209 | |
210 SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { | |
211 SkPDFArray* result = new SkPDFArray(); | 318 SkPDFArray* result = new SkPDFArray(); |
212 result->reserve(4); | 319 result->reserve(4); |
213 result->appendName("Indexed"); | 320 result->appendName("Indexed"); |
214 result->appendName("DeviceRGB"); | 321 result->appendName("DeviceRGB"); |
215 result->appendInt(table->count() - 1); | 322 result->appendInt(table->count() - 1); |
216 | 323 |
217 // Potentially, this could be represented in fewer bytes with a stream. | 324 // Potentially, this could be represented in fewer bytes with a stream. |
218 // Max size as a string is 1.5k. | 325 // Max size as a string is 1.5k. |
219 SkString index; | 326 SkString index; |
220 for (int i = 0; i < table->count(); i++) { | 327 for (int i = 0; i < table->count(); i++) { |
221 char buf[3]; | 328 char buf[3]; |
222 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); | 329 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); |
223 buf[0] = SkGetPackedR32(color); | 330 buf[0] = SkGetPackedR32(color); |
224 buf[1] = SkGetPackedG32(color); | 331 buf[1] = SkGetPackedG32(color); |
225 buf[2] = SkGetPackedB32(color); | 332 buf[2] = SkGetPackedB32(color); |
226 index.append(buf, 3); | 333 index.append(buf, 3); |
227 } | 334 } |
228 result->append(new SkPDFString(index))->unref(); | 335 result->append(new SkPDFString(index))->unref(); |
229 return result; | 336 return result; |
230 } | 337 } |
231 | 338 |
232 }; // namespace | |
233 | |
234 // static | 339 // static |
235 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, | 340 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
236 const SkIRect& srcRect, | 341 const SkIRect& srcRect, |
237 EncodeToDCTStream encoder) { | 342 EncodeToDCTStream encoder) { |
238 if (bitmap.getConfig() == SkBitmap::kNo_Config) { | 343 if (bitmap.getConfig() == SkBitmap::kNo_Config) { |
239 return NULL; | 344 return NULL; |
240 } | 345 } |
241 | 346 |
242 SkStream* imageData = NULL; | 347 bool isTransparent = false; |
243 SkStream* alphaData = NULL; | 348 SkAutoTUnref<SkStream> alphaData; |
244 extractImageData(bitmap, srcRect, &imageData, &alphaData); | 349 if (!bitmap.isOpaque()) { |
245 SkAutoUnref unrefImageData(imageData); | 350 // Note that isOpaque is not guaranteed to return false for bitmaps |
246 SkAutoUnref unrefAlphaData(alphaData); | 351 // with alpha support but a completely opaque alpha channel, |
247 if (!imageData) { | 352 // so alphaData may still be NULL if we have a completely opaque |
248 SkASSERT(!alphaData); | 353 // (or transparent) bitmap. |
| 354 alphaData.reset( |
| 355 extract_image_data(bitmap, srcRect, true, &isTransparent)); |
| 356 } |
| 357 if (isTransparent) { |
249 return NULL; | 358 return NULL; |
250 } | 359 } |
251 | 360 |
252 SkPDFImage* image = | 361 SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, |
253 SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder)); | 362 false, srcRect, encoder)); |
| 363 if (alphaData.get() != NULL) { |
| 364 SkAutoTUnref<SkPDFImage> mask( |
| 365 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, |
| 366 true, srcRect, NULL))); |
| 367 image->addSMask(mask); |
| 368 } |
254 | 369 |
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; | 370 return image; |
261 } | 371 } |
262 | 372 |
263 SkPDFImage::~SkPDFImage() { | 373 SkPDFImage::~SkPDFImage() { |
264 fResources.unrefAll(); | 374 fResources.unrefAll(); |
265 } | 375 } |
266 | 376 |
267 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { | 377 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { |
268 fResources.push(mask); | 378 fResources.push(mask); |
269 mask->ref(); | 379 mask->ref(); |
270 insert("SMask", new SkPDFObjRef(mask))->unref(); | 380 insert("SMask", new SkPDFObjRef(mask))->unref(); |
271 return mask; | 381 return mask; |
272 } | 382 } |
273 | 383 |
274 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 384 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
275 SkTSet<SkPDFObject*>* newResourceObjects) { | 385 SkTSet<SkPDFObject*>* newResourceObjects) { |
276 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); | 386 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
277 } | 387 } |
278 | 388 |
279 SkPDFImage::SkPDFImage(SkStream* imageData, | 389 SkPDFImage::SkPDFImage(SkStream* stream, |
280 const SkBitmap& bitmap, | 390 const SkBitmap& bitmap, |
| 391 bool isAlpha, |
281 const SkIRect& srcRect, | 392 const SkIRect& srcRect, |
282 bool doingAlpha, | |
283 EncodeToDCTStream encoder) | 393 EncodeToDCTStream encoder) |
284 : SkPDFImageStream(imageData, bitmap, srcRect, encoder) { | 394 : fBitmap(bitmap), |
285 SkBitmap::Config config = bitmap.getConfig(); | 395 fIsAlpha(isAlpha), |
286 bool alphaOnly = (config == SkBitmap::kA1_Config || | 396 fSrcRect(srcRect), |
287 config == SkBitmap::kA8_Config); | 397 fEncoder(encoder) { |
| 398 |
| 399 if (stream != NULL) { |
| 400 setData(stream); |
| 401 fStreamValid = true; |
| 402 } else { |
| 403 fStreamValid = false; |
| 404 } |
| 405 |
| 406 SkBitmap::Config config = fBitmap.getConfig(); |
288 | 407 |
289 insertName("Type", "XObject"); | 408 insertName("Type", "XObject"); |
290 insertName("Subtype", "Image"); | 409 insertName("Subtype", "Image"); |
291 | 410 |
292 if (!doingAlpha && alphaOnly) { | 411 bool alphaOnly = (config == SkBitmap::kA1_Config || |
| 412 config == SkBitmap::kA8_Config); |
| 413 |
| 414 if (!isAlpha && alphaOnly) { |
293 // For alpha only images, we stretch a single pixel of black for | 415 // For alpha only images, we stretch a single pixel of black for |
294 // the color/shape part. | 416 // the color/shape part. |
295 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); | 417 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); |
296 insert("Width", one.get()); | 418 insert("Width", one.get()); |
297 insert("Height", one.get()); | 419 insert("Height", one.get()); |
298 } else { | 420 } else { |
299 insertInt("Width", srcRect.width()); | 421 insertInt("Width", fSrcRect.width()); |
300 insertInt("Height", srcRect.height()); | 422 insertInt("Height", fSrcRect.height()); |
301 } | 423 } |
302 | 424 |
303 // if (!image mask) { | 425 if (isAlpha || alphaOnly) { |
304 if (doingAlpha || alphaOnly) { | |
305 insertName("ColorSpace", "DeviceGray"); | 426 insertName("ColorSpace", "DeviceGray"); |
306 } else if (config == SkBitmap::kIndex8_Config) { | 427 } else if (config == SkBitmap::kIndex8_Config) { |
307 SkAutoLockPixels alp(bitmap); | 428 SkAutoLockPixels alp(fBitmap); |
308 insert("ColorSpace", | 429 insert("ColorSpace", |
309 makeIndexedColorSpace(bitmap.getColorTable()))->unref(); | 430 make_indexed_color_space(fBitmap.getColorTable()))->unref(); |
310 } else { | 431 } else { |
311 insertName("ColorSpace", "DeviceRGB"); | 432 insertName("ColorSpace", "DeviceRGB"); |
312 } | 433 } |
313 // } | |
314 | 434 |
315 int bitsPerComp = 8; | 435 int bitsPerComp = 8; |
316 if (config == SkBitmap::kARGB_4444_Config) { | 436 if (config == SkBitmap::kARGB_4444_Config) { |
317 bitsPerComp = 4; | 437 bitsPerComp = 4; |
318 } else if (doingAlpha && config == SkBitmap::kA1_Config) { | 438 } else if (isAlpha && config == SkBitmap::kA1_Config) { |
319 bitsPerComp = 1; | 439 bitsPerComp = 1; |
320 } | 440 } |
321 insertInt("BitsPerComponent", bitsPerComp); | 441 insertInt("BitsPerComponent", bitsPerComp); |
322 | 442 |
323 if (config == SkBitmap::kRGB_565_Config) { | 443 if (config == SkBitmap::kRGB_565_Config) { |
| 444 SkASSERT(!isAlpha); |
324 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); | 445 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
325 SkAutoTUnref<SkPDFScalar> scale5Val( | 446 SkAutoTUnref<SkPDFScalar> scale5Val( |
326 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 | 447 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 |
327 SkAutoTUnref<SkPDFScalar> scale6Val( | 448 SkAutoTUnref<SkPDFScalar> scale6Val( |
328 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 | 449 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 |
329 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); | 450 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); |
330 decodeValue->reserve(6); | 451 decodeValue->reserve(6); |
331 decodeValue->append(zeroVal.get()); | 452 decodeValue->append(zeroVal.get()); |
332 decodeValue->append(scale5Val.get()); | 453 decodeValue->append(scale5Val.get()); |
333 decodeValue->append(zeroVal.get()); | 454 decodeValue->append(zeroVal.get()); |
334 decodeValue->append(scale6Val.get()); | 455 decodeValue->append(scale6Val.get()); |
335 decodeValue->append(zeroVal.get()); | 456 decodeValue->append(zeroVal.get()); |
336 decodeValue->append(scale5Val.get()); | 457 decodeValue->append(scale5Val.get()); |
337 insert("Decode", decodeValue.get()); | 458 insert("Decode", decodeValue.get()); |
338 } | 459 } |
339 } | 460 } |
| 461 |
| 462 SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) |
| 463 : SkPDFStream(pdfImage), |
| 464 fBitmap(pdfImage.fBitmap), |
| 465 fIsAlpha(pdfImage.fIsAlpha), |
| 466 fSrcRect(pdfImage.fSrcRect), |
| 467 fEncoder(pdfImage.fEncoder), |
| 468 fStreamValid(pdfImage.fStreamValid) { |
| 469 // Nothing to do here - the image params are already copied in SkPDFStream's |
| 470 // constructor, and the bitmap will be regenerated and encoded in |
| 471 // populate. |
| 472 } |
| 473 |
| 474 bool SkPDFImage::populate(SkPDFCatalog* catalog) { |
| 475 if (getState() == kUnused_State) { |
| 476 // Initializing image data for the first time. |
| 477 SkDynamicMemoryWStream dctCompressedWStream; |
| 478 if (!skip_compression(catalog) && fEncoder && |
| 479 get_uncompressed_size(fBitmap, fSrcRect) > 1 && |
| 480 fEncoder(&dctCompressedWStream, fBitmap, fSrcRect) && |
| 481 dctCompressedWStream.getOffset() < |
| 482 get_uncompressed_size(fBitmap, fSrcRect)) { |
| 483 SkAutoTUnref<SkData> data(dctCompressedWStream.copyToData()); |
| 484 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data))); |
| 485 setData(stream.get()); |
| 486 |
| 487 insertName("Filter", "DCTDecode"); |
| 488 insertInt("ColorTransform", kNoColorTransform); |
| 489 insertInt("Length", getData()->getLength()); |
| 490 setState(kCompressed_State); |
| 491 return true; |
| 492 } |
| 493 // Fallback method |
| 494 if (!fStreamValid) { |
| 495 SkAutoTUnref<SkStream> stream( |
| 496 extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); |
| 497 setData(stream); |
| 498 fStreamValid = true; |
| 499 } |
| 500 return INHERITED::populate(catalog); |
| 501 } else if (getState() == kNoCompression_State && |
| 502 !skip_compression(catalog) && |
| 503 (SkFlate::HaveFlate() || fEncoder)) { |
| 504 // Compression has not been requested when the stream was first created, |
| 505 // but the new catalog wants it compressed. |
| 506 if (!getSubstitute()) { |
| 507 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); |
| 508 setSubstitute(substitute); |
| 509 catalog->setSubstitute(this, substitute); |
| 510 } |
| 511 return false; |
| 512 } |
| 513 return true; |
| 514 } |
OLD | NEW |