FloatRect TilingData::tileBoundsNormalized(int tile) const { assertTile(tile); FloatRect bounds(tileBounds(tile)); bounds.scale(1.0f / m_totalSizeX, 1.0f / m_totalSizeY); return bounds; }
void CaptureGameEngine::Render(CIwRect& bounds) { float tileSize = 64.0f; float scale = tileSize / 256; CIwVec2 centerTileTL(bounds.x + (bounds.w - tileSize) / 2, bounds.y + (bounds.h - tileSize) / 2); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { CIwRect tileBounds(centerTileTL.x + (x * tileSize), centerTileTL.y + (y * tileSize), tileSize, tileSize); Utils::AlphaRenderImage(g_pTiles[x+1][y+1], tileBounds, 255); } } s3eLocation loc; Utils::GetLocation(&loc); int xDiff = LiveMaps::LongitudeToXAtZoom(loc.m_Longitude, LiveMaps::MaxZoom) - g_topLeft.x; int yDiff = LiveMaps::LatitudeToYAtZoom(loc.m_Latitude, LiveMaps::MaxZoom) - g_topLeft.y; CIwRect meLoc(centerTileTL.x + (xDiff * scale) - g_pUserTexture->GetWidth()/4, centerTileTL.y + (yDiff * scale) - g_pUserTexture->GetWidth()/4, g_pUserTexture->GetWidth() / 2, g_pUserTexture->GetWidth() / 2); Utils::AlphaRenderImage(g_pUserTexture, meLoc, 255); for (int i = 0; i < g_pCaptures.size(); ++i) { int xDiff = LiveMaps::LongitudeToXAtZoom(g_pCaptures[i]->m_Longitude, LiveMaps::MaxZoom) - g_topLeft.x; int yDiff = LiveMaps::LatitudeToYAtZoom(g_pCaptures[i]->m_Latitude, LiveMaps::MaxZoom) - g_topLeft.y; CIwRect capLoc(centerTileTL.x + (xDiff * scale) - g_pCursorTexture->GetWidth()/4, centerTileTL.y + (yDiff * scale) - g_pCursorTexture->GetWidth()/4, g_pCursorTexture->GetWidth() / 2, g_pCursorTexture->GetWidth() / 2); Utils::AlphaRenderImage(g_pCursorTexture, capLoc, 255); } }
void FindTheSpotGameEngine::Render(CIwRect& bounds) { CIwSVec2 imgBounds(bounds.x, bounds.y); Utils::AlphaRenderImage(g_pLogoTexture, imgBounds, 0xff); float tileSize = 160.0f; if (g_finalScore == 0) { CIwRect tileBounds(bounds.x + (bounds.w - tileSize) / 2, bounds.y + (bounds.h - tileSize) / 2, tileSize, tileSize); Utils::AlphaRenderImage(g_pTile, tileBounds, 255); CIwColour white; white.Set(0xff, 0xff, 0xff, 0x7f); float scale = tileSize / 256.0f; Iw2DSetColour(white); Iw2DDrawRect(CIwSVec2(tileBounds.x-1, tileBounds.y-1), CIwSVec2(tileBounds.w+2, tileBounds.h+2)); CIwSVec2 spotLoc(tileBounds.x + (g_tileLoc.x * scale) - (g_pCursorTexture->GetWidth() / 2), tileBounds.y + (g_tileLoc.y * scale) - (g_pCursorTexture->GetHeight() / 2)); Utils::AlphaRenderImage(g_pCursorTexture, spotLoc, 255); if (g_renderTemp == 1) { CIwSVec2 imgBounds(bounds.x, bounds.y + bounds.h - g_pColderTexture->GetHeight()); Utils::AlphaRenderImage(g_pColderTexture, imgBounds, 255); } else if (g_renderTemp == 2) { CIwSVec2 imgBounds(bounds.x, bounds.y + bounds.h - g_pHotterTexture->GetHeight()); Utils::AlphaRenderImage(g_pHotterTexture, imgBounds, 255); } } else { tileSize = g_pFoundTexture->GetWidth(); CIwRect tileBounds(bounds.x + (bounds.w - tileSize) / 2, bounds.y + (bounds.h - tileSize) / 2, tileSize, tileSize); Utils::AlphaRenderImage(g_pFoundTexture, tileBounds, 255); } }
void WorldMapSprite::draw(Renderer *renderer, const RenderContext &cxt){ Grid *grid = worldMap->getMapGrid(); for(int gridX = 0; gridX < grid->getWidth(); gridX++){ for(int gridY = 0; gridY < grid->getHeight(); gridY++){ Vector4 color(grid->sample(gridX, gridY), 0, 0, 1.0); Vector4 tileBounds( gridX*gridCellPixelWidth, gridY*gridCellPixelWidth, gridCellPixelWidth, gridCellPixelWidth ); renderer->fillRect(tileBounds, color, cxt); } } }
std::shared_ptr<TileData> TopoJsonSource::parse(const TileTask& _task, const MapProjection& _projection) const { auto& task = static_cast<const DownloadTileTask&>(_task); std::shared_ptr<TileData> tileData = std::make_shared<TileData>(); // Parse data into a JSON document const char* error; size_t offset; auto document = JsonParseBytes(task.rawTileData->data(), task.rawTileData->size(), &error, &offset); if (error) { LOGE("Json parsing failed on tile [%s]: %s (%u)", task.tileId().toString().c_str(), error, offset); return tileData; } // Transform JSON data into a TileData using TopoJson functions BoundingBox tileBounds(_projection.TileBounds(task.tileId())); glm::dvec2 tileOrigin = {tileBounds.min.x, tileBounds.max.y*-1.0}; double tileInverseScale = 1.0 / tileBounds.width(); const auto projFn = [&](glm::dvec2 _lonLat){ glm::dvec2 tmp = _projection.LonLatToMeters(_lonLat); return Point { (tmp.x - tileOrigin.x) * tileInverseScale, (tmp.y - tileOrigin.y) * tileInverseScale, 0 }; }; // Parse topology and transform auto topology = TopoJson::getTopology(document, projFn); // Parse each TopoJson object as a data layer auto objectsIt = document.FindMember("objects"); if (objectsIt == document.MemberEnd()) { return tileData; } auto& objects = objectsIt->value; for (auto layer = objects.MemberBegin(); layer != objects.MemberEnd(); ++layer) { tileData->layers.push_back(TopoJson::getLayer(layer, topology, m_id)); } // Discard JSON object and return TileData return tileData; }
std::unique_ptr<AnnotationTile> AnnotationManager::getTile(const TileID& tileID) { auto tile = std::make_unique<AnnotationTile>(); AnnotationTileLayer& pointLayer = *tile->layers.emplace( PointLayerID, std::make_unique<AnnotationTileLayer>()).first->second; LatLngBounds tileBounds(tileID); pointTree.query(boost::geometry::index::intersects(tileBounds), boost::make_function_output_iterator([&](const auto& val){ val->updateLayer(tileID, pointLayer); })); for (const auto& shape : shapeAnnotations) { shape.second->updateTile(tileID, *tile); } return tile; }
IntRect TilingData::tileBoundsWithBorder(int tile) const { IntRect bounds = tileBounds(tile); if (m_borderTexels) { int x1 = bounds.x(); int x2 = bounds.maxX(); int y1 = bounds.y(); int y2 = bounds.maxY(); if (tileXIndex(tile) > 0) x1--; if (tileXIndex(tile) < (numTilesX() - 1)) x2++; if (tileYIndex(tile) > 0) y1--; if (tileYIndex(tile) < (numTilesY() - 1)) y2++; bounds = IntRect(x1, y1, x2 - x1, y2 - y1); } return bounds; }
void BDPTIntegrator::Render(const Scene &scene) { std::unique_ptr<LightDistribution> lightDistribution = CreateLightSampleDistribution(lightSampleStrategy, scene); // Compute a reverse mapping from light pointers to offsets into the // scene lights vector (and, equivalently, offsets into // lightDistr). Added after book text was finalized; this is critical // to reasonable performance with 100s+ of light sources. std::unordered_map<const Light *, size_t> lightToIndex; for (size_t i = 0; i < scene.lights.size(); ++i) lightToIndex[scene.lights[i].get()] = i; // Partition the image into tiles Film *film = camera->film; const Bounds2i sampleBounds = film->GetSampleBounds(); const Vector2i sampleExtent = sampleBounds.Diagonal(); const int tileSize = 16; const int nXTiles = (sampleExtent.x + tileSize - 1) / tileSize; const int nYTiles = (sampleExtent.y + tileSize - 1) / tileSize; ProgressReporter reporter(nXTiles * nYTiles, "Rendering"); // Allocate buffers for debug visualization const int bufferCount = (1 + maxDepth) * (6 + maxDepth) / 2; std::vector<std::unique_ptr<Film>> weightFilms(bufferCount); if (visualizeStrategies || visualizeWeights) { for (int depth = 0; depth <= maxDepth; ++depth) { for (int s = 0; s <= depth + 2; ++s) { int t = depth + 2 - s; if (t == 0 || (s == 1 && t == 1)) continue; std::string filename = StringPrintf("bdpt_d%02i_s%02i_t%02i.exr", depth, s, t); weightFilms[BufferIndex(s, t)] = std::unique_ptr<Film>(new Film( film->fullResolution, Bounds2f(Point2f(0, 0), Point2f(1, 1)), std::unique_ptr<Filter>(CreateBoxFilter(ParamSet())), film->diagonal * 1000, filename, 1.f)); } } } // Render and write the output image to disk if (scene.lights.size() > 0) { ParallelFor2D([&](const Point2i tile) { // Render a single tile using BDPT MemoryArena arena; int seed = tile.y * nXTiles + tile.x; std::unique_ptr<Sampler> tileSampler = sampler->Clone(seed); int x0 = sampleBounds.pMin.x + tile.x * tileSize; int x1 = std::min(x0 + tileSize, sampleBounds.pMax.x); int y0 = sampleBounds.pMin.y + tile.y * tileSize; int y1 = std::min(y0 + tileSize, sampleBounds.pMax.y); Bounds2i tileBounds(Point2i(x0, y0), Point2i(x1, y1)); std::unique_ptr<FilmTile> filmTile = camera->film->GetFilmTile(tileBounds); for (Point2i pPixel : tileBounds) { tileSampler->StartPixel(pPixel); if (!InsideExclusive(pPixel, pixelBounds)) continue; do { // Generate a single sample using BDPT Point2f pFilm = (Point2f)pPixel + tileSampler->Get2D(); // Trace the camera subpath Vertex *cameraVertices = arena.Alloc<Vertex>(maxDepth + 2); Vertex *lightVertices = arena.Alloc<Vertex>(maxDepth + 1); int nCamera = GenerateCameraSubpath( scene, *tileSampler, arena, maxDepth + 2, *camera, pFilm, cameraVertices); // Get a distribution for sampling the light at the // start of the light subpath. Because the light path // follows multiple bounces, basing the sampling // distribution on any of the vertices of the camera // path is unlikely to be a good strategy. We use the // PowerLightDistribution by default here, which // doesn't use the point passed to it. const Distribution1D *lightDistr = lightDistribution->Lookup(cameraVertices[0].p()); // Now trace the light subpath int nLight = GenerateLightSubpath( scene, *tileSampler, arena, maxDepth + 1, cameraVertices[0].time(), *lightDistr, lightToIndex, lightVertices); // Execute all BDPT connection strategies Spectrum L(0.f); for (int t = 1; t <= nCamera; ++t) { for (int s = 0; s <= nLight; ++s) { int depth = t + s - 2; if ((s == 1 && t == 1) || depth < 0 || depth > maxDepth) continue; // Execute the $(s, t)$ connection strategy and // update _L_ Point2f pFilmNew = pFilm; Float misWeight = 0.f; Spectrum Lpath = ConnectBDPT( scene, lightVertices, cameraVertices, s, t, *lightDistr, lightToIndex, *camera, *tileSampler, &pFilmNew, &misWeight); VLOG(2) << "Connect bdpt s: " << s <<", t: " << t << ", Lpath: " << Lpath << ", misWeight: " << misWeight; if (visualizeStrategies || visualizeWeights) { Spectrum value; if (visualizeStrategies) value = misWeight == 0 ? 0 : Lpath / misWeight; if (visualizeWeights) value = Lpath; weightFilms[BufferIndex(s, t)]->AddSplat( pFilmNew, value); } if (t != 1) L += Lpath; else film->AddSplat(pFilmNew, Lpath); } } VLOG(2) << "Add film sample pFilm: " << pFilm << ", L: " << L << ", (y: " << L.y() << ")"; filmTile->AddSample(pFilm, L); arena.Reset(); } while (tileSampler->StartNextSample()); } film->MergeFilmTile(std::move(filmTile)); reporter.Update(); }, Point2i(nXTiles, nYTiles)); reporter.Done(); } film->WriteImage(1.0f / sampler->samplesPerPixel); // Write buffers for debug visualization if (visualizeStrategies || visualizeWeights) { const Float invSampleCount = 1.0f / sampler->samplesPerPixel; for (size_t i = 0; i < weightFilms.size(); ++i) if (weightFilms[i]) weightFilms[i]->WriteImage(invSampleCount); } }
void BoundsCreator::processTile(int z, int i, int j, BoundsMap& bounds, VolumeMap& volumes) { std::string path = m_stack.getTilePath(0, j, i, 's', z); printf("%s\n", path.c_str()); PngImage image(path.c_str()); int width = image.getWidth(); int height = image.getHeight(); //printf("width=%d height=%d\n", image.getWidth(), image.getHeight()); // Note that i, j always represent full tiles, because only the // final edge/corner tile is ever partial int baseX = m_tilesize * i; int baseY = m_tilesize * j; // As a special case for speed if tile is completely empty we can // union in the bounds in a single step. if (isTileEmpty(image)) { int edgeX = baseX + width - 1; int edgeY = baseY + height - 1; PixelBoundBox tileBounds(baseX, baseY, edgeX, edgeY); if (bounds.find(0) == bounds.end()) { bounds[0] = tileBounds; volumes[0] = width * height; } else { bounds[0].unionBounds(tileBounds); volumes[0] += (width * height); } } else { // The tile is not empty, union every pixel separately for (int tileX = 0; tileX < width; ++tileX) { for (int tileY = 0; tileY < height; ++tileY) { int x = baseX + tileX; int y = baseY + tileY; unsigned int spid = image.getPixelID(tileX, tileY); if (bounds.find(spid) == bounds.end()) { bounds[spid] = PixelBoundBox(x, y); volumes[spid] = 1; } else { bounds[spid].unionPoint(x, y); volumes[spid] += 1; } } } } }
// SamplerIntegrator Method Definitions void SamplerIntegrator::Render(const Scene &scene) { ProfilePhase p(Prof::IntegratorRender); Preprocess(scene, *sampler); // Render image tiles in parallel // Compute number of tiles, _nTiles_, to use for parallel rendering Bounds2i sampleBounds = camera->film->GetSampleBounds(); Vector2i sampleExtent = sampleBounds.Diagonal(); const int tileSize = 16; Point2i nTiles((sampleExtent.x + tileSize - 1) / tileSize, (sampleExtent.y + tileSize - 1) / tileSize); ProgressReporter reporter(nTiles.x * nTiles.y, "Rendering"); { StatTimer timer(&renderingTime); ParallelFor2D([&](Point2i tile) { // Render section of image corresponding to _tile_ // Allocate _MemoryArena_ for tile MemoryArena arena; // Get sampler instance for tile int seed = tile.y * nTiles.x + tile.x; std::unique_ptr<Sampler> tileSampler = sampler->Clone(seed); // Compute sample bounds for tile int x0 = sampleBounds.pMin.x + tile.x * tileSize; int x1 = std::min(x0 + tileSize, sampleBounds.pMax.x); int y0 = sampleBounds.pMin.y + tile.y * tileSize; int y1 = std::min(y0 + tileSize, sampleBounds.pMax.y); Bounds2i tileBounds(Point2i(x0, y0), Point2i(x1, y1)); // Get _FilmTile_ for tile std::unique_ptr<FilmTile> filmTile = camera->film->GetFilmTile(tileBounds); // Loop over pixels in tile to render them for (Point2i pixel : tileBounds) { { ProfilePhase pp(Prof::StartPixel); tileSampler->StartPixel(pixel); } do { // Initialize _CameraSample_ for current sample CameraSample cameraSample = tileSampler->GetCameraSample(pixel); // Generate camera ray for current sample RayDifferential ray; Float rayWeight = camera->GenerateRayDifferential(cameraSample, &ray); ray.ScaleDifferentials( 1 / std::sqrt((Float)tileSampler->samplesPerPixel)); ++nCameraRays; // Evaluate radiance along camera ray Spectrum L(0.f); if (rayWeight > 0) L = Li(ray, scene, *tileSampler, arena); // Issue warning if unexpected radiance value returned if (L.HasNaNs()) { Error( "Not-a-number radiance value returned " "for image sample. Setting to black."); L = Spectrum(0.f); } else if (L.y() < -1e-5) { Error( "Negative luminance value, %f, returned " "for image sample. Setting to black.", L.y()); L = Spectrum(0.f); } else if (std::isinf(L.y())) { Error( "Infinite luminance value returned " "for image sample. Setting to black."); L = Spectrum(0.f); } // Add camera ray's contribution to image filmTile->AddSample(cameraSample.pFilm, L, rayWeight); // Free _MemoryArena_ memory from computing image sample // value arena.Reset(); } while (tileSampler->StartNextSample()); } // Merge image tile into _Film_ camera->film->MergeFilmTile(std::move(filmTile)); reporter.Update(); }, nTiles); reporter.Done(); } // Save final image after rendering camera->film->WriteImage(); }
void BDPTIntegrator::Render(const Scene &scene) { ProfilePhase p(Prof::IntegratorRender); // Compute _lightDistr_ for sampling lights proportional to power std::unique_ptr<Distribution1D> lightDistr = ComputeLightPowerDistribution(scene); // Partition the image into tiles Film *film = camera->film; const Bounds2i sampleBounds = film->GetSampleBounds(); const Vector2i sampleExtent = sampleBounds.Diagonal(); const int tileSize = 16; const int nXTiles = (sampleExtent.x + tileSize - 1) / tileSize; const int nYTiles = (sampleExtent.y + tileSize - 1) / tileSize; ProgressReporter reporter(nXTiles * nYTiles, "Rendering"); // Allocate buffers for debug visualization const int bufferCount = (1 + maxDepth) * (6 + maxDepth) / 2; std::vector<std::unique_ptr<Film>> weightFilms(bufferCount); if (visualizeStrategies || visualizeWeights) { for (int depth = 0; depth <= maxDepth; ++depth) { for (int s = 0; s <= depth + 2; ++s) { int t = depth + 2 - s; if (t == 0 || (s == 1 && t == 1)) continue; std::string filename = StringPrintf("bdpt_d%02i_s%02i_t%02i.exr", depth, s, t); weightFilms[BufferIndex(s, t)] = std::unique_ptr<Film>(new Film( film->fullResolution, Bounds2f(Point2f(0, 0), Point2f(1, 1)), std::unique_ptr<Filter>(CreateBoxFilter(ParamSet())), film->diagonal * 1000, filename, 1.f)); } } } // Render and write the output image to disk if (scene.lights.size() > 0) { StatTimer timer(&renderingTime); ParallelFor2D([&](const Point2i tile) { // Render a single tile using BDPT MemoryArena arena; int seed = tile.y * nXTiles + tile.x; std::unique_ptr<Sampler> tileSampler = sampler->Clone(seed); int x0 = sampleBounds.pMin.x + tile.x * tileSize; int x1 = std::min(x0 + tileSize, sampleBounds.pMax.x); int y0 = sampleBounds.pMin.y + tile.y * tileSize; int y1 = std::min(y0 + tileSize, sampleBounds.pMax.y); Bounds2i tileBounds(Point2i(x0, y0), Point2i(x1, y1)); std::unique_ptr<FilmTile> filmTile = camera->film->GetFilmTile(tileBounds); for (Point2i pPixel : tileBounds) { tileSampler->StartPixel(pPixel); if (!InsideExclusive(pPixel, pixelBounds)) continue; do { // Generate a single sample using BDPT Point2f pFilm = (Point2f)pPixel + tileSampler->Get2D(); // Trace the camera and light subpaths Vertex *cameraVertices = arena.Alloc<Vertex>(maxDepth + 2); Vertex *lightVertices = arena.Alloc<Vertex>(maxDepth + 1); int nCamera = GenerateCameraSubpath( scene, *tileSampler, arena, maxDepth + 2, *camera, pFilm, cameraVertices); int nLight = GenerateLightSubpath( scene, *tileSampler, arena, maxDepth + 1, cameraVertices[0].time(), *lightDistr, lightVertices); // Execute all BDPT connection strategies Spectrum L(0.f); for (int t = 1; t <= nCamera; ++t) { for (int s = 0; s <= nLight; ++s) { int depth = t + s - 2; if ((s == 1 && t == 1) || depth < 0 || depth > maxDepth) continue; // Execute the $(s, t)$ connection strategy and // update _L_ Point2f pFilmNew = pFilm; Float misWeight = 0.f; Spectrum Lpath = ConnectBDPT( scene, lightVertices, cameraVertices, s, t, *lightDistr, *camera, *tileSampler, &pFilmNew, &misWeight); if (visualizeStrategies || visualizeWeights) { Spectrum value; if (visualizeStrategies) value = misWeight == 0 ? 0 : Lpath / misWeight; if (visualizeWeights) value = Lpath; weightFilms[BufferIndex(s, t)]->AddSplat( pFilmNew, value); } if (t != 1) L += Lpath; else film->AddSplat(pFilmNew, Lpath); } } filmTile->AddSample(pFilm, L); arena.Reset(); } while (tileSampler->StartNextSample()); } film->MergeFilmTile(std::move(filmTile)); reporter.Update(); }, Point2i(nXTiles, nYTiles)); reporter.Done(); } film->WriteImage(1.0f / sampler->samplesPerPixel); // Write buffers for debug visualization if (visualizeStrategies || visualizeWeights) { const Float invSampleCount = 1.0f / sampler->samplesPerPixel; for (size_t i = 0; i < weightFilms.size(); ++i) if (weightFilms[i]) weightFilms[i]->WriteImage(invSampleCount); } }
bool ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion, nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData, bool aIsRepeated) { aRegionToPaint = aInvalidRegion; // If the composition bounds rect is empty, we can't make any sensible // decision about how to update coherently. In this case, just update // everything in one transaction. if (aPaintData->mCompositionBounds.IsEmpty()) { aPaintData->mPaintFinished = true; return false; } // If this is a low precision buffer, we force progressive updates. The // assumption is that the contents is less important, so visual coherency // is lower priority than speed. bool drawingLowPrecision = IsLowPrecision(); // Find out if we have any non-stale content to update. nsIntRegion staleRegion; staleRegion.And(aInvalidRegion, aOldValidRegion); // Find out the current view transform to determine which tiles to draw // first, and see if we should just abort this paint. Aborting is usually // caused by there being an incoming, more relevant paint. ParentLayerRect compositionBounds; CSSToParentLayerScale zoom; #if defined(MOZ_WIDGET_ANDROID) bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), compositionBounds, zoom, !drawingLowPrecision); #else MOZ_ASSERT(mSharedFrameMetricsHelper); ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( parent, !staleRegion.Contains(aInvalidRegion), drawingLowPrecision, compositionBounds, zoom); #endif if (abortPaint) { // We ignore if front-end wants to abort if this is the first, // non-low-precision paint, as in that situation, we're about to override // front-end's page/viewport metrics. if (!aPaintData->mFirstPaint || drawingLowPrecision) { PROFILER_LABEL("ContentClient", "Abort painting"); aRegionToPaint.SetEmpty(); return aIsRepeated; } } // Transform the screen coordinates into transformed layout device coordinates. LayoutDeviceRect transformedCompositionBounds = TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, aPaintData->mResolution, aPaintData->mTransformParentLayerToLayout); // Paint tiles that have stale content or that intersected with the screen // at the time of issuing the draw command in a single transaction first. // This is to avoid rendering glitches on animated page content, and when // layers change size/shape. LayoutDeviceRect coherentUpdateRect = transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); nsIntRect roundedCoherentUpdateRect = LayoutDeviceIntRect::ToUntyped(RoundedOut(coherentUpdateRect)); aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); aRegionToPaint.Or(aRegionToPaint, staleRegion); bool drawingStale = !aRegionToPaint.IsEmpty(); if (!drawingStale) { aRegionToPaint = aInvalidRegion; } // Prioritise tiles that are currently visible on the screen. bool paintVisible = false; if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); paintVisible = true; } // Paint area that's visible and overlaps previously valid content to avoid // visible glitches in animated elements, such as gifs. bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); // The following code decides what order to draw tiles in, based on the // current scroll direction of the primary scrollable layer. NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); nsIntRect paintBounds = aRegionToPaint.GetBounds(); int startX, incX, startY, incY; int tileLength = GetScaledTileLength(); if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { startX = RoundDownToTileEdge(paintBounds.x); incX = tileLength; } else { startX = RoundDownToTileEdge(paintBounds.XMost() - 1); incX = -tileLength; } if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { startY = RoundDownToTileEdge(paintBounds.y); incY = tileLength; } else { startY = RoundDownToTileEdge(paintBounds.YMost() - 1); incY = -tileLength; } // Find a tile to draw. nsIntRect tileBounds(startX, startY, tileLength, tileLength); int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; // This loop will always terminate, as there is at least one tile area // along the first/last row/column intersecting with regionToPaint, or its // bounds would have been smaller. while (true) { aRegionToPaint.And(aInvalidRegion, tileBounds); if (!aRegionToPaint.IsEmpty()) { break; } if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { tileBounds.x += incX; } else { tileBounds.y += incY; } } if (!aRegionToPaint.Contains(aInvalidRegion)) { // The region needed to paint is larger then our progressive chunk size // therefore update what we want to paint and ask for a new paint transaction. // If we need to draw more than one tile to maintain coherency, make // sure it happens in the same transaction by requesting this work be // repeated immediately. // If this is unnecessary, the remaining work will be done tile-by-tile in // subsequent transactions. if (!drawingLowPrecision && paintInSingleTransaction) { return true; } mManager->SetRepeatTransaction(); return false; } // We're not repeating painting and we've not requested a repeat transaction, // so the paint is finished. If there's still a separate low precision // paint to do, it will get marked as unfinished later. aPaintData->mPaintFinished = true; return false; }