blob: 74ff11285f2793ef31fa8844e13a950674e473a9 [file] [log] [blame]
reed@android.com0767e472008-12-23 16:06:51 +00001/* Copyright 2008, The Android Open Source Project
2**
3** Licensed under the Apache License, Version 2.0 (the "License");
4** you may not use this file except in compliance with the License.
5** You may obtain a copy of the License at
6**
7** http://www.apache.org/licenses/LICENSE-2.0
8**
9** Unless required by applicable law or agreed to in writing, software
10** distributed under the License is distributed on an "AS IS" BASIS,
11** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12** See the License for the specific language governing permissions and
13** limitations under the License.
14*/
15
16#include <Carbon/Carbon.h>
17#include "SkImageDecoder.h"
18#include "SkMovie.h"
19#include "SkStream.h"
20#include "SkTemplates.h"
21
22static void malloc_release_proc(void* info, const void* data, size_t size) {
23 sk_free(info);
24}
25
26static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
27 // TODO: use callbacks, so we don't have to load all the data into RAM
28 size_t len = stream->getLength();
29 void* data = sk_malloc_throw(len);
30 stream->read(data, len);
31
32 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
33}
34
35static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
36 CGDataProviderRef data = SkStreamToDataProvider(stream);
37 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
38 CGDataProviderRelease(data);
39 return imageSrc;
40}
41
42class SkImageDecoder_CG : public SkImageDecoder {
43protected:
44 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
45 SkBitmap::Config pref, Mode);
46};
47
48#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
49
50bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm,
51 SkBitmap::Config pref, Mode mode) {
52 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
53
54 if (NULL == imageSrc) {
55 return false;
56 }
reed@android.com0767e472008-12-23 16:06:51 +000057 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
58
59 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
60 if (NULL == image) {
61 return false;
62 }
reed@android.com0767e472008-12-23 16:06:51 +000063 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
64
65 const int width = CGImageGetWidth(image);
66 const int height = CGImageGetHeight(image);
67 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
68 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
69 return true;
70 }
71
72 if (!this->allocPixelRef(bm, NULL)) {
73 return false;
74 }
75
76 bm->lockPixels();
77 bm->eraseColor(0);
78
79 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
80 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
81 8, bm->rowBytes(), cs, BITMAP_INFO);
82 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
83 CGContextRelease(cg);
84 CGColorSpaceRelease(cs);
85
86 bm->unlockPixels();
87 return true;
88}
89
90///////////////////////////////////////////////////////////////////////////////
91
92SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
93 return SkNEW(SkImageDecoder_CG);
94}
95
96bool SkImageDecoder::SupportsFormat(Format format) {
97 return true;
98}
99
100/////////////////////////////////////////////////////////////////////////
101
102SkMovie* SkMovie::DecodeStream(SkStream* stream) {
103 return NULL;
104}
105
106/////////////////////////////////////////////////////////////////////////
107
108#ifdef SK_SUPPORT_IMAGE_ENCODE
109
reed@android.com0ae6b242008-12-23 16:49:54 +0000110static size_t consumer_put(void* info, const void* buffer, size_t count) {
111 SkWStream* stream = reinterpret_cast<SkWStream*>(info);
112 return stream->write(buffer, count) ? count : 0;
113}
114
115static void consumer_release(void* info) {
116 // we do nothing, since by design we don't "own" the stream (i.e. info)
117}
118
119static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
120 CGDataConsumerCallbacks procs;
121 procs.putBytes = consumer_put;
122 procs.releaseConsumer = consumer_release;
123 // we don't own/reference the stream, so it our consumer must not live
124 // longer that our caller's ownership of the stream
125 return CGDataConsumerCreate(stream, &procs);
126}
127
128static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
129 CFStringRef type) {
130 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
131 if (NULL == consumer) {
132 return NULL;
133 }
134 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
135
136 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
137}
138
139class SkImageEncoder_CG : public SkImageEncoder {
140public:
141 SkImageEncoder_CG(Type t) : fType(t) {}
142
143protected:
144 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
145
146private:
147 Type fType;
148};
149
150extern CGImageRef SkCreateCGImageRef(const SkBitmap&);
151
152/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
153 to our SkWStream. Since we don't reference/own the SkWStream, our consumer
154 must only live for the duration of the onEncode() method.
155 */
156bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
157 int quality) {
158 CFStringRef type;
159 switch (fType) {
160 case kJPEG_Type:
161 type = kUTTypeJPEG;
162 break;
163 case kPNG_Type:
164 type = kUTTypePNG;
165 break;
166 default:
167 return false;
168 }
169
170 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
171 if (NULL == dst) {
172 return false;
173 }
174 SkAutoTCallVProc<const void, CFRelease> ardst(dst);
175
176 CGImageRef image = SkCreateCGImageRef(bm);
177 if (NULL == image) {
178 return false;
179 }
180 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
181
182 CGImageDestinationAddImage(dst, image, NULL);
183 CGImageDestinationFinalize(dst);
184 return true;
185}
186
reed@android.com0767e472008-12-23 16:06:51 +0000187SkImageEncoder* SkImageEncoder::Create(Type t) {
reed@android.com0767e472008-12-23 16:06:51 +0000188 switch (t) {
189 case kJPEG_Type:
reed@android.com0767e472008-12-23 16:06:51 +0000190 case kPNG_Type:
reed@android.com0ae6b242008-12-23 16:49:54 +0000191 break;
reed@android.com0767e472008-12-23 16:06:51 +0000192 default:
193 return NULL;
194 }
reed@android.com0ae6b242008-12-23 16:49:54 +0000195 return SkNEW_ARGS(SkImageEncoder_CG, (t));
reed@android.com0767e472008-12-23 16:06:51 +0000196}
197
198#endif