1 /* 2 * Copyright 2010 The Android Open Source Project 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 "SkPDFImage.h" 9 10 #include "SkBitmap.h" 11 #include "SkColor.h" 12 #include "SkColorPriv.h" 13 #include "SkPDFCatalog.h" 14 #include "SkRect.h" 15 #include "SkStream.h" 16 #include "SkString.h" 17 #include "SkUnPreMultiply.h" 18 19 namespace { 20 21 void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, 22 SkStream** imageData, SkStream** alphaData) { 23 SkMemoryStream* image = NULL; 24 SkMemoryStream* alpha = NULL; 25 bool hasAlpha = false; 26 bool isTransparent = false; 27 28 bitmap.lockPixels(); 29 switch (bitmap.getConfig()) { 30 case SkBitmap::kIndex8_Config: { 31 const int rowBytes = srcRect.width(); 32 image = new SkMemoryStream(rowBytes * srcRect.height()); 33 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 34 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 35 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); 36 dst += rowBytes; 37 } 38 break; 39 } 40 case SkBitmap::kARGB_4444_Config: { 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: 193 SkASSERT(false); 194 } 195 bitmap.unlockPixels(); 196 197 if (isTransparent) { 198 SkSafeUnref(image); 199 } else { 200 *imageData = image; 201 } 202 203 if (isTransparent || !hasAlpha) { 204 SkSafeUnref(alpha); 205 } else { 206 *alphaData = alpha; 207 } 208 } 209 210 SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { 211 SkPDFArray* result = new SkPDFArray(); 212 result->reserve(4); 213 result->appendName("Indexed"); 214 result->appendName("DeviceRGB"); 215 result->appendInt(table->count() - 1); 216 217 // Potentially, this could be represented in fewer bytes with a stream. 218 // Max size as a string is 1.5k. 219 SkString index; 220 for (int i = 0; i < table->count(); i++) { 221 char buf[3]; 222 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); 223 buf[0] = SkGetPackedR32(color); 224 buf[1] = SkGetPackedG32(color); 225 buf[2] = SkGetPackedB32(color); 226 index.append(buf, 3); 227 } 228 result->append(new SkPDFString(index))->unref(); 229 return result; 230 } 231 232 }; // namespace 233 234 // static 235 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, 236 const SkIRect& srcRect, 237 EncodeToDCTStream encoder) { 238 if (bitmap.getConfig() == SkBitmap::kNo_Config) { 239 return NULL; 240 } 241 242 SkStream* imageData = NULL; 243 SkStream* alphaData = NULL; 244 extractImageData(bitmap, srcRect, &imageData, &alphaData); 245 SkAutoUnref unrefImageData(imageData); 246 SkAutoUnref unrefAlphaData(alphaData); 247 if (!imageData) { 248 SkASSERT(!alphaData); 249 return NULL; 250 } 251 252 SkPDFImage* image = 253 SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder)); 254 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; 261 } 262 263 SkPDFImage::~SkPDFImage() { 264 fResources.unrefAll(); 265 } 266 267 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { 268 fResources.push(mask); 269 mask->ref(); 270 insert("SMask", new SkPDFObjRef(mask))->unref(); 271 return mask; 272 } 273 274 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, 275 SkTSet<SkPDFObject*>* newResourceObjects) { 276 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); 277 } 278 279 SkPDFImage::SkPDFImage(SkStream* imageData, 280 const SkBitmap& bitmap, 281 const SkIRect& srcRect, 282 bool doingAlpha, 283 EncodeToDCTStream encoder) 284 : SkPDFImageStream(imageData, bitmap, srcRect, encoder) { 285 SkBitmap::Config config = bitmap.getConfig(); 286 bool alphaOnly = (config == SkBitmap::kA1_Config || 287 config == SkBitmap::kA8_Config); 288 289 insertName("Type", "XObject"); 290 insertName("Subtype", "Image"); 291 292 if (!doingAlpha && alphaOnly) { 293 // For alpha only images, we stretch a single pixel of black for 294 // the color/shape part. 295 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); 296 insert("Width", one.get()); 297 insert("Height", one.get()); 298 } else { 299 insertInt("Width", srcRect.width()); 300 insertInt("Height", srcRect.height()); 301 } 302 303 // if (!image mask) { 304 if (doingAlpha || alphaOnly) { 305 insertName("ColorSpace", "DeviceGray"); 306 } else if (config == SkBitmap::kIndex8_Config) { 307 SkAutoLockPixels alp(bitmap); 308 insert("ColorSpace", 309 makeIndexedColorSpace(bitmap.getColorTable()))->unref(); 310 } else { 311 insertName("ColorSpace", "DeviceRGB"); 312 } 313 // } 314 315 int bitsPerComp = 8; 316 if (config == SkBitmap::kARGB_4444_Config) { 317 bitsPerComp = 4; 318 } else if (doingAlpha && config == SkBitmap::kA1_Config) { 319 bitsPerComp = 1; 320 } 321 insertInt("BitsPerComponent", bitsPerComp); 322 323 if (config == SkBitmap::kRGB_565_Config) { 324 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); 325 SkAutoTUnref<SkPDFScalar> scale5Val( 326 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 327 SkAutoTUnref<SkPDFScalar> scale6Val( 328 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 329 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); 330 decodeValue->reserve(6); 331 decodeValue->append(zeroVal.get()); 332 decodeValue->append(scale5Val.get()); 333 decodeValue->append(zeroVal.get()); 334 decodeValue->append(scale6Val.get()); 335 decodeValue->append(zeroVal.get()); 336 decodeValue->append(scale5Val.get()); 337 insert("Decode", decodeValue.get()); 338 } 339 } 340