1 /* 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2007 Holger Hans Peter Freyther <zecke (at) selfish.org> 4 * Copyright (C) 2008, 2009 Dirk Schulze <krit (at) webkit.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "ImageBuffer.h" 30 31 #include "Base64.h" 32 #include "BitmapImage.h" 33 #include "Color.h" 34 #include "GraphicsContext.h" 35 #include "ImageData.h" 36 #include "MIMETypeRegistry.h" 37 #include "Pattern.h" 38 #include "PlatformString.h" 39 40 #include <cairo.h> 41 #include <wtf/Vector.h> 42 #include <math.h> 43 44 using namespace std; 45 46 // Cairo doesn't provide a way to copy a cairo_surface_t. 47 // See http://lists.cairographics.org/archives/cairo/2007-June/010877.html 48 // Once cairo provides the way, use the function instead of this. 49 static inline cairo_surface_t* copySurface(cairo_surface_t* surface) 50 { 51 cairo_format_t format = cairo_image_surface_get_format(surface); 52 int width = cairo_image_surface_get_width(surface); 53 int height = cairo_image_surface_get_height(surface); 54 cairo_surface_t* newsurface = cairo_image_surface_create(format, width, height); 55 56 cairo_t* cr = cairo_create(newsurface); 57 cairo_set_source_surface(cr, surface, 0, 0); 58 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 59 cairo_paint(cr); 60 cairo_destroy(cr); 61 62 return newsurface; 63 } 64 65 namespace WebCore { 66 67 ImageBufferData::ImageBufferData(const IntSize& size) 68 : m_surface(0) 69 { 70 } 71 72 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) 73 : m_data(size) 74 , m_size(size) 75 { 76 success = false; // Make early return mean error. 77 m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 78 size.width(), 79 size.height()); 80 if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS) 81 return; // create will notice we didn't set m_initialized and fail. 82 83 cairo_t* cr = cairo_create(m_data.m_surface); 84 m_context.set(new GraphicsContext(cr)); 85 cairo_destroy(cr); // The context is now owned by the GraphicsContext. 86 success = true; 87 } 88 89 ImageBuffer::~ImageBuffer() 90 { 91 cairo_surface_destroy(m_data.m_surface); 92 } 93 94 GraphicsContext* ImageBuffer::context() const 95 { 96 return m_context.get(); 97 } 98 99 Image* ImageBuffer::image() const 100 { 101 if (!m_image) { 102 // It's assumed that if image() is called, the actual rendering to the 103 // GraphicsContext must be done. 104 ASSERT(context()); 105 106 // This creates a COPY of the image and will cache that copy. This means 107 // that if subsequent operations take place on the context, neither the 108 // currently-returned image, nor the results of future image() calls, 109 // will contain that operation. 110 // 111 // This seems silly, but is the way the CG port works: image() is 112 // intended to be used only when rendering is "complete." 113 cairo_surface_t* newsurface = copySurface(m_data.m_surface); 114 115 // BitmapImage will release the passed in surface on destruction 116 m_image = BitmapImage::create(newsurface); 117 } 118 return m_image.get(); 119 } 120 121 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) 122 { 123 ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); 124 125 unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); 126 int stride = cairo_image_surface_get_stride(m_data.m_surface); 127 for (int y = 0; y < m_size.height(); ++y) { 128 unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y); 129 for (int x = 0; x < m_size.width(); x++) { 130 unsigned* pixel = row + x; 131 Color pixelColor = colorFromPremultipliedARGB(*pixel); 132 pixelColor = Color(lookUpTable[pixelColor.red()], 133 lookUpTable[pixelColor.green()], 134 lookUpTable[pixelColor.blue()], 135 pixelColor.alpha()); 136 *pixel = premultipliedARGBFromColor(pixelColor); 137 } 138 } 139 cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height()); 140 } 141 142 template <Multiply multiplied> 143 PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size) 144 { 145 ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); 146 147 PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); 148 unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface); 149 unsigned char* dataDst = result->data()->data()->data(); 150 151 if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) 152 memset(dataDst, 0, result->data()->length()); 153 154 int originx = rect.x(); 155 int destx = 0; 156 if (originx < 0) { 157 destx = -originx; 158 originx = 0; 159 } 160 int endx = rect.x() + rect.width(); 161 if (endx > size.width()) 162 endx = size.width(); 163 int numColumns = endx - originx; 164 165 int originy = rect.y(); 166 int desty = 0; 167 if (originy < 0) { 168 desty = -originy; 169 originy = 0; 170 } 171 int endy = rect.y() + rect.height(); 172 if (endy > size.height()) 173 endy = size.height(); 174 int numRows = endy - originy; 175 176 int stride = cairo_image_surface_get_stride(data.m_surface); 177 unsigned destBytesPerRow = 4 * rect.width(); 178 179 unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; 180 for (int y = 0; y < numRows; ++y) { 181 unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); 182 for (int x = 0; x < numColumns; x++) { 183 int basex = x * 4; 184 unsigned* pixel = row + x + originx; 185 Color pixelColor; 186 if (multiplied == Unmultiplied) 187 pixelColor = colorFromPremultipliedARGB(*pixel); 188 else 189 pixelColor = Color(*pixel); 190 destRows[basex] = pixelColor.red(); 191 destRows[basex + 1] = pixelColor.green(); 192 destRows[basex + 2] = pixelColor.blue(); 193 destRows[basex + 3] = pixelColor.alpha(); 194 } 195 destRows += destBytesPerRow; 196 } 197 198 return result; 199 } 200 201 PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 202 { 203 return getImageData<Unmultiplied>(rect, m_data, m_size); 204 } 205 206 PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const 207 { 208 return getImageData<Premultiplied>(rect, m_data, m_size); 209 } 210 211 template <Multiply multiplied> 212 void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size) 213 { 214 ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); 215 216 unsigned char* dataDst = cairo_image_surface_get_data(data.m_surface); 217 218 ASSERT(sourceRect.width() > 0); 219 ASSERT(sourceRect.height() > 0); 220 221 int originx = sourceRect.x(); 222 int destx = destPoint.x() + sourceRect.x(); 223 ASSERT(destx >= 0); 224 ASSERT(destx < size.width()); 225 ASSERT(originx >= 0); 226 ASSERT(originx <= sourceRect.right()); 227 228 int endx = destPoint.x() + sourceRect.right(); 229 ASSERT(endx <= size.width()); 230 231 int numColumns = endx - destx; 232 233 int originy = sourceRect.y(); 234 int desty = destPoint.y() + sourceRect.y(); 235 ASSERT(desty >= 0); 236 ASSERT(desty < size.height()); 237 ASSERT(originy >= 0); 238 ASSERT(originy <= sourceRect.bottom()); 239 240 int endy = destPoint.y() + sourceRect.bottom(); 241 ASSERT(endy <= size.height()); 242 int numRows = endy - desty; 243 244 unsigned srcBytesPerRow = 4 * source->width(); 245 int stride = cairo_image_surface_get_stride(data.m_surface); 246 247 unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; 248 for (int y = 0; y < numRows; ++y) { 249 unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); 250 for (int x = 0; x < numColumns; x++) { 251 int basex = x * 4; 252 unsigned* pixel = row + x + destx; 253 Color pixelColor(srcRows[basex], 254 srcRows[basex + 1], 255 srcRows[basex + 2], 256 srcRows[basex + 3]); 257 if (multiplied == Unmultiplied) 258 *pixel = premultipliedARGBFromColor(pixelColor); 259 else 260 *pixel = pixelColor.rgb(); 261 } 262 srcRows += srcBytesPerRow; 263 } 264 cairo_surface_mark_dirty_rectangle (data.m_surface, 265 destx, desty, 266 numColumns, numRows); 267 } 268 269 void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 270 { 271 putImageData<Unmultiplied>(source, sourceRect, destPoint, m_data, m_size); 272 } 273 274 void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 275 { 276 putImageData<Premultiplied>(source, sourceRect, destPoint, m_data, m_size); 277 } 278 279 static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned int length) 280 { 281 Vector<char>* in = reinterpret_cast<Vector<char>*>(closure); 282 in->append(data, length); 283 return CAIRO_STATUS_SUCCESS; 284 } 285 286 String ImageBuffer::toDataURL(const String& mimeType) const 287 { 288 cairo_surface_t* image = cairo_get_target(context()->platformContext()); 289 if (!image) 290 return "data:,"; 291 292 String actualMimeType("image/png"); 293 if (MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)) 294 actualMimeType = mimeType; 295 296 Vector<char> in; 297 // Only PNG output is supported for now. 298 cairo_surface_write_to_png_stream(image, writeFunction, &in); 299 300 Vector<char> out; 301 base64Encode(in, out); 302 303 return "data:" + actualMimeType + ";base64," + String(out.data(), out.size()); 304 } 305 306 } // namespace WebCore 307