/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkCGUtils.h" #include "SkBitmap.h" #include "SkColorPriv.h" static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) { SkBitmap* bitmap = reinterpret_cast(info); delete bitmap; } #define HAS_ARGB_SHIFTS(a, r, g, b) \ (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \ && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b)) static bool getBitmapInfo(const SkBitmap& bm, size_t* bitsPerComponent, CGBitmapInfo* info, bool* upscaleTo32) { if (upscaleTo32) { *upscaleTo32 = false; } switch (bm.config()) { case SkBitmap::kRGB_565_Config: if (upscaleTo32) { *upscaleTo32 = true; } // fall through case SkBitmap::kARGB_8888_Config: *bitsPerComponent = 8; #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \ || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8) *info = kCGBitmapByteOrder32Big; if (bm.isOpaque()) { *info |= kCGImageAlphaNoneSkipLast; } else { *info |= kCGImageAlphaPremultipliedLast; } #elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \ || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) // Matches the CGBitmapInfo that Apple recommends for best // performance, used by google chrome. *info = kCGBitmapByteOrder32Little; if (bm.isOpaque()) { *info |= kCGImageAlphaNoneSkipFirst; } else { *info |= kCGImageAlphaPremultipliedFirst; } #else // ...add more formats as required... #warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \ This will probably not work. // Legacy behavior. Perhaps turn this into an error at some // point. *info = kCGBitmapByteOrder32Big; if (bm.isOpaque()) { *info |= kCGImageAlphaNoneSkipLast; } else { *info |= kCGImageAlphaPremultipliedLast; } #endif break; #if 0 case SkBitmap::kRGB_565_Config: // doesn't see quite right. Are they thinking 1555? *bitsPerComponent = 5; *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone; break; #endif case SkBitmap::kARGB_4444_Config: *bitsPerComponent = 4; *info = kCGBitmapByteOrder16Little; if (bm.isOpaque()) { *info |= kCGImageAlphaNoneSkipLast; } else { *info |= kCGImageAlphaPremultipliedLast; } break; default: return false; } return true; } static SkBitmap* prepareForImageRef(const SkBitmap& bm, size_t* bitsPerComponent, CGBitmapInfo* info) { bool upscaleTo32; if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) { return NULL; } SkBitmap* copy; if (upscaleTo32) { copy = new SkBitmap; // here we make a ceep copy of the pixels, since CG won't take our // 565 directly bm.copyTo(copy, SkBitmap::kARGB_8888_Config); } else { copy = new SkBitmap(bm); } return copy; } #undef HAS_ARGB_SHIFTS CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, CGColorSpaceRef colorSpace) { size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info); if (NULL == bitmap) { return NULL; } const int w = bitmap->width(); const int h = bitmap->height(); const size_t s = bitmap->getSize(); // our provider "owns" the bitmap*, and will take care of deleting it // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release // proc, which will in turn unlock the pixels bitmap->lockPixels(); CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s, SkBitmap_ReleaseInfo); bool releaseColorSpace = false; if (NULL == colorSpace) { colorSpace = CGColorSpaceCreateDeviceRGB(); releaseColorSpace = true; } CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, bitmap->bytesPerPixel() * 8, bitmap->rowBytes(), colorSpace, info, dataRef, NULL, false, kCGRenderingIntentDefault); if (releaseColorSpace) { CGColorSpaceRelease(colorSpace); } CGDataProviderRelease(dataRef); return ref; } void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { CGImageRef img = SkCreateCGImageRef(bm); if (img) { CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); CGContextSaveGState(cg); CGContextTranslateCTM(cg, x, r.size.height + y); CGContextScaleCTM(cg, 1, -1); CGContextDrawImage(cg, r, img); CGContextRestoreGState(cg); CGImageRelease(img); } } /////////////////////////////////////////////////////////////////////////////// #include "SkStream.h" class SkAutoPDFRelease { public: SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {} ~SkAutoPDFRelease() { if (fDoc) { CGPDFDocumentRelease(fDoc); } } private: CGPDFDocumentRef fDoc; }; static void CGDataProviderReleaseData_FromMalloc(void*, const void* data, size_t size) { sk_free((void*)data); } bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) { size_t size = stream->getLength(); void* ptr = sk_malloc_throw(size); stream->read(ptr, size); CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size, CGDataProviderReleaseData_FromMalloc); if (NULL == data) { return false; } CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data); CGDataProviderRelease(data); if (NULL == pdf) { return false; } SkAutoPDFRelease releaseMe(pdf); CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1); if (NULL == page) { return false; } CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); int w = (int)CGRectGetWidth(bounds); int h = (int)CGRectGetHeight(bounds); SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h); bitmap.allocPixels(); bitmap.eraseColor(SK_ColorWHITE); size_t bitsPerComponent; CGBitmapInfo info; getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL); CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h, bitsPerComponent, bitmap.rowBytes(), cs, info); CGColorSpaceRelease(cs); if (ctx) { CGContextDrawPDFPage(ctx, page); CGContextRelease(ctx); } output->swap(bitmap); return true; }