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