Home | History | Annotate | Download | only in pdf
      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