void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform) { m_layers.append(layer); SkSafeRef(layer); m_needsTexture |= layer->needsTexture(); m_hasText |= layer->hasText(); // add this layer's size to the surface's area // TODO: handle scale/3d transform mapping IntRect rect = enclosingIntRect(layer->fullContentAreaMapped()); if (layer->needsTexture()) { if (m_fullContentArea.isEmpty()) { m_drawTransform = transform; m_drawTransform.translate3d(-rect.x(), -rect.y(), 0); m_fullContentArea = rect; } else m_fullContentArea.unite(rect); ALOGV("Surf %p adding LA %p, size " INT_RECT_FORMAT " now fullContentArea " INT_RECT_FORMAT, this, layer, INT_RECT_ARGS(rect), INT_RECT_ARGS(m_fullContentArea)); } if (isBase()) m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor(); }
bool Surface::blitFromContents(Tile* tile) { if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) return false; LayerContent* content = getFirstLayer()->content(); // Extract the dirty rect from the region. Note that this is *NOT* constrained // to this tile IntRect dirtyRect = tile->dirtyArea().getBounds(); IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), tile->y() * TilesManager::tileHeight(), TilesManager::tileWidth(), TilesManager::tileHeight()); FloatRect tileRectInDoc = tileRect; tileRectInDoc.scale(1 / tile->scale()); dirtyRect.intersect(enclosingIntRect(tileRectInDoc)); PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); if (!prerenderedInval || prerenderedInval->bitmap.isNull()) return false; SkBitmap sourceBitmap = prerenderedInval->bitmap; // Calculate the screen rect that is dirty, then intersect it with the // tile's screen rect so that we end up with the pixels we need to blit FloatRect screenDirty = dirtyRect; screenDirty.scale(tile->scale()); IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); enclosingScreenDirty.intersect(tileRect); if (enclosingScreenDirty.isEmpty()) return false; // Make sure the screen area we want to blit is contained by the // prerendered screen area if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " "enclosingScreenDirty " INT_RECT_FORMAT, INT_RECT_ARGS(prerenderedInval->screenArea), INT_RECT_ARGS(enclosingScreenDirty)); return false; } IntPoint origin = prerenderedInval->screenArea.location(); SkBitmap subset; subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); subset.allocPixels(); int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) return false; // Now upload SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), enclosingScreenDirty.y() - tileRect.y(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, subset, textureInval); tile->onBlitUpdate(); return true; }
void PicturePile::appendToPile(const IntRect& inval, const IntRect& originalInval) { ALOGV("Adding inval " INT_RECT_FORMAT " for original inval " INT_RECT_FORMAT, INT_RECT_ARGS(inval), INT_RECT_ARGS(originalInval)); // Remove any entries this obscures for (int i = (int) m_pile.size() - 1; i >= 0; i--) { if (inval.contains(m_pile[i].area)) m_pile.remove(i); } PictureContainer container(inval); if (ENABLE_PRERENDERED_INVALS) { container.prerendered = PrerenderedInval::create(originalInval.isEmpty() ? inval : originalInval); } m_pile.append(container); }
void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc) { /* The ref counting here is a bit unusual. What happens is begin/end recording * will ref/unref the recording canvas. However, 'canvas' might be pointing * at an SkNWayCanvas instead of the recording canvas, which needs to be * unref'd. Thus what we do is ref the recording canvas so that we can * always unref whatever canvas we have at the end. */ TRACE_METHOD(); SkPicture* picture = new SkPicture(); SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(), SkPicture::kUsePathBoundsForClip_RecordingFlag); SkSafeRef(canvas); canvas->translate(-pc.area.x(), -pc.area.y()); IntRect drawArea = pc.area; if (pc.prerendered.get()) { SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get()); if (!prerender) { ALOGV("Failed to create prerendered for " INT_RECT_FORMAT, INT_RECT_ARGS(pc.prerendered->area)); pc.prerendered.clear(); } else { drawArea.unite(pc.prerendered->area); SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height()); nwayCanvas->translate(-drawArea.x(), -drawArea.y()); nwayCanvas->addCanvas(canvas); nwayCanvas->addCanvas(prerender); SkSafeUnref(canvas); SkSafeUnref(prerender); canvas = nwayCanvas; } } WebCore::PlatformGraphicsContextSkia pgc(canvas); WebCore::GraphicsContext gc(&pgc); ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea)); painter->paintContents(&gc, drawArea); SkSafeUnref(canvas); picture->endRecording(); SkSafeUnref(pc.picture); pc.picture = picture; pc.dirty = false; }
void PicturePile::applyWebkitInvals() { m_dirtyRegion.setEmpty(); if (!m_webkitInvals.size()) return; // Build the invals (TODO: Support multiple inval regions) IntRect inval = m_webkitInvals[0]; m_dirtyRegion.setRect(toSkIRect(inval)); for (size_t i = 1; i < m_webkitInvals.size(); i++) { inval.unite(m_webkitInvals[i]); m_dirtyRegion.op(toSkIRect(m_webkitInvals[i]), SkRegion::kUnion_Op); } m_webkitInvals.clear(); ALOGV("Webkit inval: " INT_RECT_FORMAT, INT_RECT_ARGS(inval)); if (inval.isEmpty()) return; // Find the overlaps Vector<int> overlaps; for (size_t i = 0; i < m_pile.size(); i++) { PictureContainer& pc = m_pile[i]; if (pc.area.contains(inval)) { if (pc.dirty) { ALOGV("Found already dirty intersection"); return; } if (pc.area == inval) { appendToPile(inval); return; } // Don't count the base surface as an overlap if (pc.area.size() != m_size) overlaps.append(i); } else if (pc.area.intersects(inval)) overlaps.append(i); } if (overlaps.size() >= MAX_OVERLAP_COUNT) { ALOGV("Exceeds overlap count"); IntRect overlap = inval; for (int i = (int) overlaps.size() - 1; i >= 0; i--) { overlap.unite(m_pile[overlaps[i]].area); m_pile.remove(overlaps[i]); } float overlapArea = overlap.width() * overlap.height(); float totalArea = m_size.width() * m_size.height(); if (overlapArea / totalArea > MAX_OVERLAP_AREA) overlap = IntRect(0, 0, m_size.width(), m_size.height()); appendToPile(overlap, inval); return; } // Append! appendToPile(inval); }
// Used by WebViewCore void PicturePile::invalidate(const IntRect& dirtyRect) { // This will typically happen if the document has been resized but we haven't // drawn yet. As the first draw after a size change will do a full inval anyway, // don't bother tracking individual rects // TODO: Instead of clipping here, we should take the invals as given // and when the size changes just inval the deltas. This prevents a full // redraw for a page that grows IntRect inval = dirtyRect; inval.intersect(IntRect(0, 0, m_size.width(), m_size.height())); if (inval.isEmpty()) { ALOGV("Rejecting inval " INT_RECT_FORMAT, INT_RECT_ARGS(dirtyRect)); return; } // TODO: Support multiple non-intersecting webkit invals if (m_webkitInvals.size()) m_webkitInvals[0].unite(inval); else m_webkitInvals.append(inval); }
bool Surface::blitFromContents(Tile* tile) { if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) return false; if (tile->frontTexture() != tile->lastDrawnTexture()) { // CAPPFIX_FLICKERING: fix flickering issue at floating browser mode return false; // CAPPFIX_FLICKERING_END // the below works around an issue where glTexSubImage2d can't update a // texture that hasn't drawn yet by drawing it off screen. // glFlush() and glFinish() work also, but are likely more wasteful. SkRect rect = SkRect::MakeXYWH(-100, -100, 0, 0); FloatRect fillPortion(0, 0, 0, 0); tile->frontTexture()->drawGL(false, rect, 1.0f, 0, false, true, fillPortion); } LayerContent* content = getFirstLayer()->content(); // Extract the dirty rect from the region. Note that this is *NOT* constrained // to this tile IntRect dirtyRect = tile->dirtyArea().getBounds(); IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), tile->y() * TilesManager::tileHeight(), TilesManager::tileWidth(), TilesManager::tileHeight()); FloatRect tileRectInDoc = tileRect; tileRectInDoc.scale(1 / tile->scale()); dirtyRect.intersect(enclosingIntRect(tileRectInDoc)); PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); if (!prerenderedInval || prerenderedInval->bitmap.isNull()) return false; SkBitmap sourceBitmap = prerenderedInval->bitmap; // Calculate the screen rect that is dirty, then intersect it with the // tile's screen rect so that we end up with the pixels we need to blit FloatRect screenDirty = dirtyRect; screenDirty.scale(tile->scale()); IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); enclosingScreenDirty.intersect(tileRect); if (enclosingScreenDirty.isEmpty()) return false; // Make sure the screen area we want to blit is contained by the // prerendered screen area if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " "enclosingScreenDirty " INT_RECT_FORMAT, INT_RECT_ARGS(prerenderedInval->screenArea), INT_RECT_ARGS(enclosingScreenDirty)); return false; } IntPoint origin = prerenderedInval->screenArea.location(); SkBitmap subset; subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); subset.allocPixels(); int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) return false; // Now upload SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), enclosingScreenDirty.y() - tileRect.y(), enclosingScreenDirty.width(), enclosingScreenDirty.height()); GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, subset, textureInval); tile->onBlitUpdate(); return true; }