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 CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
     13     CGBitmapInfo info = kCGBitmapByteOrder32Big;
     14     switch (at) {
     15         case kOpaque_SkAlphaType:
     16         case kIgnore_SkAlphaType:
     17             info |= kCGImageAlphaNoneSkipLast;
     18             break;
     19         case kPremul_SkAlphaType:
     20             info |= kCGImageAlphaPremultipliedLast;
     21             break;
     22         case kUnpremul_SkAlphaType:
     23             info |= kCGImageAlphaLast;
     24             break;
     25     }
     26     return info;
     27 }
     28 
     29 static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
     30     CGBitmapInfo info = kCGBitmapByteOrder32Little;
     31     switch (at) {
     32         case kOpaque_SkAlphaType:
     33         case kIgnore_SkAlphaType:
     34             info |= kCGImageAlphaNoneSkipFirst;
     35             break;
     36         case kPremul_SkAlphaType:
     37             info |= kCGImageAlphaPremultipliedFirst;
     38             break;
     39         case kUnpremul_SkAlphaType:
     40             info |= kCGImageAlphaFirst;
     41             break;
     42     }
     43     return info;
     44 }
     45 
     46 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
     47     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
     48     delete bitmap;
     49 }
     50 
     51 static bool getBitmapInfo(const SkBitmap& bm,
     52                           size_t* bitsPerComponent,
     53                           CGBitmapInfo* info,
     54                           bool* upscaleTo32) {
     55     if (upscaleTo32) {
     56         *upscaleTo32 = false;
     57     }
     58 
     59     switch (bm.colorType()) {
     60         case kRGB_565_SkColorType:
     61 #if 0
     62             // doesn't see quite right. Are they thinking 1555?
     63             *bitsPerComponent = 5;
     64             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
     65 #else
     66             if (upscaleTo32) {
     67                 *upscaleTo32 = true;
     68             }
     69             // now treat like RGBA
     70             *bitsPerComponent = 8;
     71             *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
     72 #endif
     73             break;
     74         case kRGBA_8888_SkColorType:
     75             *bitsPerComponent = 8;
     76             *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
     77             break;
     78         case kBGRA_8888_SkColorType:
     79             *bitsPerComponent = 8;
     80             *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
     81             break;
     82         case kARGB_4444_SkColorType:
     83             *bitsPerComponent = 4;
     84             *info = kCGBitmapByteOrder16Little;
     85             if (bm.isOpaque()) {
     86                 *info |= kCGImageAlphaNoneSkipLast;
     87             } else {
     88                 *info |= kCGImageAlphaPremultipliedLast;
     89             }
     90             break;
     91         default:
     92             return false;
     93     }
     94     return true;
     95 }
     96 
     97 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
     98                                     size_t* bitsPerComponent,
     99                                     CGBitmapInfo* info) {
    100     bool upscaleTo32;
    101     if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
    102         return NULL;
    103     }
    104 
    105     SkBitmap* copy;
    106     if (upscaleTo32) {
    107         copy = new SkBitmap;
    108         // here we make a ceep copy of the pixels, since CG won't take our
    109         // 565 directly
    110         bm.copyTo(copy, kN32_SkColorType);
    111     } else {
    112         copy = new SkBitmap(bm);
    113     }
    114     return copy;
    115 }
    116 
    117 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
    118                                             CGColorSpaceRef colorSpace) {
    119     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
    120     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
    121 
    122     SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
    123     if (NULL == bitmap) {
    124         return NULL;
    125     }
    126 
    127     const int w = bitmap->width();
    128     const int h = bitmap->height();
    129     const size_t s = bitmap->getSize();
    130 
    131     // our provider "owns" the bitmap*, and will take care of deleting it
    132     // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
    133     // proc, which will in turn unlock the pixels
    134     bitmap->lockPixels();
    135     CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
    136                                                              SkBitmap_ReleaseInfo);
    137 
    138     bool releaseColorSpace = false;
    139     if (NULL == colorSpace) {
    140         colorSpace = CGColorSpaceCreateDeviceRGB();
    141         releaseColorSpace = true;
    142     }
    143 
    144     CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
    145                                    bitmap->bytesPerPixel() * 8,
    146                                    bitmap->rowBytes(), colorSpace, info, dataRef,
    147                                    NULL, false, kCGRenderingIntentDefault);
    148 
    149     if (releaseColorSpace) {
    150         CGColorSpaceRelease(colorSpace);
    151     }
    152     CGDataProviderRelease(dataRef);
    153     return ref;
    154 }
    155 
    156 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
    157     CGImageRef img = SkCreateCGImageRef(bm);
    158 
    159     if (img) {
    160         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
    161 
    162         CGContextSaveGState(cg);
    163         CGContextTranslateCTM(cg, x, r.size.height + y);
    164         CGContextScaleCTM(cg, 1, -1);
    165 
    166         CGContextDrawImage(cg, r, img);
    167 
    168         CGContextRestoreGState(cg);
    169 
    170         CGImageRelease(img);
    171     }
    172 }
    173 
    174 ///////////////////////////////////////////////////////////////////////////////
    175 
    176 #include "SkStream.h"
    177 
    178 class SkAutoPDFRelease {
    179 public:
    180     SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
    181     ~SkAutoPDFRelease() {
    182         if (fDoc) {
    183             CGPDFDocumentRelease(fDoc);
    184         }
    185     }
    186 private:
    187     CGPDFDocumentRef fDoc;
    188 };
    189 #define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
    190 
    191 static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
    192                                                  size_t size) {
    193     sk_free((void*)data);
    194 }
    195 
    196 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
    197     size_t size = stream->getLength();
    198     void* ptr = sk_malloc_throw(size);
    199     stream->read(ptr, size);
    200     CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
    201                                           CGDataProviderReleaseData_FromMalloc);
    202     if (NULL == data) {
    203         return false;
    204     }
    205 
    206     CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
    207     CGDataProviderRelease(data);
    208     if (NULL == pdf) {
    209         return false;
    210     }
    211     SkAutoPDFRelease releaseMe(pdf);
    212 
    213     CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
    214     if (NULL == page) {
    215         return false;
    216     }
    217 
    218     CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
    219 
    220     int w = (int)CGRectGetWidth(bounds);
    221     int h = (int)CGRectGetHeight(bounds);
    222 
    223     SkBitmap bitmap;
    224     if (!bitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) {
    225         return false;
    226     }
    227     bitmap.eraseColor(SK_ColorWHITE);
    228 
    229     size_t bitsPerComponent;
    230     CGBitmapInfo info;
    231     getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
    232 
    233     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
    234     CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
    235                                              bitsPerComponent, bitmap.rowBytes(),
    236                                              cs, info);
    237     CGColorSpaceRelease(cs);
    238 
    239     if (ctx) {
    240         CGContextDrawPDFPage(ctx, page);
    241         CGContextRelease(ctx);
    242     }
    243 
    244     output->swap(bitmap);
    245     return true;
    246 }
    247 
    248 ///////////////////////////////////////////////////////////////////////////////////////////////////
    249 
    250 SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
    251                                     CGImageRef image) {
    252     CGBitmapInfo cg_bitmap_info = 0;
    253     size_t bitsPerComponent = 0;
    254     switch (info.colorType()) {
    255         case kRGBA_8888_SkColorType:
    256             bitsPerComponent = 8;
    257             cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
    258             break;
    259         case kBGRA_8888_SkColorType:
    260             bitsPerComponent = 8;
    261             cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
    262             break;
    263         default:
    264             return false;   // no other colortypes are supported (for now)
    265     }
    266 
    267     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
    268     CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
    269                                             rowBytes, cs, cg_bitmap_info);
    270     CFRelease(cs);
    271     if (NULL == cg) {
    272         return false;
    273     }
    274 
    275     // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
    276     // any blending (which could introduce errors and be slower).
    277     CGContextSetBlendMode(cg, kCGBlendModeCopy);
    278 
    279     CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
    280     CGContextRelease(cg);
    281     return true;
    282 }
    283 
    284 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
    285     const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
    286     const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
    287     SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
    288 
    289     SkBitmap tmp;
    290     if (!tmp.allocPixels(info)) {
    291         return false;
    292     }
    293 
    294     if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
    295         return false;
    296     }
    297 
    298     CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
    299     switch (cgInfo) {
    300         case kCGImageAlphaNone:
    301         case kCGImageAlphaNoneSkipLast:
    302         case kCGImageAlphaNoneSkipFirst:
    303             SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
    304             tmp.setAlphaType(kOpaque_SkAlphaType);
    305             break;
    306         default:
    307             // we don't know if we're opaque or not, so compute it.
    308             if (SkBitmap::ComputeIsOpaque(tmp)) {
    309                 tmp.setAlphaType(kOpaque_SkAlphaType);
    310             }
    311     }
    312 
    313     *dst = tmp;
    314     return true;
    315 }
    316