blob: dc9437fb81acf42065b8bad728e7bc75c427318e [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
reed@android.com0767e472008-12-23 16:06:51 +000016#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +000017#include "SkImageEncoder.h"
reed@android.com0767e472008-12-23 16:06:51 +000018#include "SkMovie.h"
19#include "SkStream.h"
20#include "SkTemplates.h"
reed@android.comad789ee2011-01-03 19:52:17 +000021#include "SkCGUtils.h"
reed@android.com0767e472008-12-23 16:06:51 +000022
yangsu@google.com900d8772011-06-24 18:56:00 +000023#ifdef SK_BUILD_FOR_MAC
24#include <ApplicationServices/ApplicationServices.h>
25#endif
26
27#ifdef SK_BUILD_FOR_IOS
28#include <CoreGraphics/CoreGraphics.h>
29#endif
yangsu@google.comc134f392011-06-23 22:27:30 +000030
reed@android.com0767e472008-12-23 16:06:51 +000031static void malloc_release_proc(void* info, const void* data, size_t size) {
32 sk_free(info);
33}
34
35static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
36 // TODO: use callbacks, so we don't have to load all the data into RAM
37 size_t len = stream->getLength();
38 void* data = sk_malloc_throw(len);
39 stream->read(data, len);
40
41 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
42}
43
44static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
45 CGDataProviderRef data = SkStreamToDataProvider(stream);
46 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
47 CGDataProviderRelease(data);
48 return imageSrc;
49}
50
51class SkImageDecoder_CG : public SkImageDecoder {
52protected:
reed@android.com3f1f06a2010-03-03 21:04:12 +000053 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
reed@android.com0767e472008-12-23 16:06:51 +000054};
55
56#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
57
reed@android.com3f1f06a2010-03-03 21:04:12 +000058bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
reed@android.com0767e472008-12-23 16:06:51 +000059 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
60
61 if (NULL == imageSrc) {
62 return false;
63 }
reed@android.com0767e472008-12-23 16:06:51 +000064 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
65
66 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
67 if (NULL == image) {
68 return false;
69 }
reed@android.com0767e472008-12-23 16:06:51 +000070 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
71
72 const int width = CGImageGetWidth(image);
73 const int height = CGImageGetHeight(image);
74 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
75 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
76 return true;
77 }
78
79 if (!this->allocPixelRef(bm, NULL)) {
80 return false;
81 }
82
83 bm->lockPixels();
84 bm->eraseColor(0);
85
reed@google.comaf9d9c22011-06-15 17:33:29 +000086 // use the same colorspace, so we don't change the pixels at all
87 CGColorSpaceRef cs = CGImageGetColorSpace(image);
reed@android.com0767e472008-12-23 16:06:51 +000088 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
89 8, bm->rowBytes(), cs, BITMAP_INFO);
90 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
91 CGContextRelease(cg);
reed@android.com0767e472008-12-23 16:06:51 +000092
93 bm->unlockPixels();
94 return true;
95}
96
97///////////////////////////////////////////////////////////////////////////////
98
99SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
100 return SkNEW(SkImageDecoder_CG);
101}
102
reed@android.com0767e472008-12-23 16:06:51 +0000103/////////////////////////////////////////////////////////////////////////
104
105SkMovie* SkMovie::DecodeStream(SkStream* stream) {
106 return NULL;
107}
108
109/////////////////////////////////////////////////////////////////////////
110
reed@android.com0ae6b242008-12-23 16:49:54 +0000111static size_t consumer_put(void* info, const void* buffer, size_t count) {
112 SkWStream* stream = reinterpret_cast<SkWStream*>(info);
113 return stream->write(buffer, count) ? count : 0;
114}
115
116static void consumer_release(void* info) {
117 // we do nothing, since by design we don't "own" the stream (i.e. info)
118}
119
120static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
121 CGDataConsumerCallbacks procs;
122 procs.putBytes = consumer_put;
123 procs.releaseConsumer = consumer_release;
124 // we don't own/reference the stream, so it our consumer must not live
125 // longer that our caller's ownership of the stream
126 return CGDataConsumerCreate(stream, &procs);
127}
128
129static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
130 CFStringRef type) {
131 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
132 if (NULL == consumer) {
133 return NULL;
134 }
135 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
136
137 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
138}
139
140class SkImageEncoder_CG : public SkImageEncoder {
141public:
142 SkImageEncoder_CG(Type t) : fType(t) {}
143
144protected:
145 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
146
147private:
148 Type fType;
149};
150
reed@android.com0ae6b242008-12-23 16:49:54 +0000151/* 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