/* * Copyright 2012, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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. */ #define LOG_TAG "CanvasLayer" #define LOG_NDEBUG 1 #include "config.h" #include "CanvasLayer.h" #if USE(ACCELERATED_COMPOSITING) #include "AndroidLog.h" #include "CanvasTexture.h" #include "DrawQuadData.h" #include "Image.h" #include "ImageBuffer.h" #include "RenderLayerCompositor.h" #include "SkBitmap.h" #include "SkBitmapRef.h" #include "SkCanvas.h" #include "TilesManager.h" namespace WebCore { CanvasLayer::CanvasLayer(RenderLayer* owner, HTMLCanvasElement* canvas) : LayerAndroid(owner) , m_canvas(canvas) , m_dirtyCanvas() , m_bitmap(0) { init(); m_canvas->addObserver(this); } CanvasLayer::CanvasLayer(const CanvasLayer& layer) : LayerAndroid(layer) , m_canvas(0) , m_bitmap(0) { init(); if (!layer.m_canvas) { // The canvas has already been destroyed - this shouldn't happen ALOGW("Creating a CanvasLayer for a destroyed canvas!"); m_contentRect = IntRect(); m_offsetFromRenderer = IntSize(); m_texture->setHwAccelerated(false); return; } // We are making a copy for the UI, sync the interesting bits m_contentRect = layer.contentRect(); m_offsetFromRenderer = layer.offsetFromRenderer(); bool previousState = m_texture->hasValidTexture(); if (!previousState && layer.m_dirtyCanvas.isEmpty()) { // We were previously in software and don't have anything new to draw, // so stay in software m_bitmap = layer.bitmap(); SkSafeRef(m_bitmap); } else { // Attempt to upload to a surface texture if (!m_texture->uploadImageBuffer(layer.m_canvas->buffer())) { // Blargh, no surface texture or ImageBuffer - fall back to software m_bitmap = layer.bitmap(); SkSafeRef(m_bitmap); // Merge the canvas invals with the layer's invals to repaint the needed // tiles. SkRegion::Iterator iter(layer.m_dirtyCanvas); const IntPoint& offset = m_contentRect.location(); for (; !iter.done(); iter.next()) { SkIRect diff = iter.rect(); diff.fLeft += offset.x(); diff.fRight += offset.x(); diff.fTop += offset.y(); diff.fBottom += offset.y(); m_dirtyRegion.op(diff, SkRegion::kUnion_Op); } } if (previousState != m_texture->hasValidTexture()) { // Need to do a full inval of the canvas content as we are mode switching m_dirtyRegion.op(m_contentRect.x(), m_contentRect.y(), m_contentRect.maxX(), m_contentRect.maxY(), SkRegion::kUnion_Op); } } } CanvasLayer::~CanvasLayer() { if (m_canvas) m_canvas->removeObserver(this); SkSafeUnref(m_bitmap); } void CanvasLayer::init() { m_texture = CanvasTexture::getCanvasTexture(this); } void CanvasLayer::canvasChanged(HTMLCanvasElement*, const FloatRect& changedRect) { if (!m_texture->hasValidTexture()) { // We only need to track invals if we aren't using a SurfaceTexture. // If we drop out of hwa, we will do a full inval anyway SkIRect irect = SkIRect::MakeXYWH(changedRect.x(), changedRect.y(), changedRect.width(), changedRect.height()); m_dirtyCanvas.op(irect, SkRegion::kUnion_Op); } owningLayer()->compositor()->scheduleLayerFlush(); } void CanvasLayer::canvasResized(HTMLCanvasElement*) { const IntSize& size = m_canvas->size(); m_dirtyCanvas.setRect(0, 0, size.width(), size.height()); // If we are smaller than one tile, don't bother using a surface texture if (size.width() <= TilesManager::tileWidth() && size.height() <= TilesManager::tileHeight()) m_texture->setSize(IntSize()); else m_texture->setSize(size); } void CanvasLayer::canvasDestroyed(HTMLCanvasElement*) { m_canvas = 0; } void CanvasLayer::clearDirtyRegion() { LayerAndroid::clearDirtyRegion(); m_dirtyCanvas.setEmpty(); if (m_canvas) m_canvas->clearDirtyRect(); } SkBitmapRef* CanvasLayer::bitmap() const { if (!m_canvas || !m_canvas->buffer()) return 0; return m_canvas->copiedImage()->nativeImageForCurrentFrame(); } IntRect CanvasLayer::contentRect() const { if (!m_canvas || !m_canvas->renderer() || !m_canvas->renderer()->style() || !m_canvas->inDocument() || m_canvas->renderer()->style()->visibility() != VISIBLE) return IntRect(); return m_canvas->renderBox()->contentBoxRect(); } IntSize CanvasLayer::offsetFromRenderer() const { return m_canvas->renderBox()->layer()->backing()->graphicsLayer()->offsetFromRenderer(); } bool CanvasLayer::needsTexture() { return m_bitmap || LayerAndroid::needsTexture(); } void CanvasLayer::contentDraw(SkCanvas* canvas, PaintStyle style) { LayerAndroid::contentDraw(canvas, style); if (!m_bitmap) return; SkBitmap& bitmap = m_bitmap->bitmap(); SkRect dst = SkRect::MakeXYWH(m_contentRect.x() - m_offsetFromRenderer.width(), m_contentRect.y() - m_offsetFromRenderer.height(), m_contentRect.width(), m_contentRect.height()); canvas->drawBitmapRect(bitmap, 0, dst, 0); } bool CanvasLayer::drawGL(bool layerTilesDisabled) { bool ret = LayerAndroid::drawGL(layerTilesDisabled); m_texture->requireTexture(); if (!m_bitmap && m_texture->updateTexImage()) { SkRect rect = SkRect::MakeXYWH(m_contentRect.x() - m_offsetFromRenderer.width(), m_contentRect.y() - m_offsetFromRenderer.height(), m_contentRect.width(), m_contentRect.height()); TextureQuadData data(m_texture->texture(), GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, LayerQuad, &m_drawTransform, &rect); TilesManager::instance()->shader()->drawQuad(&data); } return ret; } LayerAndroid::InvalidateFlags CanvasLayer::onSetHwAccelerated(bool hwAccelerated) { if (m_texture->setHwAccelerated(hwAccelerated)) return LayerAndroid::InvalidateLayers; return LayerAndroid::InvalidateNone; } } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING)