bool Surface::blitFromContents(Tile* tile) { if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) return false; if (tile->frontTexture() != tile->lastDrawnTexture()) { // 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; }
void TileGrid::drawGL(const IntRect& visibleContentArea, float opacity, const TransformationMatrix* transform, const Color* background) { m_area = computeTilesArea(visibleContentArea, m_scale); if (m_area.width() == 0 || m_area.height() == 0) return; float invScale = 1.0 / m_scale; const float tileWidth = TilesManager::tileWidth() * invScale; const float tileHeight = TilesManager::tileHeight() * invScale; int drawn = 0; SkRegion missingRegion; bool semiOpaqueBaseSurface = background ? (background->hasAlpha() && background->alpha() > 0) : false; if (semiOpaqueBaseSurface) { SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(), m_area.width(), m_area.height()); missingRegion = SkRegion(totalArea); } bool usePointSampling = TilesManager::instance()->shader()->usePointSampling(m_scale, transform); float minTileX = visibleContentArea.x() / tileWidth; float minTileY = visibleContentArea.y() / tileWidth; float maxTileWidth = visibleContentArea.maxX() / tileWidth; float maxTileHeight = visibleContentArea.maxY() / tileWidth; ALOGV("minTileX, minTileY, maxTileWidth, maxTileHeight %f, %f, %f %f", minTileX, minTileY, maxTileWidth, maxTileHeight); for (unsigned int i = 0; i < m_tiles.size(); i++) { Tile* tile = m_tiles[i]; bool tileInView = tile->isTileVisible(m_area); if (tileInView) { SkRect rect; rect.fLeft = tile->x() * tileWidth; rect.fTop = tile->y() * tileHeight; rect.fRight = rect.fLeft + tileWidth; rect.fBottom = rect.fTop + tileHeight; ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d", tile, tile->isLayerTile(), tile->x(), tile->y(), tile->scale(), m_scale, tile->isTileReady(), tile->isDirty()); bool forceBaseBlending = background ? background->hasAlpha() : false; float left = std::max(minTileX - tile->x(), 0.0f); float top = std::max(minTileY - tile->y(), 0.0f); float right = std::min(maxTileWidth - tile->x(), 1.0f); float bottom = std::min(maxTileHeight - tile->y(), 1.0f); if (left > 1.0f || top > 1.0f || right < 0.0f || bottom < 0.0f) { ALOGE("Unexpected portion:left, top, right, bottom %f %f %f %f", left, top, right, bottom); left = 0.0f; top = 0.0f; right = 1.0f; bottom = 1.0f; } FloatRect fillPortion(left, top, right - left, bottom - top); bool success = tile->drawGL(opacity, rect, m_scale, transform, forceBaseBlending, usePointSampling, fillPortion); if (semiOpaqueBaseSurface && success) { // Cut the successful drawn tile area from the missing region. missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1), SkRegion::kDifference_Op); } if (tile->frontTexture()) drawn++; } // log tile information for base, high res tiles if (m_isBaseSurface && background) TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView); } // Draw missing Regions with blend turned on if (semiOpaqueBaseSurface) drawMissingRegion(missingRegion, opacity, background); ALOGV("TG %p drew %d tiles, scale %f", this, drawn, m_scale); }