Home | History | Annotate | Download | only in mac
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      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 #include "SkCGUtils.h"
      9 #include "SkBitmap.h"
     10 #include "SkColorPriv.h"
     11 
     12 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
     13     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
     14     delete bitmap;
     15 }
     16 
     17 #define HAS_ARGB_SHIFTS(a, r, g, b) \
     18     (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
     19     && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
     20 
     21 static bool getBitmapInfo(const SkBitmap& bm,
     22                           size_t* bitsPerComponent,
     23                           CGBitmapInfo* info,
     24                           bool* upscaleTo32) {
     25     if (upscaleTo32) {
     26         *upscaleTo32 = false;
     27     }
     28 
     29     switch (bm.config()) {
     30         case SkBitmap::kRGB_565_Config:
     31             if (upscaleTo32) {
     32                 *upscaleTo32 = true;
     33             }
     34             // fall through
     35         case SkBitmap::kARGB_8888_Config:
     36             *bitsPerComponent = 8;
     37 #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \
     38 || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8)
     39             *info = kCGBitmapByteOrder32Big;
     40             if (bm.isOpaque()) {
     41                 *info |= kCGImageAlphaNoneSkipLast;
     42             } else {
     43                 *info |= kCGImageAlphaPremultipliedLast;
     44             }
     45 #elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \
     46 || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
     47             // Matches the CGBitmapInfo that Apple recommends for best
     48             // performance, used by google chrome.
     49             *info = kCGBitmapByteOrder32Little;
     50             if (bm.isOpaque()) {
     51                 *info |= kCGImageAlphaNoneSkipFirst;
     52             } else {
     53                 *info |= kCGImageAlphaPremultipliedFirst;
     54             }
     55 #else
     56             // ...add more formats as required...
     57 #warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
     58 This will probably not work.
     59             // Legacy behavior. Perhaps turn this into an error at some
     60             // point.
     61             *info = kCGBitmapByteOrder32Big;
     62             if (bm.isOpaque()) {
     63                 *info |= kCGImageAlphaNoneSkipLast;
     64             } else {
     65                 *info |= kCGImageAlphaPremultipliedLast;
     66             }
     67 #endif
     68             break;
     69 #if 0
     70         case SkBitmap::kRGB_565_Config:
     71             // doesn't see quite right. Are they thinking 1555?
     72             *bitsPerComponent = 5;
     73             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
     74             break;
     75 #endif
     76         case SkBitmap::kARGB_4444_Config:
     77             *bitsPerComponent = 4;
     78             *info = kCGBitmapByteOrder16Little;
     79             if (bm.isOpaque()) {
     80                 *info |= kCGImageAlphaNoneSkipLast;
     81             } else {
     82                 *info |= kCGImageAlphaPremultipliedLast;
     83             }
     84             break;
     85         default:
     86             return false;
     87     }
     88     return true;
     89 }
     90 
     91 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
     92                                     size_t* bitsPerComponent,
     93                                     CGBitmapInfo* info) {
     94     bool upscaleTo32;
     95     if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
     96         return NULL;
     97     }
     98 
     99     SkBitmap* copy;
    100     if (upscaleTo32) {
    101         copy = new SkBitmap;
    102         // here we make a ceep copy of the pixels, since CG won't take our
    103         // 565 directly
    104         bm.copyTo(copy, SkBitmap::kARGB_8888_Config);
    105     } else {
    106         copy = new SkBitmap(bm);
    107     }
    108     return copy;
    109 }
    110 
    111 #undef HAS_ARGB_SHIFTS
    112 
    113 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
    114                                             CGColorSpaceRef colorSpace) {
    115     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
    116     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
    117 
    118     SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
    119     if (NULL == bitmap) {
    120         return NULL;
    121     }
    122 
    123     const int w = bitmap->width();
    124     const int h = bitmap->height();
    125     const size_t s = bitmap->getSize();
    126 
    127     // our provider "owns" the bitmap*, and will take care of deleting it
    128     // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
    129     // proc, which will in turn unlock the pixels
    130     bitmap->lockPixels();
    131     CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
    132                                                              SkBitmap_ReleaseInfo);
    133 
    134     bool releaseColorSpace = false;
    135     if (NULL == colorSpace) {
    136         colorSpace = CGColorSpaceCreateDeviceRGB();
    137         releaseColorSpace = true;
    138     }
    139 
    140     CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
    141                                    bitmap->bytesPerPixel() * 8,
    142                                    bitmap->rowBytes(), colorSpace, info, dataRef,
    143                                    NULL, false, kCGRenderingIntentDefault);
    144 
    145     if (releaseColorSpace) {
    146         CGColorSpaceRelease(colorSpace);
    147     }
    148     CGDataProviderRelease(dataRef);
    149     return ref;
    150 }
    151 
    152 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
    153     CGImageRef img = SkCreateCGImageRef(bm);
    154 
    155     if (img) {
    156         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
    157 
    158         CGContextSaveGState(cg);
    159         CGContextTranslateCTM(cg, x, r.size.height + y);
    160         CGContextScaleCTM(cg, 1, -1);
    161 
    162         CGContextDrawImage(cg, r, img);
    163 
    164         CGContextRestoreGState(cg);
    165 
    166         CGImageRelease(img);
    167     }
    168 }
    169 
    170 ///////////////////////////////////////////////////////////////////////////////
    171 
    172 #include "SkStream.h"
    173 
    174 class SkAutoPDFRelease {
    175 public:
    176     SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
    177     ~SkAutoPDFRelease() {
    178         if (fDoc) {
    179             CGPDFDocumentRelease(fDoc);
    180         }
    181     }
    182 private:
    183     CGPDFDocumentRef fDoc;
    184 };
    185 
    186 static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
    187                                                  size_t size) {
    188     sk_free((void*)data);
    189 }
    190 
    191 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
    192     size_t size = stream->getLength();
    193     void* ptr = sk_malloc_throw(size);
    194     stream->read(ptr, size);
    195     CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
    196                                           CGDataProviderReleaseData_FromMalloc);
    197     if (NULL == data) {
    198         return false;
    199     }
    200 
    201     CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
    202     CGDataProviderRelease(data);
    203     if (NULL == pdf) {
    204         return false;
    205     }
    206     SkAutoPDFRelease releaseMe(pdf);
    207 
    208     CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
    209     if (NULL == page) {
    210         return false;
    211     }
    212 
    213     CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
    214 
    215     int w = (int)CGRectGetWidth(bounds);
    216     int h = (int)CGRectGetHeight(bounds);
    217 
    218     SkBitmap bitmap;
    219     bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
    220     bitmap.allocPixels();
    221     bitmap.eraseColor(SK_ColorWHITE);
    222 
    223     size_t bitsPerComponent;
    224     CGBitmapInfo info;
    225     getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
    226 
    227     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
    228     CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
    229                                              bitsPerComponent, bitmap.rowBytes(),
    230                                              cs, info);
    231     CGColorSpaceRelease(cs);
    232 
    233     if (ctx) {
    234         CGContextDrawPDFPage(ctx, page);
    235         CGContextRelease(ctx);
    236     }
    237 
    238     output->swap(bitmap);
    239     return true;
    240 }
    241