• 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 
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