| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * 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 "CanvasSurface.h" |
| |
| #include "AffineTransform.h" |
| #include "ExceptionCode.h" |
| #include "FloatRect.h" |
| #include "GraphicsContext.h" |
| #include "HTMLCanvasElement.h" |
| #include "ImageBuffer.h" |
| #include "MIMETypeRegistry.h" |
| |
| namespace WebCore { |
| |
| // These values come from the WhatWG spec. |
| const int CanvasSurface::DefaultWidth = 300; |
| const int CanvasSurface::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. |
| const float CanvasSurface::MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels |
| |
| CanvasSurface::CanvasSurface(float pageScaleFactor) |
| : m_size(DefaultWidth, DefaultHeight) |
| #if PLATFORM(ANDROID) |
| /* In Android we capture the drawing into a displayList, and then replay |
| * that list at various scale factors (sometimes zoomed out, other times |
| * zoomed in for "normal" reading, yet other times at arbitrary zoom values |
| * based on the user's choice). In all of these cases, we do not re-record |
| * the displayList, hence it is usually harmful to perform any pre-rounding, |
| * since we just don't know the actual drawing resolution at record time. |
| */ |
| // TODO - may be better to move the ifdef to the call site of this |
| // constructor |
| , m_pageScaleFactor(1.0f) |
| #else |
| , m_pageScaleFactor(pageScaleFactor) |
| #endif |
| , m_originClean(true) |
| , m_hasCreatedImageBuffer(false) |
| { |
| } |
| |
| CanvasSurface::~CanvasSurface() |
| { |
| } |
| |
| void CanvasSurface::setSurfaceSize(const IntSize& size) |
| { |
| m_size = size; |
| m_hasCreatedImageBuffer = false; |
| m_imageBuffer.clear(); |
| } |
| |
| String CanvasSurface::toDataURL(const String& mimeType, const double* quality, ExceptionCode& ec) |
| { |
| if (!m_originClean) { |
| ec = SECURITY_ERR; |
| return String(); |
| } |
| |
| if (m_size.isEmpty() || !buffer()) |
| return String("data:,"); |
| |
| String lowercaseMimeType = mimeType.lower(); |
| |
| // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread). |
| if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType)) |
| return buffer()->toDataURL("image/png"); |
| |
| return buffer()->toDataURL(lowercaseMimeType, quality); |
| } |
| |
| void CanvasSurface::willDraw(const FloatRect&) |
| { |
| if (m_imageBuffer) |
| m_imageBuffer->clearImage(); |
| } |
| |
| IntRect CanvasSurface::convertLogicalToDevice(const FloatRect& logicalRect) const |
| { |
| return IntRect(convertLogicalToDevice(logicalRect.location()), convertLogicalToDevice(logicalRect.size())); |
| } |
| |
| IntSize CanvasSurface::convertLogicalToDevice(const FloatSize& logicalSize) const |
| { |
| float wf = ceilf(logicalSize.width() * m_pageScaleFactor); |
| float hf = ceilf(logicalSize.height() * m_pageScaleFactor); |
| |
| if (!(wf >= 1 && hf >= 1 && wf * hf <= MaxCanvasArea)) |
| return IntSize(); |
| |
| return IntSize(static_cast<unsigned>(wf), static_cast<unsigned>(hf)); |
| } |
| |
| IntPoint CanvasSurface::convertLogicalToDevice(const FloatPoint& logicalPos) const |
| { |
| float xf = logicalPos.x() * m_pageScaleFactor; |
| float yf = logicalPos.y() * m_pageScaleFactor; |
| |
| return IntPoint(static_cast<unsigned>(xf), static_cast<unsigned>(yf)); |
| } |
| |
| void CanvasSurface::createImageBuffer() const |
| { |
| ASSERT(!m_imageBuffer); |
| |
| m_hasCreatedImageBuffer = true; |
| |
| FloatSize unscaledSize(width(), height()); |
| IntSize size = convertLogicalToDevice(unscaledSize); |
| if (!size.width() || !size.height()) |
| return; |
| |
| m_imageBuffer = ImageBuffer::create(size); |
| // The convertLogicalToDevice MaxCanvasArea check should prevent common cases |
| // where ImageBuffer::create() returns 0, however we could still be low on memory. |
| if (!m_imageBuffer) |
| return; |
| m_imageBuffer->context()->scale(FloatSize(size.width() / unscaledSize.width(), size.height() / unscaledSize.height())); |
| m_imageBuffer->context()->setShadowsIgnoreTransforms(true); |
| } |
| |
| GraphicsContext* CanvasSurface::drawingContext() const |
| { |
| return buffer() ? m_imageBuffer->context() : 0; |
| } |
| |
| ImageBuffer* CanvasSurface::buffer() const |
| { |
| if (!m_hasCreatedImageBuffer) |
| createImageBuffer(); |
| return m_imageBuffer.get(); |
| } |
| |
| AffineTransform CanvasSurface::baseTransform() const |
| { |
| ASSERT(m_hasCreatedImageBuffer); |
| FloatSize unscaledSize(width(), height()); |
| IntSize size = convertLogicalToDevice(unscaledSize); |
| AffineTransform transform; |
| if (size.width() && size.height()) |
| transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); |
| transform.multiply(m_imageBuffer->baseTransform()); |
| return transform; |
| } |
| |
| // FIXME: Everything below here relies on CanvasSurface really being |
| // a HTMLCanvasElement. |
| const SecurityOrigin& CanvasSurface::securityOrigin() const |
| { |
| return *(static_cast<const HTMLCanvasElement*>(this)->document()->securityOrigin()); |
| } |
| |
| RenderBox* CanvasSurface::renderBox() const |
| { |
| return static_cast<const HTMLCanvasElement*>(this)->renderBox(); |
| } |
| |
| RenderStyle* CanvasSurface::computedStyle() |
| { |
| return static_cast<HTMLCanvasElement*>(this)->computedStyle(); |
| } |
| |
| CSSStyleSelector* CanvasSurface::styleSelector() |
| { |
| return static_cast<HTMLCanvasElement*>(this)->document()->styleSelector(); |
| } |
| |
| } // namespace WebCore |