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