1 2/* 3 * Copyright 2010 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkPDFImage.h" 11 12#include "SkBitmap.h" 13#include "SkColor.h" 14#include "SkColorPriv.h" 15#include "SkPaint.h" 16#include "SkPackBits.h" 17#include "SkPDFCatalog.h" 18#include "SkRect.h" 19#include "SkStream.h" 20#include "SkString.h" 21#include "SkUnPreMultiply.h" 22 23namespace { 24 25void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, 26 SkStream** imageData, SkStream** alphaData) { 27 SkMemoryStream* image = NULL; 28 SkMemoryStream* alpha = NULL; 29 bool hasAlpha = false; 30 bool isTransparent = false; 31 32 bitmap.lockPixels(); 33 switch (bitmap.getConfig()) { 34 case SkBitmap::kIndex8_Config: { 35 const int rowBytes = srcRect.width(); 36 image = new SkMemoryStream(rowBytes * srcRect.height()); 37 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 38 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 39 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); 40 dst += rowBytes; 41 } 42 break; 43 } 44 case SkBitmap::kRLE_Index8_Config: { 45 const int rowBytes = srcRect.width(); 46 image = new SkMemoryStream(rowBytes * srcRect.height()); 47 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 48 const SkBitmap::RLEPixels* rle = 49 (const SkBitmap::RLEPixels*)bitmap.getPixels(); 50 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 51 SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes, 52 rle->packedAtY(y)); 53 dst += rowBytes; 54 } 55 break; 56 } 57 case SkBitmap::kARGB_4444_Config: { 58 isTransparent = true; 59 const int rowBytes = (srcRect.width() * 3 + 1) / 2; 60 const int alphaRowBytes = (srcRect.width() + 1) / 2; 61 image = new SkMemoryStream(rowBytes * srcRect.height()); 62 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); 63 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 64 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); 65 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 66 uint16_t* src = bitmap.getAddr16(0, y); 67 int x; 68 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { 69 dst[0] = (SkGetPackedR4444(src[x]) << 4) | 70 SkGetPackedG4444(src[x]); 71 dst[1] = (SkGetPackedB4444(src[x]) << 4) | 72 SkGetPackedR4444(src[x + 1]); 73 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | 74 SkGetPackedB4444(src[x + 1]); 75 dst += 3; 76 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | 77 SkGetPackedA4444(src[x + 1]); 78 if (alphaDst[0] != 0xFF) { 79 hasAlpha = true; 80 } 81 if (alphaDst[0]) { 82 isTransparent = false; 83 } 84 alphaDst++; 85 } 86 if (srcRect.width() & 1) { 87 dst[0] = (SkGetPackedR4444(src[x]) << 4) | 88 SkGetPackedG4444(src[x]); 89 dst[1] = (SkGetPackedB4444(src[x]) << 4); 90 dst += 2; 91 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); 92 if (alphaDst[0] != 0xF0) { 93 hasAlpha = true; 94 } 95 if (alphaDst[0] & 0xF0) { 96 isTransparent = false; 97 } 98 alphaDst++; 99 } 100 } 101 break; 102 } 103 case SkBitmap::kRGB_565_Config: { 104 const int rowBytes = srcRect.width() * 3; 105 image = new SkMemoryStream(rowBytes * srcRect.height()); 106 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 107 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 108 uint16_t* src = bitmap.getAddr16(0, y); 109 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { 110 dst[0] = SkGetPackedR16(src[x]); 111 dst[1] = SkGetPackedG16(src[x]); 112 dst[2] = SkGetPackedB16(src[x]); 113 dst += 3; 114 } 115 } 116 break; 117 } 118 case SkBitmap::kARGB_8888_Config: { 119 isTransparent = true; 120 const int rowBytes = srcRect.width() * 3; 121 image = new SkMemoryStream(rowBytes * srcRect.height()); 122 alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); 123 uint8_t* dst = (uint8_t*)image->getMemoryBase(); 124 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); 125 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 126 uint32_t* src = bitmap.getAddr32(0, y); 127 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { 128 dst[0] = SkGetPackedR32(src[x]); 129 dst[1] = SkGetPackedG32(src[x]); 130 dst[2] = SkGetPackedB32(src[x]); 131 dst += 3; 132 alphaDst[0] = SkGetPackedA32(src[x]); 133 if (alphaDst[0] != 0xFF) { 134 hasAlpha = true; 135 } 136 if (alphaDst[0]) { 137 isTransparent = false; 138 } 139 alphaDst++; 140 } 141 } 142 break; 143 } 144 case SkBitmap::kA1_Config: { 145 isTransparent = true; 146 image = new SkMemoryStream(1); 147 ((uint8_t*)image->getMemoryBase())[0] = 0; 148 149 const int alphaRowBytes = (srcRect.width() + 7) / 8; 150 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); 151 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); 152 int offset1 = srcRect.fLeft % 8; 153 int offset2 = 8 - offset1; 154 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 155 uint8_t* src = bitmap.getAddr1(0, y); 156 // This may read up to one byte after src, but the potentially 157 // invalid bits are never used for computation. 158 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { 159 if (offset1) { 160 alphaDst[0] = src[x / 8] << offset1 | 161 src[x / 8 + 1] >> offset2; 162 } else { 163 alphaDst[0] = src[x / 8]; 164 } 165 if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) { 166 hasAlpha = true; 167 } 168 if (x + 7 < srcRect.fRight && alphaDst[0]) { 169 isTransparent = false; 170 } 171 alphaDst++; 172 } 173 // Calculate the mask of bits we're interested in within the 174 // last byte of alphaDst. 175 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE 176 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); 177 if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) { 178 hasAlpha = true; 179 } 180 if (srcRect.width() % 8 && (alphaDst[-1] & mask)) { 181 isTransparent = false; 182 } 183 } 184 break; 185 } 186 case SkBitmap::kA8_Config: { 187 isTransparent = true; 188 image = new SkMemoryStream(1); 189 ((uint8_t*)image->getMemoryBase())[0] = 0; 190 191 const int alphaRowBytes = srcRect.width(); 192 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); 193 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); 194 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 195 uint8_t* src = bitmap.getAddr8(0, y); 196 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { 197 alphaDst[0] = src[x]; 198 if (alphaDst[0] != 0xFF) { 199 hasAlpha = true; 200 } 201 if (alphaDst[0]) { 202 isTransparent = false; 203 } 204 alphaDst++; 205 } 206 } 207 break; 208 } 209 default: 210 SkASSERT(false); 211 } 212 bitmap.unlockPixels(); 213 214 if (isTransparent) { 215 SkSafeUnref(image); 216 } else { 217 *imageData = image; 218 } 219 220 if (isTransparent || !hasAlpha) { 221 SkSafeUnref(alpha); 222 } else { 223 *alphaData = alpha; 224 } 225} 226 227SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { 228 SkPDFArray* result = new SkPDFArray(); 229 result->reserve(4); 230 result->appendName("Indexed"); 231 result->appendName("DeviceRGB"); 232 result->appendInt(table->count() - 1); 233 234 // Potentially, this could be represented in fewer bytes with a stream. 235 // Max size as a string is 1.5k. 236 SkString index; 237 for (int i = 0; i < table->count(); i++) { 238 char buf[3]; 239 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); 240 buf[0] = SkGetPackedR32(color); 241 buf[1] = SkGetPackedG32(color); 242 buf[2] = SkGetPackedB32(color); 243 index.append(buf, 3); 244 } 245 result->append(new SkPDFString(index))->unref(); 246 return result; 247} 248 249}; // namespace 250 251// static 252SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, 253 const SkIRect& srcRect, 254 const SkPaint& paint) { 255 if (bitmap.getConfig() == SkBitmap::kNo_Config) { 256 return NULL; 257 } 258 259 SkStream* imageData = NULL; 260 SkStream* alphaData = NULL; 261 extractImageData(bitmap, srcRect, &imageData, &alphaData); 262 SkAutoUnref unrefImageData(imageData); 263 SkAutoUnref unrefAlphaData(alphaData); 264 if (!imageData) { 265 SkASSERT(!alphaData); 266 return NULL; 267 } 268 269 SkPDFImage* image = 270 new SkPDFImage(imageData, bitmap, srcRect, false, paint); 271 272 if (alphaData != NULL) { 273 image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true, 274 paint))->unref(); 275 } 276 return image; 277} 278 279SkPDFImage::~SkPDFImage() { 280 fResources.unrefAll(); 281} 282 283SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { 284 fResources.push(mask); 285 mask->ref(); 286 insert("SMask", new SkPDFObjRef(mask))->unref(); 287 return mask; 288} 289 290void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) { 291 GetResourcesHelper(&fResources, resourceList); 292} 293 294SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, 295 const SkIRect& srcRect, bool doingAlpha, 296 const SkPaint& paint) { 297 this->setData(imageData); 298 SkBitmap::Config config = bitmap.getConfig(); 299 bool alphaOnly = (config == SkBitmap::kA1_Config || 300 config == SkBitmap::kA8_Config); 301 302 insertName("Type", "XObject"); 303 insertName("Subtype", "Image"); 304 305 if (!doingAlpha && alphaOnly) { 306 // For alpha only images, we stretch a single pixel of black for 307 // the color/shape part. 308 SkRefPtr<SkPDFInt> one = new SkPDFInt(1); 309 one->unref(); // SkRefPtr and new both took a reference. 310 insert("Width", one.get()); 311 insert("Height", one.get()); 312 } else { 313 insertInt("Width", srcRect.width()); 314 insertInt("Height", srcRect.height()); 315 } 316 317 // if (!image mask) { 318 if (doingAlpha || alphaOnly) { 319 insertName("ColorSpace", "DeviceGray"); 320 } else if (config == SkBitmap::kIndex8_Config || 321 config == SkBitmap::kRLE_Index8_Config) { 322 insert("ColorSpace", 323 makeIndexedColorSpace(bitmap.getColorTable()))->unref(); 324 } else { 325 insertName("ColorSpace", "DeviceRGB"); 326 } 327 // } 328 329 int bitsPerComp = 8; 330 if (config == SkBitmap::kARGB_4444_Config) { 331 bitsPerComp = 4; 332 } else if (doingAlpha && config == SkBitmap::kA1_Config) { 333 bitsPerComp = 1; 334 } 335 insertInt("BitsPerComponent", bitsPerComp); 336 337 if (config == SkBitmap::kRGB_565_Config) { 338 SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0); 339 zeroVal->unref(); // SkRefPtr and new both took a reference. 340 SkRefPtr<SkPDFScalar> scale5Val = 341 new SkPDFScalar(8.2258f); // 255/2^5-1 342 scale5Val->unref(); // SkRefPtr and new both took a reference. 343 SkRefPtr<SkPDFScalar> scale6Val = 344 new SkPDFScalar(4.0476f); // 255/2^6-1 345 scale6Val->unref(); // SkRefPtr and new both took a reference. 346 SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray(); 347 decodeValue->unref(); // SkRefPtr and new both took a reference. 348 decodeValue->reserve(6); 349 decodeValue->append(zeroVal.get()); 350 decodeValue->append(scale5Val.get()); 351 decodeValue->append(zeroVal.get()); 352 decodeValue->append(scale6Val.get()); 353 decodeValue->append(zeroVal.get()); 354 decodeValue->append(scale5Val.get()); 355 insert("Decode", decodeValue.get()); 356 } 357} 358