1 /*
2 * Copyright 2008 The Android Open Source Project
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 "SkImageEncoderPriv.h"
9
10 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
11
12 #include "SkBitmap.h"
13 #include "SkCGUtils.h"
14 #include "SkColorPriv.h"
15 #include "SkData.h"
16 #include "SkStream.h"
17 #include "SkStreamPriv.h"
18 #include "SkTemplates.h"
19 #include "SkUnPreMultiply.h"
20
21 #ifdef SK_BUILD_FOR_MAC
22 #include <ApplicationServices/ApplicationServices.h>
23 #endif
24
25 #ifdef SK_BUILD_FOR_IOS
26 #include <CoreGraphics/CoreGraphics.h>
27 #include <ImageIO/ImageIO.h>
28 #include <MobileCoreServices/MobileCoreServices.h>
29 #endif
30
consumer_put(void * info,const void * buffer,size_t count)31 static size_t consumer_put(void* info, const void* buffer, size_t count) {
32 SkWStream* stream = reinterpret_cast<SkWStream*>(info);
33 return stream->write(buffer, count) ? count : 0;
34 }
35
consumer_release(void * info)36 static void consumer_release(void* info) {
37 // we do nothing, since by design we don't "own" the stream (i.e. info)
38 }
39
SkStreamToCGDataConsumer(SkWStream * stream)40 static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
41 CGDataConsumerCallbacks procs;
42 procs.putBytes = consumer_put;
43 procs.releaseConsumer = consumer_release;
44 // we don't own/reference the stream, so it our consumer must not live
45 // longer that our caller's ownership of the stream
46 return CGDataConsumerCreate(stream, &procs);
47 }
48
SkStreamToImageDestination(SkWStream * stream,CFStringRef type)49 static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
50 CFStringRef type) {
51 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
52 if (nullptr == consumer) {
53 return nullptr;
54 }
55 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
56
57 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, nullptr);
58 }
59
60 /* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
61 to our SkWStream. Since we don't reference/own the SkWStream, our consumer
62 must only live for the duration of the onEncode() method.
63 */
SkEncodeImageWithCG(SkWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format)64 bool SkEncodeImageWithCG(SkWStream* stream, const SkPixmap& pixmap, SkEncodedImageFormat format) {
65 SkBitmap bm;
66 if (!bm.installPixels(pixmap)) {
67 return false;
68 }
69 bm.setImmutable();
70
71 CFStringRef type;
72 switch (format) {
73 case SkEncodedImageFormat::kICO:
74 type = kUTTypeICO;
75 break;
76 case SkEncodedImageFormat::kBMP:
77 type = kUTTypeBMP;
78 break;
79 case SkEncodedImageFormat::kGIF:
80 type = kUTTypeGIF;
81 break;
82 case SkEncodedImageFormat::kJPEG:
83 type = kUTTypeJPEG;
84 break;
85 case SkEncodedImageFormat::kPNG:
86 // PNG encoding an ARGB_4444 bitmap gives the following errors in GM:
87 // <Error>: CGImageDestinationAddImage image could not be converted to destination
88 // format.
89 // <Error>: CGImageDestinationFinalize image destination does not have enough images
90 // So instead we copy to 8888.
91 if (bm.colorType() == kARGB_4444_SkColorType) {
92 SkBitmap bitmap8888;
93 bm.copyTo(&bitmap8888, kN32_SkColorType);
94 bm.swap(bitmap8888);
95 }
96 type = kUTTypePNG;
97 break;
98 default:
99 return false;
100 }
101
102 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
103 if (nullptr == dst) {
104 return false;
105 }
106 SkAutoTCallVProc<const void, CFRelease> ardst(dst);
107
108 CGImageRef image = SkCreateCGImageRef(bm);
109 if (nullptr == image) {
110 return false;
111 }
112 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
113
114 CGImageDestinationAddImage(dst, image, nullptr);
115 return CGImageDestinationFinalize(dst);
116 }
117
118 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
119