diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/pending/HTMLCanvasElement.cpp | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2 |
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/pending/HTMLCanvasElement.cpp')
-rw-r--r-- | webkit/pending/HTMLCanvasElement.cpp | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/webkit/pending/HTMLCanvasElement.cpp b/webkit/pending/HTMLCanvasElement.cpp new file mode 100644 index 0000000..e44612e --- /dev/null +++ b/webkit/pending/HTMLCanvasElement.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * 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 "HTMLCanvasElement.h" +#include "ImageBuffer.h" + +#include "CanvasGradient.h" +#include "CanvasPattern.h" +#include "CanvasRenderingContext2D.h" +#include "CanvasStyle.h" +#include "Chrome.h" +#include "Document.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderHTMLCanvas.h" +#include "Settings.h" +#include <math.h> + +#if PLATFORM(QT) +#include <QPainter> +#include <QPixmap> +#elif PLATFORM(CAIRO) +#include <cairo.h> +#endif + +// Use the Android canvas implementation instead. +#undef WTF_PLATFORM_CAIRO + +namespace WebCore { + +using namespace HTMLNames; + +// These values come from the WhatWG spec. +static const int defaultWidth = 300; +static const int defaultHeight = 150; + +// Firefox limits width/height to 32767 pixels, but slows down dramatically before it +// reaches that limit. We limit by area instead, giving us larger maximum dimensions, +// in exchange for a smaller maximum canvas size. +static const float maxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels + +HTMLCanvasElement::HTMLCanvasElement(Document* doc) + : HTMLElement(canvasTag, doc) + , m_size(defaultWidth, defaultHeight) + , m_createdDrawingContext(false) + , m_data(0) +#if PLATFORM(QT) + , m_painter(0) +#elif PLATFORM(CAIRO) + , m_surface(0) +#endif + , m_drawingContext(0) +{ +} + +HTMLCanvasElement::~HTMLCanvasElement() +{ + if (m_2DContext) + m_2DContext->detachCanvas(); +#if PLATFORM(CG) + fastFree(m_data); +#elif PLATFORM(QT) + delete m_painter; + delete m_data; +#elif PLATFORM(CAIRO) + if (m_surface) + cairo_surface_destroy(m_surface); +#elif defined(ANDROID_CANVAS_IMPL) + delete static_cast<ImageBuffer*>(m_data); +#endif + +#if !defined(ANDROID_CANVAS_IMPL) + delete m_drawingContext; +#endif +} + +HTMLTagStatus HTMLCanvasElement::endTagRequirement() const +{ + Settings* settings = document()->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return TagStatusForbidden; + + return HTMLElement::endTagRequirement(); +} + +int HTMLCanvasElement::tagPriority() const +{ + Settings* settings = document()->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return 0; + + return HTMLElement::tagPriority(); +} + +void HTMLCanvasElement::parseMappedAttribute(MappedAttribute* attr) +{ + const QualifiedName& attrName = attr->name(); + if (attrName == widthAttr || attrName == heightAttr) + reset(); + HTMLElement::parseMappedAttribute(attr); +} + +RenderObject* HTMLCanvasElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + Settings* settings = document()->settings(); + if (settings && settings->isJavaScriptEnabled()) { + m_rendererIsCanvas = true; + return new (arena) RenderHTMLCanvas(this); + } + + m_rendererIsCanvas = false; + return HTMLElement::createRenderer(arena, style); +} + +void HTMLCanvasElement::setHeight(int value) +{ + setAttribute(heightAttr, String::number(value)); +} + +void HTMLCanvasElement::setWidth(int value) +{ + setAttribute(widthAttr, String::number(value)); +} + +CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type) +{ + if (type == "2d") { + if (!m_2DContext) + m_2DContext = new CanvasRenderingContext2D(this); + return m_2DContext.get(); + } + return 0; +} + +void HTMLCanvasElement::willDraw(const FloatRect& rect) +{ + if (RenderObject* ro = renderer()) { +#ifdef CANVAS_INCREMENTAL_REPAINT + // Handle CSS triggered scaling + float widthScale = static_cast<float>(ro->width()) / static_cast<float>(m_size.width()); + float heightScale = static_cast<float>(ro->height()) / static_cast<float>(m_size.height()); + FloatRect r(rect.x() * widthScale, rect.y() * heightScale, rect.width() * widthScale, rect.height() * heightScale); + ro->repaintRectangle(enclosingIntRect(r)); +#else + ro->repaint(); +#endif + } +} + +void HTMLCanvasElement::reset() +{ + bool ok; + int w = getAttribute(widthAttr).toInt(&ok); + if (!ok) + w = defaultWidth; + int h = getAttribute(heightAttr).toInt(&ok); + if (!ok) + h = defaultHeight; + + IntSize oldSize = m_size; + m_size = IntSize(w, h); + + bool hadDrawingContext = m_createdDrawingContext; + m_createdDrawingContext = false; +#if PLATFORM(CG) + fastFree(m_data); +#elif PLATFORM(QT) + delete m_painter; + m_painter = 0; + delete m_data; +#elif PLATFORM(CAIRO) + if (m_surface) + cairo_surface_destroy(m_surface); + m_surface = 0; +#elif defined(ANDROID_CANVAS_IMPL) + delete static_cast<ImageBuffer*>(m_data); +#endif + m_data = 0; +#if !defined(ANDROID_CANVAS_IMPL) + delete m_drawingContext; +#endif + m_drawingContext = 0; + if (m_2DContext) + m_2DContext->reset(); + + if (RenderObject* ro = renderer()) + if (m_rendererIsCanvas) { + if (oldSize != m_size) + static_cast<RenderHTMLCanvas*>(ro)->canvasSizeChanged(); + if (hadDrawingContext) + ro->repaint(); + } +} + +void HTMLCanvasElement::paint(GraphicsContext* p, const IntRect& r) +{ + if (p->paintingDisabled()) + return; +#if PLATFORM(CG) + if (CGImageRef image = createPlatformImage()) { + CGContextDrawImage(p->platformContext(), p->roundToDevicePixels(r), image); + CGImageRelease(image); + } +#elif PLATFORM(QT) + if (m_data) { + QPen currentPen = m_painter->pen(); + qreal currentOpacity = m_painter->opacity(); + QBrush currentBrush = m_painter->brush(); + QBrush currentBackground = m_painter->background(); + if (m_painter->isActive()) + m_painter->end(); + static_cast<QPainter*>(p->platformContext())->drawImage(r, *m_data); + m_painter->begin(m_data); + m_painter->setPen(currentPen); + m_painter->setBrush(currentBrush); + m_painter->setOpacity(currentOpacity); + m_painter->setBackground(currentBackground); + } +#elif PLATFORM(CAIRO) + if (cairo_surface_t* image = createPlatformImage()) { + cairo_t* cr = p->platformContext(); + cairo_save(cr); + cairo_translate(cr, r.x(), r.y()); + cairo_set_source_surface(cr, image, 0, 0); + cairo_surface_destroy(image); + cairo_rectangle(cr, 0, 0, r.width(), r.height()); + cairo_fill(cr); + cairo_restore(cr); + } +#elif defined(ANDROID_CANVAS_IMPL) + if (m_drawingContext) { + p->drawOffscreenContext(m_drawingContext, NULL, FloatRect(r)); + } +#endif +} + +void HTMLCanvasElement::createDrawingContext() const +{ + ASSERT(!m_createdDrawingContext); + ASSERT(!m_data); + + m_createdDrawingContext = true; + + float unscaledWidth = width(); + float unscaledHeight = height(); + float pageScaleFactor = document()->frame() ? document()->frame()->page()->chrome()->scaleFactor() : 1.0f; + float wf = ceilf(unscaledWidth * pageScaleFactor); + float hf = ceilf(unscaledHeight * pageScaleFactor); + + if (!(wf >= 1 && hf >= 1 && wf * hf <= maxCanvasArea)) + return; + + unsigned w = static_cast<unsigned>(wf); + unsigned h = static_cast<unsigned>(hf); + +#if PLATFORM(CG) + size_t bytesPerRow = w * 4; + if (bytesPerRow / 4 != w) // check for overflow + return; + m_data = fastCalloc(h, bytesPerRow); + if (!m_data) + return; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bitmapContext = CGBitmapContextCreate(m_data, w, h, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); + CGContextScaleCTM(bitmapContext, w / unscaledWidth, h / unscaledHeight); + CGColorSpaceRelease(colorSpace); + m_drawingContext = new GraphicsContext(bitmapContext); + CGContextRelease(bitmapContext); +#elif PLATFORM(QT) + m_data = new QImage(w, h, QImage::Format_ARGB32_Premultiplied); + if (!m_data) + return; + m_painter = new QPainter(m_data); + m_painter->setBackground(QBrush(Qt::transparent)); + m_painter->fillRect(0, 0, w, h, QColor(Qt::transparent)); + m_drawingContext = new GraphicsContext(m_painter); +#elif PLATFORM(CAIRO) + m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + // m_data is owned by m_surface + m_data = cairo_image_surface_get_data(m_surface); + cairo_t* cr = cairo_create(m_surface); + cairo_scale(cr, w / unscaledWidth, h / unscaledHeight); + m_drawingContext = new GraphicsContext(cr); + cairo_destroy(cr); +#elif defined(ANDROID_CANVAS_IMPL) + IntSize size(w, h); + m_data = ImageBuffer::create(size, false).release(); + m_drawingContext = static_cast<ImageBuffer*>(m_data)->context(); +#endif +} + +GraphicsContext* HTMLCanvasElement::drawingContext() const +{ + if (!m_createdDrawingContext) + createDrawingContext(); + return m_drawingContext; +} + +#if PLATFORM(CG) + +CGImageRef HTMLCanvasElement::createPlatformImage() const +{ + GraphicsContext* context = drawingContext(); + if (!context) + return 0; + + CGContextRef contextRef = context->platformContext(); + if (!contextRef) + return 0; + + CGContextFlush(contextRef); + + return CGBitmapContextCreateImage(contextRef); +} + +#elif PLATFORM(QT) + +QImage HTMLCanvasElement::createPlatformImage() const +{ + if (m_data) + return *m_data; + return QImage(); +} + +#elif PLATFORM(CAIRO) + +cairo_surface_t* HTMLCanvasElement::createPlatformImage() const +{ + if (!m_surface) + return 0; + + // Note that unlike CG, our returned image is not a copy or + // copy-on-write, but the original. This is fine, since it is only + // ever used as a source. + + cairo_surface_flush(m_surface); + cairo_surface_reference(m_surface); + return m_surface; +} + +#endif + +} |