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