blob: 726a98601d0e4bd6f558c7d04955376a76ad5dc1 [file] [log] [blame]
/*
* Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2008 Holger Hans Peter Freyther
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
*
* 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 "CString.h"
#include "GraphicsContext.h"
#include "ImageData.h"
#include "MIMETypeRegistry.h"
#include "StillImageQt.h"
#include <QBuffer>
#include <QColor>
#include <QImage>
#include <QImageWriter>
#include <QPainter>
#include <QPixmap>
#include <math.h>
namespace WebCore {
ImageBufferData::ImageBufferData(const IntSize& size)
: m_pixmap(size)
, m_painter(0)
{
if (m_pixmap.isNull())
return;
m_pixmap.fill(QColor(Qt::transparent));
QPainter* painter = new QPainter;
m_painter.set(painter);
if (!painter->begin(&m_pixmap))
return;
// Since ImageBuffer is used mainly for Canvas, explicitly initialize
// its painter's pen and brush with the corresponding canvas defaults
// NOTE: keep in sync with CanvasRenderingContext2D::State
QPen pen = painter->pen();
pen.setColor(Qt::black);
pen.setWidth(1);
pen.setCapStyle(Qt::FlatCap);
pen.setJoinStyle(Qt::MiterJoin);
pen.setMiterLimit(10);
painter->setPen(pen);
QBrush brush = painter->brush();
brush.setColor(Qt::black);
painter->setBrush(brush);
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
}
ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace, bool& success)
: m_data(size)
, m_size(size)
{
success = m_data.m_painter && m_data.m_painter->isActive();
if (!success)
return;
m_context.set(new GraphicsContext(m_data.m_painter.get()));
}
ImageBuffer::~ImageBuffer()
{
}
GraphicsContext* ImageBuffer::context() const
{
ASSERT(m_data.m_painter->isActive());
return m_context.get();
}
Image* ImageBuffer::image() const
{
if (!m_image) {
// It's assumed that if image() is called, the actual rendering to the
// GraphicsContext must be done.
ASSERT(context());
m_image = StillImage::create(m_data.m_pixmap);
}
return m_image.get();
}
void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
{
bool isPainting = m_data.m_painter->isActive();
if (isPainting)
m_data.m_painter->end();
QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
ASSERT(!image.isNull());
for (int y = 0; y < m_size.height(); ++y) {
for (int x = 0; x < m_size.width(); x++) {
QRgb value = image.pixel(x, y);
value = qRgba(lookUpTable[qRed(value)],
lookUpTable[qGreen(value)],
lookUpTable[qBlue(value)],
qAlpha(value));
image.setPixel(x, y, value);
}
}
m_data.m_pixmap = QPixmap::fromImage(image);
if (isPainting)
m_data.m_painter->begin(&m_data.m_pixmap);
}
template <Multiply multiplied>
PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
{
PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
unsigned char* data = result->data()->data()->data();
if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
memset(data, 0, result->data()->length());
int originx = rect.x();
int destx = 0;
if (originx < 0) {
destx = -originx;
originx = 0;
}
int endx = rect.x() + rect.width();
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.y() + rect.height();
if (endy > size.height())
endy = size.height();
int numRows = endy - originy;
QImage image = imageData.m_pixmap.toImage();
if (multiplied == Unmultiplied)
image = image.convertToFormat(QImage::Format_ARGB32);
else
image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
ASSERT(!image.isNull());
unsigned destBytesPerRow = 4 * rect.width();
unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
for (int y = 0; y < numRows; ++y) {
for (int x = 0; x < numColumns; x++) {
QRgb value = image.pixel(x + originx, y + originy);
int basex = x * 4;
destRows[basex] = qRed(value);
destRows[basex + 1] = qGreen(value);
destRows[basex + 2] = qBlue(value);
destRows[basex + 3] = qAlpha(value);
}
destRows += destBytesPerRow;
}
return result;
}
PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
{
return getImageData<Unmultiplied>(rect, m_data, m_size);
}
PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
{
return getImageData<Premultiplied>(rect, m_data, m_size);
}
template <Multiply multiplied>
void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size)
{
ASSERT(sourceRect.width() > 0);
ASSERT(sourceRect.height() > 0);
int originx = sourceRect.x();
int destx = destPoint.x() + sourceRect.x();
ASSERT(destx >= 0);
ASSERT(destx < size.width());
ASSERT(originx >= 0);
ASSERT(originx <= sourceRect.right());
int endx = destPoint.x() + sourceRect.right();
ASSERT(endx <= size.width());
int numColumns = endx - destx;
int originy = sourceRect.y();
int desty = destPoint.y() + sourceRect.y();
ASSERT(desty >= 0);
ASSERT(desty < size.height());
ASSERT(originy >= 0);
ASSERT(originy <= sourceRect.bottom());
int endy = destPoint.y() + sourceRect.bottom();
ASSERT(endy <= size.height());
int numRows = endy - desty;
unsigned srcBytesPerRow = 4 * source->width();
bool isPainting = data.m_painter->isActive();
if (isPainting)
data.m_painter->end();
QImage image = data.m_pixmap.toImage();
if (multiplied == Unmultiplied)
image = image.convertToFormat(QImage::Format_ARGB32);
else
image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
for (int y = 0; y < numRows; ++y) {
quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty));
for (int x = 0; x < numColumns; x++) {
// ImageData stores the pixels in RGBA while QImage is ARGB
quint32 pixel = reinterpret_cast<quint32*>(srcRows + 4 * x)[0];
pixel = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
scanLine[x + destx] = pixel;
}
srcRows += srcBytesPerRow;
}
data.m_pixmap = QPixmap::fromImage(image);
if (isPainting)
data.m_painter->begin(&data.m_pixmap);
}
void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
{
putImageData<Unmultiplied>(source, sourceRect, destPoint, m_data, m_size);
}
void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
{
putImageData<Premultiplied>(source, sourceRect, destPoint, m_data, m_size);
}
// We get a mimeType here but QImageWriter does not support mimetypes but
// only formats (png, gif, jpeg..., xpm). So assume we get image/ as image
// mimetypes and then remove the image/ to get the Qt format.
String ImageBuffer::toDataURL(const String& mimeType) const
{
ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
if (!mimeType.startsWith("image/"))
return "data:,";
// prepare our target
QByteArray data;
QBuffer buffer(&data);
buffer.open(QBuffer::WriteOnly);
if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data()))
return "data:,";
buffer.close();
return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data());
}
}