1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkTypes.h"
9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10
11#include "include/core/SkBitmap.h"
12#include "include/private/SkColorData.h"
13#include "include/private/SkMacros.h"
14#include "include/private/SkTo.h"
15#include "include/utils/mac/SkCGUtils.h"
16#include "src/utils/mac/SkUniqueCFRef.h"
17
18#include <climits>
19
20static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) {
21 CGBitmapInfo info = kCGBitmapByteOrder32Big;
22 switch (at) {
23 case kUnknown_SkAlphaType: break;
24 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break;
25 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedLast; break;
26 case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast; break;
27 }
28 return info;
29}
30
31static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) {
32 CGBitmapInfo info = kCGBitmapByteOrder32Little;
33 switch (at) {
34 case kUnknown_SkAlphaType: break;
35 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipFirst; break;
36 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedFirst; break;
37 case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst; break;
38 }
39 return info;
40}
41static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) {
42 CGBitmapInfo info = kCGBitmapByteOrder16Little;
43 switch (at) {
44 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break;
45 default: info |= kCGImageAlphaPremultipliedLast; break;
46 }
47 return info;
48}
49
50static bool get_bitmap_info(SkColorType skColorType,
51 SkAlphaType skAlphaType,
52 size_t* bitsPerComponent,
53 CGBitmapInfo* info,
54 bool* upscaleTo32) {
55 if (upscaleTo32) {
56 *upscaleTo32 = false;
57 }
58 switch (skColorType) {
59 case kRGB_565_SkColorType:
60 if (upscaleTo32) {
61 *upscaleTo32 = true;
62 }
63 // now treat like RGBA
64 *bitsPerComponent = 8;
65 *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType);
66 break;
67 case kRGBA_8888_SkColorType:
68 *bitsPerComponent = 8;
69 *info = compute_cgalpha_info_rgba(skAlphaType);
70 break;
71 case kBGRA_8888_SkColorType:
72 *bitsPerComponent = 8;
73 *info = compute_cgalpha_info_bgra(skAlphaType);
74 break;
75 case kARGB_4444_SkColorType:
76 *bitsPerComponent = 4;
77 *info = compute_cgalpha_info_4444(skAlphaType);
78 break;
79 default:
80 return false;
81 }
82 return true;
83}
84
85static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm,
86 size_t* bitsPerComponent,
87 CGBitmapInfo* info) {
88 bool upscaleTo32;
89 if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) {
90 return nullptr;
91 }
92 if (upscaleTo32) {
93 std::unique_ptr<SkBitmap> copy(new SkBitmap);
94 // here we make a deep copy of the pixels, since CG won't take our
95 // 565 directly, so we always go to RGBA
96 copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType));
97 bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
98 return copy;
99 }
100 return std::unique_ptr<SkBitmap>(new SkBitmap(bm));
101}
102
103CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
104 CGColorSpaceRef colorSpace) {
105 if (bm.drawsNothing()) {
106 return nullptr;
107 }
108 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
109 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
110
111 std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info);
112 if (nullptr == bitmap) {
113 return nullptr;
114 }
115
116 SkPixmap pm = bitmap->pixmap(); // Copy bitmap info before releasing it.
117 const size_t s = bitmap->computeByteSize();
118 void* pixels = bitmap->getPixels();
119
120 // our provider "owns" the bitmap*, and will take care of deleting it
121 SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData(
122 bitmap.release(), pixels, s,
123 [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); }));
124
125 SkUniqueCFRef<CGColorSpaceRef> rgb;
126 if (nullptr == colorSpace) {
127 rgb.reset(CGColorSpaceCreateDeviceRGB());
128 colorSpace = rgb.get();
129 }
130 return CGImageCreate(pm.width(), pm.height(), bitsPerComponent,
131 pm.info().bytesPerPixel() * CHAR_BIT, pm.rowBytes(), colorSpace,
132 info, dataRef.get(), nullptr, false, kCGRenderingIntentDefault);
133}
134
135void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
136 SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm));
137
138 if (img) {
139 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
140
141 CGContextSaveGState(cg);
142 CGContextTranslateCTM(cg, x, r.size.height + y);
143 CGContextScaleCTM(cg, 1, -1);
144
145 CGContextDrawImage(cg, r, img.get());
146
147 CGContextRestoreGState(cg);
148 }
149}
150
151///////////////////////////////////////////////////////////////////////////////////////////////////
152
153CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
154 CGBitmapInfo cg_bitmap_info = 0;
155 size_t bitsPerComponent = 0;
156 switch (pmap.colorType()) {
157 case kRGBA_8888_SkColorType:
158 bitsPerComponent = 8;
159 cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType());
160 break;
161 case kBGRA_8888_SkColorType:
162 bitsPerComponent = 8;
163 cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType());
164 break;
165 default:
166 return nullptr; // no other colortypes are supported (for now)
167 }
168
169 size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
170 SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
171 CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
172 bitsPerComponent, rb, cs.get(), cg_bitmap_info);
173 return cg;
174}
175
176bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
177 CGImageRef image) {
178 CGBitmapInfo cg_bitmap_info = 0;
179 size_t bitsPerComponent = 0;
180 switch (info.colorType()) {
181 case kRGBA_8888_SkColorType:
182 bitsPerComponent = 8;
183 cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType());
184 break;
185 case kBGRA_8888_SkColorType:
186 bitsPerComponent = 8;
187 cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType());
188 break;
189 default:
190 return false; // no other colortypes are supported (for now)
191 }
192
193 SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
194 SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate(
195 pixels, info.width(), info.height(), bitsPerComponent,
196 rowBytes, cs.get(), cg_bitmap_info));
197 if (!cg) {
198 return false;
199 }
200
201 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
202 // any blending (which could introduce errors and be slower).
203 CGContextSetBlendMode(cg.get(), kCGBlendModeCopy);
204
205 CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image);
206 return true;
207}
208
209bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
210 const int width = SkToInt(CGImageGetWidth(image));
211 const int height = SkToInt(CGImageGetHeight(image));
212 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
213
214 SkBitmap tmp;
215 if (!tmp.tryAllocPixels(info)) {
216 return false;
217 }
218
219 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
220 return false;
221 }
222
223 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
224 switch (cgInfo) {
225 case kCGImageAlphaNone:
226 case kCGImageAlphaNoneSkipLast:
227 case kCGImageAlphaNoneSkipFirst:
228 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
229 tmp.setAlphaType(kOpaque_SkAlphaType);
230 break;
231 default:
232 // we don't know if we're opaque or not, so compute it.
233 if (SkBitmap::ComputeIsOpaque(tmp)) {
234 tmp.setAlphaType(kOpaque_SkAlphaType);
235 }
236 }
237
238 *dst = tmp;
239 return true;
240}
241
242sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
243 SkBitmap bm;
244 if (!SkCreateBitmapFromCGImage(&bm, src)) {
245 return nullptr;
246 }
247
248 bm.setImmutable();
249 return SkImage::MakeFromBitmap(bm);
250}
251
252#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
253