blob: 5594eac91a8e0bea2b501f3ac1f93f1815cd8756 [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"
reed@android.comb08eb2b2009-01-06 20:16:26 +000018#include "SkImageEncoder.h"
reed@android.com0767e472008-12-23 16:06:51 +000019#include "SkMovie.h"
20#include "SkStream.h"
21#include "SkTemplates.h"
22
23static void malloc_release_proc(void* info, const void* data, size_t size) {
24 sk_free(info);
25}
26
27static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
28 // TODO: use callbacks, so we don't have to load all the data into RAM
29 size_t len = stream->getLength();
30 void* data = sk_malloc_throw(len);
31 stream->read(data, len);
32
33 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
34}
35
36static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
37 CGDataProviderRef data = SkStreamToDataProvider(stream);
38 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
39 CGDataProviderRelease(data);
40 return imageSrc;
41}
42
43class SkImageDecoder_CG : public SkImageDecoder {
44protected:
45 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
46 SkBitmap::Config pref, Mode);
47};
48
49#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
50
51bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm,
52 SkBitmap::Config pref, Mode mode) {
53 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
54
55 if (NULL == imageSrc) {
56 return false;
57 }
reed@android.com0767e472008-12-23 16:06:51 +000058 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
59
60 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
61 if (NULL == image) {
62 return false;
63 }
reed@android.com0767e472008-12-23 16:06:51 +000064 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
65
66 const int width = CGImageGetWidth(image);
67 const int height = CGImageGetHeight(image);
68 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
69 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
70 return true;
71 }
72
73 if (!this->allocPixelRef(bm, NULL)) {
74 return false;
75 }
76
77 bm->lockPixels();
78 bm->eraseColor(0);
79
80 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
81 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
82 8, bm->rowBytes(), cs, BITMAP_INFO);
83 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
84 CGContextRelease(cg);
85 CGColorSpaceRelease(cs);
86
87 bm->unlockPixels();
88 return true;
89}
90
91///////////////////////////////////////////////////////////////////////////////
92
93SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
94 return SkNEW(SkImageDecoder_CG);
95}
96
97bool SkImageDecoder::SupportsFormat(Format format) {
98 return true;
99}
100
101/////////////////////////////////////////////////////////////////////////
102
103SkMovie* SkMovie::DecodeStream(SkStream* stream) {
104 return NULL;
105}
106
107/////////////////////////////////////////////////////////////////////////
108
reed@android.com0ae6b242008-12-23 16:49:54 +0000109static size_t consumer_put(void* info, const void* buffer, size_t count) {
110 SkWStream* stream = reinterpret_cast<SkWStream*>(info);
111 return stream->write(buffer, count) ? count : 0;
112}
113
114static void consumer_release(void* info) {
115 // we do nothing, since by design we don't "own" the stream (i.e. info)
116}
117
118static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
119 CGDataConsumerCallbacks procs;
120 procs.putBytes = consumer_put;
121 procs.releaseConsumer = consumer_release;
122 // we don't own/reference the stream, so it our consumer must not live
123 // longer that our caller's ownership of the stream
124 return CGDataConsumerCreate(stream, &procs);
125}
126
127static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
128 CFStringRef type) {
129 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
130 if (NULL == consumer) {
131 return NULL;
132 }
133 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
134
135 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
136}
137
138class SkImageEncoder_CG : public SkImageEncoder {
139public:
140 SkImageEncoder_CG(Type t) : fType(t) {}
141
142protected:
143 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
144
145private:
146 Type fType;
147};
148
149extern CGImageRef SkCreateCGImageRef(const SkBitmap&);
150
151/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
152 to our SkWStream. Since we don't reference/own the SkWStream, our consumer
153 must only live for the duration of the onEncode() method.
154 */
155bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
156 int quality) {
157 CFStringRef type;
158 switch (fType) {
159 case kJPEG_Type:
160 type = kUTTypeJPEG;
161 break;
162 case kPNG_Type:
163 type = kUTTypePNG;
164 break;
165 default:
166 return false;
167 }
168
169 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
170 if (NULL == dst) {
171 return false;
172 }
173 SkAutoTCallVProc<const void, CFRelease> ardst(dst);
174
175 CGImageRef image = SkCreateCGImageRef(bm);
176 if (NULL == image) {
177 return false;
178 }
179 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
180
181 CGImageDestinationAddImage(dst, image, NULL);
182 CGImageDestinationFinalize(dst);
183 return true;
184}
185
reed@android.com0767e472008-12-23 16:06:51 +0000186SkImageEncoder* SkImageEncoder::Create(Type t) {
reed@android.com0767e472008-12-23 16:06:51 +0000187 switch (t) {
188 case kJPEG_Type:
reed@android.com0767e472008-12-23 16:06:51 +0000189 case kPNG_Type:
reed@android.com0ae6b242008-12-23 16:49:54 +0000190 break;
reed@android.com0767e472008-12-23 16:06:51 +0000191 default:
192 return NULL;
193 }
reed@android.com0ae6b242008-12-23 16:49:54 +0000194 return SkNEW_ARGS(SkImageEncoder_CG, (t));
reed@android.com0767e472008-12-23 16:06:51 +0000195}
196