blob: d41de18ad7b9159d07316ba760cd35524e3f95aa [file] [log] [blame]
reed@android.com0d55f1e2008-12-18 19:26:11 +00001#include "SkCGUtils.h"
reed@android.com758b1292008-12-18 17:54:12 +00002#include "SkBitmap.h"
reed@android.coma545a552009-06-29 17:07:19 +00003#include "SkColorPriv.h"
reed@android.com758b1292008-12-18 17:54:12 +00004
reed@android.com2b26cac2008-12-22 02:33:11 +00005static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
reed@android.com758b1292008-12-18 17:54:12 +00006 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
7 delete bitmap;
8}
9
reed@android.coma545a552009-06-29 17:07:19 +000010#define HAS_ARGB_SHIFTS(a, r, g, b) \
11 (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
12 && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
13
reed@google.com9c16bc02011-06-28 21:52:34 +000014static bool getBitmapInfo(const SkBitmap& bm,
15 size_t* bitsPerComponent,
16 CGBitmapInfo* info,
17 bool* upscaleTo32) {
18 if (upscaleTo32) {
19 *upscaleTo32 = false;
20 }
21
reed@android.com758b1292008-12-18 17:54:12 +000022 switch (bm.config()) {
reed@android.com32a42492009-07-10 03:33:52 +000023 case SkBitmap::kRGB_565_Config:
reed@google.com9c16bc02011-06-28 21:52:34 +000024 if (upscaleTo32) {
25 *upscaleTo32 = true;
26 }
reed@android.com32a42492009-07-10 03:33:52 +000027 // fall through
reed@android.com758b1292008-12-18 17:54:12 +000028 case SkBitmap::kARGB_8888_Config:
29 *bitsPerComponent = 8;
reed@android.com8ede4922009-06-22 20:04:33 +000030#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \
reed@google.com9c16bc02011-06-28 21:52:34 +000031|| defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8)
reed@google.com62f46592011-01-05 15:49:43 +000032 *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
reed@android.com8ede4922009-06-22 20:04:33 +000033#elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \
reed@google.com9c16bc02011-06-28 21:52:34 +000034|| defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
reed@android.com8ede4922009-06-22 20:04:33 +000035 // Matches the CGBitmapInfo that Apple recommends for best
36 // performance, used by google chrome.
reed@google.com62f46592011-01-05 15:49:43 +000037 *info = kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst;
reed@android.com8ede4922009-06-22 20:04:33 +000038#else
reed@google.com9c16bc02011-06-28 21:52:34 +000039 // ...add more formats as required...
reed@android.com8ede4922009-06-22 20:04:33 +000040#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
reed@google.com9c16bc02011-06-28 21:52:34 +000041This will probably not work.
reed@android.com8ede4922009-06-22 20:04:33 +000042 // Legacy behavior. Perhaps turn this into an error at some
43 // point.
reed@google.com62f46592011-01-05 15:49:43 +000044 *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
reed@android.com8ede4922009-06-22 20:04:33 +000045#endif
reed@android.com758b1292008-12-18 17:54:12 +000046 break;
reed@android.com32a42492009-07-10 03:33:52 +000047#if 0
reed@android.com0680d6c2008-12-19 19:46:15 +000048 case SkBitmap::kRGB_565_Config:
49 // doesn't see quite right. Are they thinking 1555?
50 *bitsPerComponent = 5;
51 *info = kCGBitmapByteOrder16Little;
52 break;
reed@android.com32a42492009-07-10 03:33:52 +000053#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000054 case SkBitmap::kARGB_4444_Config:
55 *bitsPerComponent = 4;
56 *info = kCGBitmapByteOrder16Little | kCGImageAlphaPremultipliedLast;
57 break;
reed@android.com758b1292008-12-18 17:54:12 +000058 default:
reed@google.com9c16bc02011-06-28 21:52:34 +000059 return false;
60 }
61 return true;
62}
63
64static SkBitmap* prepareForImageRef(const SkBitmap& bm,
65 size_t* bitsPerComponent,
66 CGBitmapInfo* info) {
67 bool upscaleTo32;
68 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
69 return NULL;
reed@android.com758b1292008-12-18 17:54:12 +000070 }
71
reed@android.com32a42492009-07-10 03:33:52 +000072 SkBitmap* copy;
73 if (upscaleTo32) {
74 copy = new SkBitmap;
75 // here we make a ceep copy of the pixels, since CG won't take our
76 // 565 directly
77 bm.copyTo(copy, SkBitmap::kARGB_8888_Config);
78 } else {
79 copy = new SkBitmap(bm);
80 }
81 return copy;
reed@android.com758b1292008-12-18 17:54:12 +000082}
83
reed@android.coma545a552009-06-29 17:07:19 +000084#undef HAS_ARGB_SHIFTS
85
reed@android.com38669c12011-01-03 13:48:50 +000086CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
87 CGColorSpaceRef colorSpace) {
88 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
89 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +000090
91 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
92 if (NULL == bitmap) {
93 return NULL;
94 }
95
96 const int w = bitmap->width();
97 const int h = bitmap->height();
98 const size_t s = bitmap->getSize();
99
reed@android.com758b1292008-12-18 17:54:12 +0000100 // our provider "owns" the bitmap*, and will take care of deleting it
reed@android.com2b26cac2008-12-22 02:33:11 +0000101 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
102 // proc, which will in turn unlock the pixels
103 bitmap->lockPixels();
104 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
105 SkBitmap_ReleaseInfo);
106
reed@android.com38669c12011-01-03 13:48:50 +0000107 bool releaseColorSpace = false;
108 if (NULL == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000109 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000110 releaseColorSpace = true;
111 }
112
reed@android.com758b1292008-12-18 17:54:12 +0000113 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
114 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000115 bitmap->rowBytes(), colorSpace, info, dataRef,
reed@android.com758b1292008-12-18 17:54:12 +0000116 NULL, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000117
118 if (releaseColorSpace) {
119 CGColorSpaceRelease(colorSpace);
120 }
reed@android.com758b1292008-12-18 17:54:12 +0000121 CGDataProviderRelease(dataRef);
122 return ref;
123}
124
reed@android.comf2b98d62010-12-20 18:26:13 +0000125void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
126 CGImageRef img = SkCreateCGImageRef(bm);
127
128 if (img) {
129 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
reed@google.com62f46592011-01-05 15:49:43 +0000130
reed@android.comf2b98d62010-12-20 18:26:13 +0000131 CGContextSaveGState(cg);
132 CGContextTranslateCTM(cg, x, r.size.height + y);
133 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000134
reed@android.comf2b98d62010-12-20 18:26:13 +0000135 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000136
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000138
reed@android.comf2b98d62010-12-20 18:26:13 +0000139 CGImageRelease(img);
140 }
141}
142
reed@google.com292ade62011-06-28 20:54:03 +0000143///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000144
reed@google.com292ade62011-06-28 20:54:03 +0000145#include "SkStream.h"
146
147class SkAutoPDFRelease {
148public:
149 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
150 ~SkAutoPDFRelease() {
151 if (fDoc) {
152 CGPDFDocumentRelease(fDoc);
153 }
154 }
155private:
156 CGPDFDocumentRef fDoc;
157};
158
159static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
160 size_t size) {
161 sk_free((void*)data);
162}
163
164bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
165 size_t size = stream->getLength();
166 void* ptr = sk_malloc_throw(size);
167 stream->read(ptr, size);
168 CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
169 CGDataProviderReleaseData_FromMalloc);
170 if (NULL == data) {
171 return false;
172 }
173
174 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
175 CGDataProviderRelease(data);
176 if (NULL == pdf) {
177 return false;
178 }
179 SkAutoPDFRelease releaseMe(pdf);
180
181 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
182 if (NULL == page) {
183 return false;
184 }
185
186 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
187
188 int w = (int)CGRectGetWidth(bounds);
189 int h = (int)CGRectGetHeight(bounds);
reed@google.com9c16bc02011-06-28 21:52:34 +0000190
reed@google.com292ade62011-06-28 20:54:03 +0000191 SkBitmap bitmap;
192 bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
193 bitmap.allocPixels();
194 bitmap.eraseColor(SK_ColorWHITE);
reed@google.com9c16bc02011-06-28 21:52:34 +0000195
196 size_t bitsPerComponent;
197 CGBitmapInfo info;
198 getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
reed@google.com292ade62011-06-28 20:54:03 +0000199
reed@google.com292ade62011-06-28 20:54:03 +0000200 CGContextRef ctx = CGBitmapContextCreateWithData(bitmap.getPixels(),
reed@google.com9c16bc02011-06-28 21:52:34 +0000201 w, h, bitsPerComponent,
202 bitmap.rowBytes(),
reed@google.com292ade62011-06-28 20:54:03 +0000203 CGColorSpaceCreateDeviceRGB(),
204 info, NULL, NULL);
205 if (ctx) {
206 CGContextDrawPDFPage(ctx, page);
207 CGContextRelease(ctx);
208 }
209
210 output->swap(bitmap);
211 return true;
212}
reed@android.com758b1292008-12-18 17:54:12 +0000213