xref: /aosp_15_r20/external/skia/src/utils/mac/SkCreateCGImageRef.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/encode/SkICC.h"
15 #include "include/private/SkColorData.h"
16 #include "include/private/base/SkMacros.h"
17 #include "include/private/base/SkTo.h"
18 #include "include/utils/mac/SkCGUtils.h"
19 #include "src/utils/mac/SkUniqueCFRef.h"
20 
21 #include <climits>
22 #include <memory>
23 
compute_cgalpha_info_rgba(SkAlphaType at)24 static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) {
25     CGBitmapInfo info = kCGBitmapByteOrder32Big;
26     switch (at) {
27         case kUnknown_SkAlphaType:                                          break;
28         case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipLast;      break;
29         case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedLast; break;
30         case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast;              break;
31     }
32     return info;
33 }
34 
compute_cgalpha_info_bgra(SkAlphaType at)35 static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) {
36     CGBitmapInfo info = kCGBitmapByteOrder32Little;
37     switch (at) {
38         case kUnknown_SkAlphaType:                                           break;
39         case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipFirst;      break;
40         case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedFirst; break;
41         case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst;              break;
42     }
43     return info;
44 }
compute_cgalpha_info_4444(SkAlphaType at)45 static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) {
46     CGBitmapInfo info = kCGBitmapByteOrder16Little;
47     switch (at) {
48         case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast;      break;
49         default:                  info |= kCGImageAlphaPremultipliedLast; break;
50     }
51     return info;
52 }
53 
get_bitmap_info(SkColorType skColorType,SkAlphaType skAlphaType,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)54 static bool get_bitmap_info(SkColorType skColorType,
55                             SkAlphaType skAlphaType,
56                             size_t* bitsPerComponent,
57                             CGBitmapInfo* info,
58                             bool* upscaleTo32) {
59     if (upscaleTo32) {
60         *upscaleTo32 = false;
61     }
62     switch (skColorType) {
63         case kRGB_565_SkColorType:
64             if (upscaleTo32) {
65                 *upscaleTo32 = true;
66             }
67             // now treat like RGBA
68             *bitsPerComponent = 8;
69             *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType);
70             break;
71         case kRGBA_8888_SkColorType:
72             *bitsPerComponent = 8;
73             *info = compute_cgalpha_info_rgba(skAlphaType);
74             break;
75         case kBGRA_8888_SkColorType:
76             *bitsPerComponent = 8;
77             *info = compute_cgalpha_info_bgra(skAlphaType);
78             break;
79         case kARGB_4444_SkColorType:
80             *bitsPerComponent = 4;
81             *info = compute_cgalpha_info_4444(skAlphaType);
82             break;
83         default:
84             return false;
85     }
86     return true;
87 }
88 
prepare_for_image_ref(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)89 static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm,
90                                                        size_t* bitsPerComponent,
91                                                        CGBitmapInfo* info) {
92     bool upscaleTo32;
93     if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) {
94         return nullptr;
95     }
96     if (upscaleTo32) {
97         std::unique_ptr<SkBitmap> copy(new SkBitmap);
98         // here we make a deep copy of the pixels, since CG won't take our
99         // 565 directly, so we always go to RGBA
100         copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType));
101         bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
102         return copy;
103     }
104     return std::make_unique<SkBitmap>(bm);
105 }
106 
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)107 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
108                                             CGColorSpaceRef colorSpace) {
109     return SkCreateCGImageRef(bm);
110 }
111 
SkCreateCGImageRef(const SkBitmap & bm)112 CGImageRef SkCreateCGImageRef(const SkBitmap& bm) {
113     if (bm.drawsNothing()) {
114         return nullptr;
115     }
116     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
117     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
118 
119     std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info);
120     if (nullptr == bitmap) {
121         return nullptr;
122     }
123 
124     SkPixmap pm = bitmap->pixmap();  // Copy bitmap info before releasing it.
125     const size_t s = bitmap->computeByteSize();
126     void* pixels = bitmap->getPixels();
127 
128     // our provider "owns" the bitmap*, and will take care of deleting it
129     SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData(
130             bitmap.release(), pixels, s,
131             [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); }));
132 
133     SkUniqueCFRef<CGColorSpaceRef> colorSpace(SkCreateCGColorSpace(bm.colorSpace()));
134     return CGImageCreate(pm.width(),
135                          pm.height(),
136                          bitsPerComponent,
137                          pm.info().bytesPerPixel() * CHAR_BIT,
138                          pm.rowBytes(),
139                          colorSpace.get(),
140                          info,
141                          dataRef.get(),
142                          nullptr,
143                          false,
144                          kCGRenderingIntentDefault);
145 }
146 
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)147 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
148     SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm));
149 
150     if (img) {
151         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
152 
153         CGContextSaveGState(cg);
154         CGContextTranslateCTM(cg, x, r.size.height + y);
155         CGContextScaleCTM(cg, 1, -1);
156 
157         CGContextDrawImage(cg, r, img.get());
158 
159         CGContextRestoreGState(cg);
160     }
161 }
162 
163 ///////////////////////////////////////////////////////////////////////////////////////////////////
164 
SkCreateCGContext(const SkPixmap & pmap)165 CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
166     CGBitmapInfo cg_bitmap_info = 0;
167     size_t bitsPerComponent = 0;
168     switch (pmap.colorType()) {
169         case kRGBA_8888_SkColorType:
170             bitsPerComponent = 8;
171             cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType());
172             break;
173         case kBGRA_8888_SkColorType:
174             bitsPerComponent = 8;
175             cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType());
176             break;
177         default:
178             return nullptr;   // no other colortypes are supported (for now)
179     }
180 
181     size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
182     SkUniqueCFRef<CGColorSpaceRef> cs(SkCreateCGColorSpace(pmap.colorSpace()));
183     CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
184                                             bitsPerComponent, rb, cs.get(), cg_bitmap_info);
185     return cg;
186 }
187 
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)188 bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
189                              CGImageRef image) {
190     CGBitmapInfo cg_bitmap_info = 0;
191     size_t bitsPerComponent = 0;
192     switch (info.colorType()) {
193         case kRGBA_8888_SkColorType:
194             bitsPerComponent = 8;
195             cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType());
196             break;
197         case kBGRA_8888_SkColorType:
198             bitsPerComponent = 8;
199             cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType());
200             break;
201         default:
202             return false;   // no other colortypes are supported (for now)
203     }
204 
205     SkUniqueCFRef<CGColorSpaceRef> cs(SkCreateCGColorSpace(info.colorSpace()));
206     SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate(
207                 pixels, info.width(), info.height(), bitsPerComponent,
208                 rowBytes, cs.get(), cg_bitmap_info));
209     if (!cg) {
210         return false;
211     }
212 
213     // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
214     // any blending (which could introduce errors and be slower).
215     CGContextSetBlendMode(cg.get(), kCGBlendModeCopy);
216 
217     CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image);
218     return true;
219 }
220 
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image)221 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
222     const int width = SkToInt(CGImageGetWidth(image));
223     const int height = SkToInt(CGImageGetHeight(image));
224     sk_sp<SkColorSpace> colorSpace(SkMakeColorSpaceFromCGColorSpace(CGImageGetColorSpace(image)));
225     SkImageInfo info = SkImageInfo::MakeN32Premul(width, height, colorSpace);
226 
227     SkBitmap tmp;
228     if (!tmp.tryAllocPixels(info)) {
229         return false;
230     }
231 
232     if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
233         return false;
234     }
235 
236     CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
237     switch (cgInfo) {
238         case kCGImageAlphaNone:
239         case kCGImageAlphaNoneSkipLast:
240         case kCGImageAlphaNoneSkipFirst:
241             SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
242             tmp.setAlphaType(kOpaque_SkAlphaType);
243             break;
244         default:
245             // we don't know if we're opaque or not, so compute it.
246             if (SkBitmap::ComputeIsOpaque(tmp)) {
247                 tmp.setAlphaType(kOpaque_SkAlphaType);
248             }
249     }
250 
251     *dst = tmp;
252     return true;
253 }
254 
SkMakeImageFromCGImage(CGImageRef src)255 sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
256     SkBitmap bm;
257     if (!SkCreateBitmapFromCGImage(&bm, src)) {
258         return nullptr;
259     }
260 
261     bm.setImmutable();
262     return bm.asImage();
263 }
264 
SkCreateCGDataProvider(sk_sp<SkData> data)265 CGDataProviderRef SkCreateCGDataProvider(sk_sp<SkData> data) {
266     if (!data) {
267         return nullptr;
268     }
269 
270     CGDataProviderRef result = CGDataProviderCreateWithData(
271             data.get(), data->data(), data->size(), [](void* info, const void*, size_t) {
272                 reinterpret_cast<SkData*>(info)->unref();
273             });
274     if (!result) {
275         return nullptr;
276     }
277 
278     // Retain `data` for the release that will come when `result` is freed.
279     data->ref();
280     return result;
281 }
282 
SkMakeColorSpaceFromCGColorSpace(CGColorSpaceRef cgColorSpace)283 sk_sp<SkColorSpace> SkMakeColorSpaceFromCGColorSpace(CGColorSpaceRef cgColorSpace) {
284     if (!cgColorSpace) {
285         return nullptr;
286     }
287 
288     // Attempt to convert by name.
289     SkUniqueCFRef<CFStringRef> name(CGColorSpaceCopyName(cgColorSpace));
290     if (name && CFStringCompare(name.get(), kCGColorSpaceSRGB, 0) == kCFCompareEqualTo) {
291         return SkColorSpace::MakeSRGB();
292     }
293 
294     // Attempt to convert by parsing the ICC profile.
295     SkUniqueCFRef<CFDataRef> iccData(CGColorSpaceCopyICCData(cgColorSpace));
296     if (!iccData) {
297         return nullptr;
298     }
299     skcms_ICCProfile iccProfile;
300     if (!skcms_Parse(
301                 CFDataGetBytePtr(iccData.get()), CFDataGetLength(iccData.get()), &iccProfile)) {
302         return nullptr;
303     }
304     return SkColorSpace::Make(iccProfile);
305 }
306 
SkCreateCGColorSpace(const SkColorSpace * space)307 CGColorSpaceRef SkCreateCGColorSpace(const SkColorSpace* space) {
308     // Initialize result to sRGB. We will use this as the fallback on failure.
309     CGColorSpaceRef cgSRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
310 
311     // Early-out of this is sRGB (or nullptr defaulting to sRGB).
312     if (!space || space->isSRGB()) {
313         return cgSRGB;
314     }
315 
316     // Create an SkData with the ICC profile.
317     skcms_TransferFunction fn;
318     skcms_Matrix3x3 to_xyzd50;
319     space->transferFn(&fn);
320     space->toXYZD50(&to_xyzd50);
321     sk_sp<SkData> iccData = SkWriteICCProfile(fn, to_xyzd50);
322     if (!iccData) {
323         return cgSRGB;
324     }
325 
326     // Create a CGColorSpaceRef from that ICC data.
327     const size_t kNumComponents = 3;
328     const CGFloat kComponentRanges[6] = {0, 1, 0, 1, 0, 1};
329     SkUniqueCFRef<CGDataProviderRef> iccDataProvider(SkCreateCGDataProvider(iccData));
330     CGColorSpaceRef result = CGColorSpaceCreateICCBased(
331             kNumComponents, kComponentRanges, iccDataProvider.get(), cgSRGB);
332     if (!result) {
333         return cgSRGB;
334     }
335 
336     // We will not be returning |cgSRGB|, so free it now.
337     CFRelease(cgSRGB);
338     cgSRGB = nullptr;
339 
340     return result;
341 }
342 
343 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
344