2008-03-07 Stephanie Lewis <slewis@apple.com>
[WebKit-https.git] / WebCore / platform / graphics / cg / ImageBufferCG.cpp
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 "CString.h"
32 #include "GraphicsContext.h"
33 #include "ImageData.h"
34 #include "MIMETypeRegistry.h"
35 #include "PlatformString.h"
36 #include <ApplicationServices/ApplicationServices.h>
37 #include <wtf/Assertions.h>
38 #include <wtf/OwnArrayPtr.h>
39 #include <wtf/RetainPtr.h>
40
41 using namespace std;
42
43 namespace WebCore {
44
45 auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale)
46 {
47     if (size.width() < 0 || size.height() < 0)
48         return auto_ptr<ImageBuffer>();
49     unsigned int bytesPerRow = size.width();
50     if (!grayScale) {
51         // Protect against overflow
52         if (bytesPerRow > 0x3FFFFFFF)
53             return auto_ptr<ImageBuffer>();
54         bytesPerRow *= 4;
55     }
56
57     void* imageBuffer = fastCalloc(size.height(), bytesPerRow);
58     if (!imageBuffer)
59         return auto_ptr<ImageBuffer>();
60
61     CGColorSpaceRef colorSpace = grayScale ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
62     CGContextRef cgContext = CGBitmapContextCreate(imageBuffer, size.width(), size.height(), 8, bytesPerRow,
63         colorSpace, grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast);
64     CGColorSpaceRelease(colorSpace);
65     if (!cgContext) {
66         fastFree(imageBuffer);
67         return auto_ptr<ImageBuffer>();
68     }
69
70     auto_ptr<GraphicsContext> context(new GraphicsContext(cgContext));
71     CGContextRelease(cgContext);
72
73     return auto_ptr<ImageBuffer>(new ImageBuffer(imageBuffer, size, context));
74 }
75
76
77 ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<GraphicsContext> context)
78     : m_data(imageData)
79     , m_size(size)
80     , m_context(context.release())
81     , m_cgImage(0)
82 {
83     ASSERT((reinterpret_cast<size_t>(imageData) & 2) == 0);
84 }
85
86 ImageBuffer::~ImageBuffer()
87 {
88     fastFree(m_data);
89     CGImageRelease(m_cgImage);
90 }
91
92 GraphicsContext* ImageBuffer::context() const
93 {
94     return m_context.get();
95 }
96
97 CGImageRef ImageBuffer::cgImage() const
98 {
99     // It's assumed that if cgImage() is called, the actual rendering to the
100     // contained GraphicsContext must be done, as we create the CGImageRef here.
101     if (!m_cgImage) {
102         ASSERT(context());
103         m_cgImage = CGBitmapContextCreateImage(context()->platformContext());
104     }
105
106     return m_cgImage;
107 }
108
109 PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
110 {
111     if (!m_data)
112         return 0;
113
114     PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
115     unsigned char* data = result->data()->data().data();
116
117     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
118         memset(data, 0, result->data()->length());
119
120     int originx = rect.x();
121     int destx = 0;
122     if (originx < 0) {
123         destx = -originx;
124         originx = 0;
125     }
126     int endx = rect.x() + rect.width();
127     if (endx > m_size.width())
128         endx = m_size.width();
129     int numColumns = endx - originx;
130
131     int originy = rect.y();
132     int desty = 0;
133     if (originy < 0) {
134         desty = -originy;
135         originy = 0;
136     }
137     int endy = rect.y() + rect.height();
138     if (endy > m_size.height())
139         endy = m_size.height();
140     int numRows = endy - originy;
141
142     unsigned srcBytesPerRow = 4 * m_size.width();
143     unsigned destBytesPerRow = 4 * rect.width();
144
145     // m_size.height() - originy to handle the accursed flipped y axis in CG backing store
146     unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data) + (m_size.height() - originy - 1) * srcBytesPerRow + originx * 4;
147     unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
148     for (int y = 0; y < numRows; ++y) {
149         for (int x = 0; x < numColumns; x++) {
150             int basex = x * 4;
151             if (unsigned char alpha = srcRows[basex + 3]) {
152                 destRows[0] = (srcRows[basex] * 255) / alpha;
153                 destRows[1] = (srcRows[basex + 1] * 255) / alpha;
154                 destRows[2] = (srcRows[basex + 2] * 255) / alpha;
155                 destRows[3] = alpha;
156             } else {
157                 reinterpret_cast<uint32_t*>(destRows)[0] = reinterpret_cast<uint32_t*>(srcRows)[0];
158             }
159             destRows += 4;
160         }
161         srcRows -= srcBytesPerRow;
162     }
163     return result;
164 }
165
166 void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
167 {
168     ASSERT(sourceRect.width() > 0);
169     ASSERT(sourceRect.height() > 0);
170
171     int originx = sourceRect.x();
172     int destx = destPoint.x() + sourceRect.x();
173     ASSERT(destx >= 0);
174     ASSERT(destx < m_size.width());
175     ASSERT(originx >= 0);
176     ASSERT(originx <= sourceRect.right());
177
178     int endx = destPoint.x() + sourceRect.right();
179     ASSERT(endx <= m_size.width());
180
181     int numColumns = endx - destx;
182
183     int originy = sourceRect.y();
184     int desty = destPoint.y() + sourceRect.y();
185     ASSERT(desty >= 0);
186     ASSERT(desty < m_size.height());
187     ASSERT(originy >= 0);
188     ASSERT(originy <= sourceRect.bottom());
189
190     int endy = destPoint.y() + sourceRect.bottom();
191     ASSERT(endy <= m_size.height());
192     int numRows = endy - desty;
193
194     unsigned srcBytesPerRow = 4 * source->width();
195     unsigned destBytesPerRow = 4 * m_size.width();
196
197     unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4;
198
199     // -desty to handle the accursed flipped y axis
200     unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data) + (m_size.height() - desty - 1) * destBytesPerRow + destx * 4;
201     for (int y = 0; y < numRows; ++y) {
202         for (int x = 0; x < numColumns; x++) {
203             unsigned char alpha = srcRows[x * 4 + 3];
204             if (alpha != 255) {
205                 destRows[x * 4 + 0] = (srcRows[0] * alpha) / 255;
206                 destRows[x * 4 + 1] = (srcRows[1] * alpha) / 255;
207                 destRows[x * 4 + 2] = (srcRows[2] * alpha) / 255;
208                 destRows[x * 4 + 3] = alpha;
209             } else {
210                 reinterpret_cast<uint32_t*>(destRows + x * 4)[0] = reinterpret_cast<uint32_t*>(srcRows + x * 4)[0];
211             }
212         }
213         destRows -= destBytesPerRow;
214         srcRows += srcBytesPerRow;
215     }
216 }
217
218 String ImageBuffer::toDataURL(const String& mimeType) const
219 {
220 #if PLATFORM(MAC)
221     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
222
223     RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context()->platformContext()));
224     if (!image)
225         return String("data:,");
226
227     size_t width = CGImageGetWidth(image.get());
228     size_t height = CGImageGetHeight(image.get());
229
230     OwnArrayPtr<uint32_t> imageData(new uint32_t[width * height]);
231
232     RetainPtr<CGContextRef> bitmapContext(AdoptCF, CGBitmapContextCreate(imageData.get(), width, height,
233         CGImageGetBitsPerComponent(image.get()), CGImageGetBytesPerRow(image.get()),
234         CGImageGetColorSpace(image.get()), kCGImageAlphaPremultipliedFirst));
235     if (!bitmapContext)
236         return String("data:,");
237
238     CGContextSaveGState(bitmapContext.get());
239     CGContextTranslateCTM(bitmapContext.get(), 0, height);
240     CGContextScaleCTM(bitmapContext.get(), 1.0f, -1.0f);
241     CGContextDrawImage(bitmapContext.get(), CGRectMake(0.0f, 0.0f, width, height), image.get());
242     CGContextRestoreGState(bitmapContext.get());
243
244     RetainPtr<CGImageRef> transformedImage(AdoptCF, CGBitmapContextCreateImage(bitmapContext.get()));
245     if (!transformedImage)
246         return String("data:,");
247
248     RetainPtr<CFMutableDataRef> transformedImageData(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
249     if (!transformedImageData)
250         return String("data:,");
251
252     RetainPtr<CFStringRef> imageUTI(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType.createCFString(), 0));
253     RetainPtr<CGImageDestinationRef> imageDestination(AdoptCF, CGImageDestinationCreateWithData(transformedImageData.get(), imageUTI.get(), 1, 0));
254     if (!imageDestination)
255         return String("data:,");
256
257     CGImageDestinationAddImage(imageDestination.get(), transformedImage.get(), 0);
258     CGImageDestinationFinalize(imageDestination.get());
259
260     Vector<char> in;
261     in.append(CFDataGetBytePtr(transformedImageData.get()), CFDataGetLength(transformedImageData.get()));
262
263     Vector<char> out;
264     base64Encode(in, out);
265     out.append('\0');
266
267     return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data());
268 #else
269     return String();
270 #endif
271 }
272
273 } // namespace WebCore