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