Home | History | Annotate | Download | only in pdf
      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 
     23 namespace {
     24 
     25 void 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 
    227 SkPDFArray* 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
    252 SkPDFImage* 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 
    279 SkPDFImage::~SkPDFImage() {
    280     fResources.unrefAll();
    281 }
    282 
    283 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
    284     fResources.push(mask);
    285     mask->ref();
    286     insert("SMask", new SkPDFObjRef(mask))->unref();
    287     return mask;
    288 }
    289 
    290 void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) {
    291     GetResourcesHelper(&fResources, resourceList);
    292 }
    293 
    294 SkPDFImage::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         SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
    309         insert("Width", one.get());
    310         insert("Height", one.get());
    311     } else {
    312         insertInt("Width", srcRect.width());
    313         insertInt("Height", srcRect.height());
    314     }
    315 
    316     // if (!image mask) {
    317     if (doingAlpha || alphaOnly) {
    318         insertName("ColorSpace", "DeviceGray");
    319     } else if (config == SkBitmap::kIndex8_Config ||
    320         config == SkBitmap::kRLE_Index8_Config) {
    321         insert("ColorSpace",
    322                makeIndexedColorSpace(bitmap.getColorTable()))->unref();
    323     } else {
    324         insertName("ColorSpace", "DeviceRGB");
    325     }
    326     // }
    327 
    328     int bitsPerComp = 8;
    329     if (config == SkBitmap::kARGB_4444_Config) {
    330         bitsPerComp = 4;
    331     } else if (doingAlpha && config == SkBitmap::kA1_Config) {
    332         bitsPerComp = 1;
    333     }
    334     insertInt("BitsPerComponent", bitsPerComp);
    335 
    336     if (config == SkBitmap::kRGB_565_Config) {
    337         SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
    338         SkAutoTUnref<SkPDFScalar> scale5Val(
    339                 new SkPDFScalar(SkFloatToScalar(8.2258f)));  // 255/2^5-1
    340         SkAutoTUnref<SkPDFScalar> scale6Val(
    341                 new SkPDFScalar(SkFloatToScalar(4.0476f)));  // 255/2^6-1
    342         SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
    343         decodeValue->reserve(6);
    344         decodeValue->append(zeroVal.get());
    345         decodeValue->append(scale5Val.get());
    346         decodeValue->append(zeroVal.get());
    347         decodeValue->append(scale6Val.get());
    348         decodeValue->append(zeroVal.get());
    349         decodeValue->append(scale5Val.get());
    350         insert("Decode", decodeValue.get());
    351     }
    352 }
    353