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