1 /*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "ImageBuffer.h"
29
30 #include "Base64.h"
31 #include "BitmapImage.h"
32 #include "CString.h"
33 #include "GraphicsContext.h"
34 #include "ImageData.h"
35 #include "MIMETypeRegistry.h"
36 #include "PlatformString.h"
37 #include <ApplicationServices/ApplicationServices.h>
38 #include <wtf/Assertions.h>
39 #include <wtf/OwnArrayPtr.h>
40 #include <wtf/RetainPtr.h>
41 #include <math.h>
42
43 using namespace std;
44
45 namespace WebCore {
46
ImageBufferData(const IntSize &)47 ImageBufferData::ImageBufferData(const IntSize&)
48 : m_data(0)
49 {
50 }
51
ImageBuffer(const IntSize & size,ImageColorSpace imageColorSpace,bool & success)52 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success)
53 : m_data(size)
54 , m_size(size)
55 {
56 success = false; // Make early return mean failure.
57 unsigned bytesPerRow;
58 if (size.width() < 0 || size.height() < 0)
59 return;
60 bytesPerRow = size.width();
61 if (imageColorSpace != GrayScale) {
62 // Protect against overflow
63 if (bytesPerRow > 0x3FFFFFFF)
64 return;
65 bytesPerRow *= 4;
66 }
67
68 m_data.m_data = tryFastCalloc(size.height(), bytesPerRow);
69 ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0);
70
71 CGColorSpaceRef colorSpace;
72 switch(imageColorSpace) {
73 case DeviceRGB:
74 colorSpace = CGColorSpaceCreateDeviceRGB();
75 break;
76 case GrayScale:
77 colorSpace = CGColorSpaceCreateDeviceGray();
78 break;
79 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER)
80 case LinearRGB:
81 colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
82 break;
83 #endif
84 default:
85 colorSpace = CGColorSpaceCreateDeviceRGB();
86 break;
87 }
88
89 CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow,
90 colorSpace, (imageColorSpace == GrayScale) ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast);
91 CGColorSpaceRelease(colorSpace);
92 if (!cgContext)
93 return;
94
95 m_context.set(new GraphicsContext(cgContext));
96 m_context->scale(FloatSize(1, -1));
97 m_context->translate(0, -size.height());
98 CGContextRelease(cgContext);
99 success = true;
100 }
101
~ImageBuffer()102 ImageBuffer::~ImageBuffer()
103 {
104 fastFree(m_data.m_data);
105 }
106
context() const107 GraphicsContext* ImageBuffer::context() const
108 {
109 return m_context.get();
110 }
111
image() const112 Image* ImageBuffer::image() const
113 {
114 if (!m_image) {
115 // It's assumed that if image() is called, the actual rendering to the
116 // GraphicsContext must be done.
117 ASSERT(context());
118 CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext());
119 // BitmapImage will release the passed in CGImage on destruction
120 m_image = BitmapImage::create(cgImage);
121 }
122 return m_image.get();
123 }
124
getImageData(const IntRect & rect) const125 PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
126 {
127 PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
128 unsigned char* data = result->data()->data()->data();
129
130 if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
131 memset(data, 0, result->data()->length());
132
133 int originx = rect.x();
134 int destx = 0;
135 if (originx < 0) {
136 destx = -originx;
137 originx = 0;
138 }
139 int endx = rect.x() + rect.width();
140 if (endx > m_size.width())
141 endx = m_size.width();
142 int numColumns = endx - originx;
143
144 int originy = rect.y();
145 int desty = 0;
146 if (originy < 0) {
147 desty = -originy;
148 originy = 0;
149 }
150 int endy = rect.y() + rect.height();
151 if (endy > m_size.height())
152 endy = m_size.height();
153 int numRows = endy - originy;
154
155 unsigned srcBytesPerRow = 4 * m_size.width();
156 unsigned destBytesPerRow = 4 * rect.width();
157
158 // ::create ensures that all ImageBuffers have valid data, so we don't need to check it here.
159 unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data.m_data) + originy * srcBytesPerRow + originx * 4;
160 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
161 for (int y = 0; y < numRows; ++y) {
162 for (int x = 0; x < numColumns; x++) {
163 int basex = x * 4;
164 if (unsigned char alpha = srcRows[basex + 3]) {
165 destRows[basex] = (srcRows[basex] * 255) / alpha;
166 destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
167 destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha;
168 destRows[basex + 3] = alpha;
169 } else
170 reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
171 }
172 srcRows += srcBytesPerRow;
173 destRows += destBytesPerRow;
174 }
175 return result;
176 }
177
putImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint)178 void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
179 {
180 ASSERT(sourceRect.width() > 0);
181 ASSERT(sourceRect.height() > 0);
182
183 int originx = sourceRect.x();
184 int destx = destPoint.x() + sourceRect.x();
185 ASSERT(destx >= 0);
186 ASSERT(destx < m_size.width());
187 ASSERT(originx >= 0);
188 ASSERT(originx <= sourceRect.right());
189
190 int endx = destPoint.x() + sourceRect.right();
191 ASSERT(endx <= m_size.width());
192
193 int numColumns = endx - destx;
194
195 int originy = sourceRect.y();
196 int desty = destPoint.y() + sourceRect.y();
197 ASSERT(desty >= 0);
198 ASSERT(desty < m_size.height());
199 ASSERT(originy >= 0);
200 ASSERT(originy <= sourceRect.bottom());
201
202 int endy = destPoint.y() + sourceRect.bottom();
203 ASSERT(endy <= m_size.height());
204 int numRows = endy - desty;
205
206 unsigned srcBytesPerRow = 4 * source->width();
207 unsigned destBytesPerRow = 4 * m_size.width();
208
209 unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
210 unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data.m_data) + desty * destBytesPerRow + destx * 4;
211 for (int y = 0; y < numRows; ++y) {
212 for (int x = 0; x < numColumns; x++) {
213 int basex = x * 4;
214 unsigned char alpha = srcRows[basex + 3];
215 if (alpha != 255) {
216 destRows[basex] = (srcRows[basex] * alpha + 254) / 255;
217 destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
218 destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255;
219 destRows[basex + 3] = alpha;
220 } else
221 reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
222 }
223 destRows += destBytesPerRow;
224 srcRows += srcBytesPerRow;
225 }
226 }
227
utiFromMIMEType(const String & mimeType)228 static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
229 {
230 #if PLATFORM(MAC)
231 RetainPtr<CFStringRef> mimeTypeCFString(AdoptCF, mimeType.createCFString());
232 return RetainPtr<CFStringRef>(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeCFString.get(), 0));
233 #else
234 // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found.
235 // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>.
236 static const CFStringRef kUTTypePNG = CFSTR("public.png");
237 static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
238 static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
239
240 if (equalIgnoringCase(mimeType, "image/png"))
241 return kUTTypePNG;
242 if (equalIgnoringCase(mimeType, "image/jpeg"))
243 return kUTTypeJPEG;
244 if (equalIgnoringCase(mimeType, "image/gif"))
245 return kUTTypeGIF;
246
247 ASSERT_NOT_REACHED();
248 return kUTTypePNG;
249 #endif
250 }
251
toDataURL(const String & mimeType) const252 String ImageBuffer::toDataURL(const String& mimeType) const
253 {
254 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
255
256 RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext()));
257 if (!image)
258 return "data:,";
259
260 size_t width = CGImageGetWidth(image.get());
261 size_t height = CGImageGetHeight(image.get());
262
263 OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]);
264 if (!imageData)
265 return "data:,";
266
267 RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(context()->platformContext()));
268 if (!transformedImage)
269 return "data:,";
270
271 RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
272 if (!transformedImageData)
273 return "data:,";
274
275 RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(),
276 utiFromMIMEType(mimeType).get(), 1, 0));
277 if (!imageDestination)
278 return "data:,";
279
280 CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0);
281 CGImageDestinationFinalize(imageDestination.get());
282
283 Vector<char> in;
284 in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get()));
285
286 Vector<char> out;
287 base64Encode(in, out);
288 out.append('\0');
289
290 return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data());
291 }
292
293 } // namespace WebCore
294