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
42 using namespace std;
43
44 namespace WebCore {
45
ImageBufferData(const IntSize &)46 ImageBufferData::ImageBufferData(const IntSize&)
47 : m_data(0)
48 {
49 }
50
ImageBuffer(const IntSize & size,bool grayScale,bool & success)51 ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success)
52 : m_data(size)
53 , m_size(size)
54 {
55 success = false; // Make early return mean failure.
56 unsigned bytesPerRow;
57 if (size.width() < 0 || size.height() < 0)
58 return;
59 bytesPerRow = size.width();
60 if (!grayScale) {
61 // Protect against overflow
62 if (bytesPerRow > 0x3FFFFFFF)
63 return;
64 bytesPerRow *= 4;
65 }
66
67 m_data.m_data = tryFastCalloc(size.height(), bytesPerRow);
68 ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0);
69
70 CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
71 CGContextRef cgContext = CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow,
72 colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast);
73 CGColorSpaceRelease(colorSpace);
74 if (!cgContext)
75 return;
76
77 m_context.set(new GraphicsContext(cgContext));
78 m_context->scale(FloatSize(1, -1));
79 m_context->translate(0, -size.height());
80 CGContextRelease(cgContext);
81 success = true;
82 }
83
~ImageBuffer()84 ImageBuffer::~ImageBuffer()
85 {
86 fastFree(m_data.m_data);
87 }
88
context() const89 GraphicsContext* ImageBuffer::context() const
90 {
91 return m_context.get();
92 }
93
image() const94 Image* ImageBuffer::image() const
95 {
96 if (!m_image) {
97 // It's assumed that if image() is called, the actual rendering to the
98 // GraphicsContext must be done.
99 ASSERT(context());
100 CGImageRef cgImage = CGBitmapContextCreateImage(context()->platformContext());
101 // BitmapImage will release the passed in CGImage on destruction
102 m_image = BitmapImage::create(cgImage);
103 }
104 return m_image.get();
105 }
106
getImageData(const IntRect & rect) const107 PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
108 {
109 PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
110 unsigned char* data = result->data()->data()->data();
111
112 if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
113 memset(data, 0, result->data()->length());
114
115 int originx = rect.x();
116 int destx = 0;
117 if (originx < 0) {
118 destx = -originx;
119 originx = 0;
120 }
121 int endx = rect.x() + rect.width();
122 if (endx > m_size.width())
123 endx = m_size.width();
124 int numColumns = endx - originx;
125
126 int originy = rect.y();
127 int desty = 0;
128 if (originy < 0) {
129 desty = -originy;
130 originy = 0;
131 }
132 int endy = rect.y() + rect.height();
133 if (endy > m_size.height())
134 endy = m_size.height();
135 int numRows = endy - originy;
136
137 unsigned srcBytesPerRow = 4 * m_size.width();
138 unsigned destBytesPerRow = 4 * rect.width();
139
140 // ::create ensures that all ImageBuffers have valid data, so we don't need to check it here.
141 unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data.m_data) + originy * srcBytesPerRow + originx * 4;
142 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
143 for (int y = 0; y < numRows; ++y) {
144 for (int x = 0; x < numColumns; x++) {
145 int basex = x * 4;
146 if (unsigned char alpha = srcRows[basex + 3]) {
147 destRows[basex] = (srcRows[basex] * 255) / alpha;
148 destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
149 destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha;
150 destRows[basex + 3] = alpha;
151 } else
152 reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
153 }
154 srcRows += srcBytesPerRow;
155 destRows += destBytesPerRow;
156 }
157 return result;
158 }
159
putImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint)160 void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
161 {
162 ASSERT(sourceRect.width() > 0);
163 ASSERT(sourceRect.height() > 0);
164
165 int originx = sourceRect.x();
166 int destx = destPoint.x() + sourceRect.x();
167 ASSERT(destx >= 0);
168 ASSERT(destx < m_size.width());
169 ASSERT(originx >= 0);
170 ASSERT(originx <= sourceRect.right());
171
172 int endx = destPoint.x() + sourceRect.right();
173 ASSERT(endx <= m_size.width());
174
175 int numColumns = endx - destx;
176
177 int originy = sourceRect.y();
178 int desty = destPoint.y() + sourceRect.y();
179 ASSERT(desty >= 0);
180 ASSERT(desty < m_size.height());
181 ASSERT(originy >= 0);
182 ASSERT(originy <= sourceRect.bottom());
183
184 int endy = destPoint.y() + sourceRect.bottom();
185 ASSERT(endy <= m_size.height());
186 int numRows = endy - desty;
187
188 unsigned srcBytesPerRow = 4 * source->width();
189 unsigned destBytesPerRow = 4 * m_size.width();
190
191 unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
192 unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data.m_data) + desty * destBytesPerRow + destx * 4;
193 for (int y = 0; y < numRows; ++y) {
194 for (int x = 0; x < numColumns; x++) {
195 int basex = x * 4;
196 unsigned char alpha = srcRows[basex + 3];
197 if (alpha != 255) {
198 destRows[basex] = (srcRows[basex] * alpha + 254) / 255;
199 destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
200 destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255;
201 destRows[basex + 3] = alpha;
202 } else
203 reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
204 }
205 destRows += destBytesPerRow;
206 srcRows += srcBytesPerRow;
207 }
208 }
209
utiFromMIMEType(const String & mimeType)210 static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
211 {
212 #if PLATFORM(MAC)
213 RetainPtr<CFStringRef> mimeTypeCFString(AdoptCF, mimeType.createCFString());
214 return RetainPtr<CFStringRef>(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeCFString.get(), 0));
215 #else
216 // FIXME: Add Windows support for all the supported UTIs when a way to convert from MIMEType to UTI reliably is found.
217 // For now, only support PNG, JPEG, and GIF. See <rdar://problem/6095286>.
218 static const CFStringRef kUTTypePNG = CFSTR("public.png");
219 static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
220 static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
221
222 if (equalIgnoringCase(mimeType, "image/png"))
223 return kUTTypePNG;
224 if (equalIgnoringCase(mimeType, "image/jpeg"))
225 return kUTTypeJPEG;
226 if (equalIgnoringCase(mimeType, "image/gif"))
227 return kUTTypeGIF;
228
229 ASSERT_NOT_REACHED();
230 return kUTTypePNG;
231 #endif
232 }
233
toDataURL(const String & mimeType) const234 String ImageBuffer::toDataURL(const String& mimeType) const
235 {
236 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
237
238 RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext()));
239 if (!image)
240 return "data:,";
241
242 size_t width = CGImageGetWidth(image.get());
243 size_t height = CGImageGetHeight(image.get());
244
245 OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]);
246 if (!imageData)
247 return "data:,";
248
249 RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(context()->platformContext()));
250 if (!transformedImage)
251 return "data:,";
252
253 RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
254 if (!transformedImageData)
255 return "data:,";
256
257 RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(),
258 utiFromMIMEType(mimeType).get(), 1, 0));
259 if (!imageDestination)
260 return "data:,";
261
262 CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0);
263 CGImageDestinationFinalize(imageDestination.get());
264
265 Vector<char> in;
266 in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get()));
267
268 Vector<char> out;
269 base64Encode(in, out);
270 out.append('\0');
271
272 return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data());
273 }
274
275 } // namespace WebCore
276