/** * Return a random SkIRect inside the range specified. * @param rand Random number generator. * @param maxX Exclusive maximum x-coordinate. SkIRect's fLeft and fRight will be * in the range [0, maxX) * @param maxY Exclusive maximum y-coordinate. SkIRect's fTop and fBottom will be * in the range [0, maxY) * @return SkIRect Non-empty, non-degenerate rectangle. */ static SkIRect generate_random_rect(SkRandom* rand, int32_t maxX, int32_t maxY) { SkASSERT(maxX > 1 && maxY > 1); int32_t left = rand->nextULessThan(maxX); int32_t right = rand->nextULessThan(maxX); int32_t top = rand->nextULessThan(maxY); int32_t bottom = rand->nextULessThan(maxY); SkIRect rect = SkIRect::MakeLTRB(left, top, right, bottom); rect.sort(); // Make sure rect is not empty. if (rect.fLeft == rect.fRight) { if (rect.fLeft > 0) { rect.fLeft--; } else { rect.fRight++; // This branch is only taken if 0 == rect.fRight, and // maxX must be at least 2, so it must still be in // range. SkASSERT(rect.fRight < maxX); } } if (rect.fTop == rect.fBottom) { if (rect.fTop > 0) { rect.fTop--; } else { rect.fBottom++; // Again, this must be in range. SkASSERT(rect.fBottom < maxY); } } return rect; }
SkIRect generate_random_subset(SkRandom* rand, int w, int h) { SkIRect rect; do { rect.fLeft = rand->nextRangeU(0, w); rect.fTop = rand->nextRangeU(0, h); rect.fRight = rand->nextRangeU(0, w); rect.fBottom = rand->nextRangeU(0, h); rect.sort(); } while (rect.isEmpty()); return rect; }
static SkIRect random_rect(SkRandom& rand) { SkIRect rect = {0,0,0,0}; while (rect.isEmpty()) { rect.fLeft = rand.nextS() % MAX_SIZE; rect.fRight = rand.nextS() % MAX_SIZE; rect.fTop = rand.nextS() % MAX_SIZE; rect.fBottom = rand.nextS() % MAX_SIZE; rect.sort(); } return rect; }
void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) { SkIRect adjustedQuery = query; // The inset is to counteract the outset that was applied in 'insert' // The outset/inset is to optimize for lookups of size // 'tileInterval + 2 * margin' that are aligned with the tile grid. adjustedQuery.inset(fInfo.fMargin.width(), fInfo.fMargin.height()); adjustedQuery.offset(fInfo.fOffset); adjustedQuery.sort(); // in case the inset inverted the rectangle // Convert the query rectangle from device coordinates to tile coordinates // by rounding outwards to the nearest tile boundary so that the resulting tile // region includes the query rectangle. (using truncating division to "floor") int tileStartX = adjustedQuery.left() / fInfo.fTileInterval.width(); int tileEndX = (adjustedQuery.right() + fInfo.fTileInterval.width() - 1) / fInfo.fTileInterval.width(); int tileStartY = adjustedQuery.top() / fInfo.fTileInterval.height(); int tileEndY = (adjustedQuery.bottom() + fInfo.fTileInterval.height() - 1) / fInfo.fTileInterval.height(); tileStartX = SkPin32(tileStartX, 0, fXTileCount - 1); tileEndX = SkPin32(tileEndX, tileStartX+1, fXTileCount); tileStartY = SkPin32(tileStartY, 0, fYTileCount - 1); tileEndY = SkPin32(tileEndY, tileStartY+1, fYTileCount); int queryTileCount = (tileEndX - tileStartX) * (tileEndY - tileStartY); SkASSERT(queryTileCount); if (queryTileCount == 1) { *results = this->tile(tileStartX, tileStartY); } else { results->reset(); SkTDArray<int> curPositions; curPositions.setCount(queryTileCount); // Note: Reserving space for 1024 tile pointers on the stack. If the // malloc becomes a bottleneck, we may consider increasing that number. // Typical large web page, say 2k x 16k, would require 512 tiles of // size 256 x 256 pixels. SkAutoSTArray<1024, SkTDArray<void *>*> storage(queryTileCount); SkTDArray<void *>** tileRange = storage.get(); int tile = 0; for (int x = tileStartX; x < tileEndX; ++x) { for (int y = tileStartY; y < tileEndY; ++y) { tileRange[tile] = &this->tile(x, y); curPositions[tile] = tileRange[tile]->count() ? 0 : kTileFinished; ++tile; } } void *nextElement; while(NULL != (nextElement = fNextDatumFunction(tileRange, curPositions))) { results->push(nextElement); } } }
void SkTileGrid::search(const SkRect& query, SkTDArray<void*>* results) const { SkIRect adjusted; query.roundOut(&adjusted); // The inset is to counteract the outset that was applied in 'insert' // The outset/inset is to optimize for lookups of size // 'tileInterval + 2 * margin' that are aligned with the tile grid. adjusted.inset(fInfo.fMargin.width(), fInfo.fMargin.height()); adjusted.offset(fInfo.fOffset); adjusted.sort(); // in case the inset inverted the rectangle // Convert the query rectangle from device coordinates to tile coordinates // by rounding outwards to the nearest tile boundary so that the resulting tile // region includes the query rectangle. int startX = adjusted.left() / fInfo.fTileInterval.width(), startY = adjusted.top() / fInfo.fTileInterval.height(); int endX = divide_ceil(adjusted.right(), fInfo.fTileInterval.width()), endY = divide_ceil(adjusted.bottom(), fInfo.fTileInterval.height()); // Logically, we could pin endX to [startX, fXTiles], but we force it // up to (startX, fXTiles] to make sure we hit at least one tile. // This snaps just-out-of-bounds queries to the neighboring border tile. // I don't know if this is an important feature outside of unit tests. startX = SkPin32(startX, 0, fXTiles - 1); startY = SkPin32(startY, 0, fYTiles - 1); endX = SkPin32(endX, startX + 1, fXTiles); endY = SkPin32(endY, startY + 1, fYTiles); const int tilesHit = (endX - startX) * (endY - startY); SkASSERT(tilesHit > 0); if (tilesHit == 1) { // A performance shortcut. The merging code below would work fine here too. const SkTDArray<Entry>& tile = fTiles[startY * fXTiles + startX]; results->setCount(tile.count()); for (int i = 0; i < tile.count(); i++) { (*results)[i] = tile[i].data; } return; } // We've got to merge the data in many tiles into a single sorted and deduplicated stream. // We do a simple k-way merge based on the order the data was inserted. // Gather pointers to the starts and ends of the tiles to merge. SkAutoSTArray<kStackAllocationTileCount, const Entry*> starts(tilesHit), ends(tilesHit); int i = 0; for (int x = startX; x < endX; x++) { for (int y = startY; y < endY; y++) { starts[i] = fTiles[y * fXTiles + x].begin(); ends[i] = fTiles[y * fXTiles + x].end(); i++; } } // Merge tiles into results until they're fully consumed. results->reset(); while (true) { // The tiles themselves are already ordered, so the earliest is at the front of some tile. // It may be at the front of several, even all, tiles. const Entry* earliest = NULL; for (int i = 0; i < starts.count(); i++) { if (starts[i] < ends[i]) { if (NULL == earliest || starts[i]->order < earliest->order) { earliest = starts[i]; } } } // If we didn't find an earliest entry, there isn't anything left to merge. if (NULL == earliest) { return; } // We did find an earliest entry. Output it, and step forward every tile that contains it. results->push(earliest->data); for (int i = 0; i < starts.count(); i++) { if (starts[i] < ends[i] && starts[i]->order == earliest->order) { starts[i]++; } } } }