| /* |
| * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org> |
| * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> |
| * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "ImageBuffer.h" |
| |
| #include "BitmapImage.h" |
| #include "CairoUtilities.h" |
| #include "Color.h" |
| #include "GraphicsContext.h" |
| #include "ImageData.h" |
| #include "MIMETypeRegistry.h" |
| #include "NotImplemented.h" |
| #include "Pattern.h" |
| #include "PlatformContextCairo.h" |
| #include "PlatformString.h" |
| #include "RefPtrCairo.h" |
| #include <cairo.h> |
| #include <wtf/Vector.h> |
| #include <wtf/text/Base64.h> |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| ImageBufferData::ImageBufferData(const IntSize& size) |
| : m_surface(0) |
| , m_platformContext(0) |
| { |
| } |
| |
| ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode, DeferralMode, bool& success) |
| : m_data(size) |
| , m_size(size) |
| , m_logicalSize(size) |
| { |
| success = false; // Make early return mean error. |
| m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, |
| size.width(), |
| size.height()); |
| if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS) |
| return; // create will notice we didn't set m_initialized and fail. |
| |
| RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface)); |
| m_data.m_platformContext.setCr(cr.get()); |
| m_context = adoptPtr(new GraphicsContext(&m_data.m_platformContext)); |
| success = true; |
| } |
| |
| ImageBuffer::~ImageBuffer() |
| { |
| cairo_surface_destroy(m_data.m_surface); |
| } |
| |
| GraphicsContext* ImageBuffer::context() const |
| { |
| return m_context.get(); |
| } |
| |
| PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior) const |
| { |
| if (copyBehavior == CopyBackingStore) |
| return BitmapImage::create(copyCairoImageSurface(m_data.m_surface).leakRef()); |
| |
| // BitmapImage will release the passed in surface on destruction |
| return BitmapImage::create(cairo_surface_reference(m_data.m_surface)); |
| } |
| |
| void ImageBuffer::clip(GraphicsContext* context, const FloatRect& maskRect) const |
| { |
| context->platformContext()->pushImageMask(m_data.m_surface, maskRect); |
| } |
| |
| void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, |
| CompositeOperator op , bool useLowQualityScale) |
| { |
| RefPtr<Image> image = copyImage(DontCopyBackingStore); |
| context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale); |
| } |
| |
| void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, |
| const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) |
| { |
| RefPtr<Image> image = copyImage(DontCopyBackingStore); |
| image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); |
| } |
| |
| void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) |
| { |
| ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); |
| |
| unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); |
| int stride = cairo_image_surface_get_stride(m_data.m_surface); |
| for (int y = 0; y < m_size.height(); ++y) { |
| unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y); |
| for (int x = 0; x < m_size.width(); x++) { |
| unsigned* pixel = row + x; |
| Color pixelColor = colorFromPremultipliedARGB(*pixel); |
| pixelColor = Color(lookUpTable[pixelColor.red()], |
| lookUpTable[pixelColor.green()], |
| lookUpTable[pixelColor.blue()], |
| pixelColor.alpha()); |
| *pixel = premultipliedARGBFromColor(pixelColor); |
| } |
| } |
| cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height()); |
| } |
| |
| template <Multiply multiplied> |
| PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size) |
| { |
| ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); |
| |
| RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); |
| unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface); |
| unsigned char* dataDst = result->data(); |
| |
| if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) |
| result->zeroFill(); |
| |
| int originx = rect.x(); |
| int destx = 0; |
| if (originx < 0) { |
| destx = -originx; |
| originx = 0; |
| } |
| int endx = rect.maxX(); |
| if (endx > size.width()) |
| endx = size.width(); |
| int numColumns = endx - originx; |
| |
| int originy = rect.y(); |
| int desty = 0; |
| if (originy < 0) { |
| desty = -originy; |
| originy = 0; |
| } |
| int endy = rect.maxY(); |
| if (endy > size.height()) |
| endy = size.height(); |
| int numRows = endy - originy; |
| |
| int stride = cairo_image_surface_get_stride(data.m_surface); |
| unsigned destBytesPerRow = 4 * rect.width(); |
| |
| unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; |
| for (int y = 0; y < numRows; ++y) { |
| unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); |
| for (int x = 0; x < numColumns; x++) { |
| int basex = x * 4; |
| unsigned* pixel = row + x + originx; |
| Color pixelColor; |
| if (multiplied == Unmultiplied) |
| pixelColor = colorFromPremultipliedARGB(*pixel); |
| else |
| pixelColor = Color(*pixel); |
| destRows[basex] = pixelColor.red(); |
| destRows[basex + 1] = pixelColor.green(); |
| destRows[basex + 2] = pixelColor.blue(); |
| destRows[basex + 3] = pixelColor.alpha(); |
| } |
| destRows += destBytesPerRow; |
| } |
| |
| return result.release(); |
| } |
| |
| PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const |
| { |
| return getImageData<Unmultiplied>(rect, m_data, m_size); |
| } |
| |
| PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const |
| { |
| return getImageData<Premultiplied>(rect, m_data, m_size); |
| } |
| |
| void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem) |
| { |
| ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); |
| |
| unsigned char* dataDst = cairo_image_surface_get_data(m_data.m_surface); |
| |
| ASSERT(sourceRect.width() > 0); |
| ASSERT(sourceRect.height() > 0); |
| |
| int originx = sourceRect.x(); |
| int destx = destPoint.x() + sourceRect.x(); |
| ASSERT(destx >= 0); |
| ASSERT(destx < m_size.width()); |
| ASSERT(originx >= 0); |
| ASSERT(originx <= sourceRect.maxX()); |
| |
| int endx = destPoint.x() + sourceRect.maxX(); |
| ASSERT(endx <= m_size.width()); |
| |
| int numColumns = endx - destx; |
| |
| int originy = sourceRect.y(); |
| int desty = destPoint.y() + sourceRect.y(); |
| ASSERT(desty >= 0); |
| ASSERT(desty < m_size.height()); |
| ASSERT(originy >= 0); |
| ASSERT(originy <= sourceRect.maxY()); |
| |
| int endy = destPoint.y() + sourceRect.maxY(); |
| ASSERT(endy <= m_size.height()); |
| int numRows = endy - desty; |
| |
| unsigned srcBytesPerRow = 4 * sourceSize.width(); |
| int stride = cairo_image_surface_get_stride(m_data.m_surface); |
| |
| unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; |
| for (int y = 0; y < numRows; ++y) { |
| unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); |
| for (int x = 0; x < numColumns; x++) { |
| int basex = x * 4; |
| unsigned* pixel = row + x + destx; |
| Color pixelColor = Color::createUnCheked(srcRows[basex], |
| srcRows[basex + 1], |
| srcRows[basex + 2], |
| srcRows[basex + 3]); |
| if (multiplied == Unmultiplied) |
| *pixel = premultipliedARGBFromColor(pixelColor); |
| else |
| *pixel = pixelColor.rgb(); |
| } |
| srcRows += srcBytesPerRow; |
| } |
| cairo_surface_mark_dirty_rectangle(m_data.m_surface, |
| destx, desty, |
| numColumns, numRows); |
| } |
| |
| #if !PLATFORM(GTK) |
| static cairo_status_t writeFunction(void* output, const unsigned char* data, unsigned int length) |
| { |
| if (!reinterpret_cast<Vector<unsigned char>*>(output)->tryAppend(data, length)) |
| return CAIRO_STATUS_WRITE_ERROR; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<char>* output) |
| { |
| ASSERT(mimeType == "image/png"); // Only PNG output is supported for now. |
| |
| return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS; |
| } |
| |
| String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateSystem) const |
| { |
| ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); |
| |
| cairo_surface_t* image = cairo_get_target(context()->platformContext()->cr()); |
| |
| Vector<char> encodedImage; |
| if (!image || !encodeImage(image, mimeType, &encodedImage)) |
| return "data:,"; |
| |
| Vector<char> base64Data; |
| base64Encode(encodedImage, base64Data); |
| |
| return "data:" + mimeType + ";base64," + base64Data; |
| } |
| #endif |
| |
| } // namespace WebCore |