• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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