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 SkTableMaskFilter::MakeGammaTable(uint8_t table[256], SkScalar gamma) { const float dx = 1 / 255.0f; const float g = SkScalarToFloat(gamma); float x = 0; for (int i = 0; i < 256; i++) { // float ee = powf(x, g) * 255; table[i] = SkPin32(sk_float_round2int(powf(x, g) * 255), 0, 255); x += dx; } }
SkFontStyle::SkFontStyle(int weight, int width, Slant slant) { fUnion.fU32 = 0; fUnion.fR.fWeight = SkPin32(weight, kThin_Weight, kBlack_Weight); fUnion.fR.fWidth = SkPin32(width, kUltraCondensed_Width, kUltaExpanded_Width); fUnion.fR.fSlant = SkPin32(slant, kUpright_Slant, kItalic_Slant); }
bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* dst, SkIPoint* offset) const { if ((src.colorType() != kN32_SkColorType) || (fSrcRect.width() >= src.width()) || (fSrcRect.height() >= src.height())) { return false; } SkAutoLockPixels alp(src); SkASSERT(src.getPixels()); if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { return false; } if (!dst->tryAllocPixels(src.info())) { return false; } SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; SkScalar inv_x_zoom = fSrcRect.width() / src.width(); SkScalar inv_y_zoom = fSrcRect.height() / src.height(); SkColor* sptr = src.getAddr32(0, 0); SkColor* dptr = dst->getAddr32(0, 0); int width = src.width(), height = src.height(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset; SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset; SkScalar weight = 0; static const SkScalar kScalar2 = SkScalar(2); // To create a smooth curve at the corners, we need to work on // a square twice the size of the inset. if (x_dist < kScalar2 && y_dist < kScalar2) { x_dist = kScalar2 - x_dist; y_dist = kScalar2 - y_dist; SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + SkScalarSquare(y_dist)); dist = SkMaxScalar(kScalar2 - dist, 0); weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); } else { SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), SkScalarSquare(y_dist)); weight = SkMinScalar(sqDist, SK_Scalar1); } SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) + (SK_Scalar1 - weight) * x; SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) + (SK_Scalar1 - weight) * y; int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1); int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1); *dptr = sptr[y_val * width + x_val]; dptr++; } } return true; }
// Convert user-space bounds to grid tiles they cover (LT and RB both inclusive). void SkTileGrid::userToGrid(const SkRect& user, SkIRect* grid) const { grid->fLeft = SkPin32(user.left() * fInvWidth , 0, fXTiles - 1); grid->fTop = SkPin32(user.top() * fInvHeight, 0, fYTiles - 1); grid->fRight = SkPin32(user.right() * fInvWidth , 0, fXTiles - 1); grid->fBottom = SkPin32(user.bottom() * fInvHeight, 0, fYTiles - 1); }
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]++; } } } }
static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) { x = SkPin32(x, bounds.fLeft, bounds.fRight - 1); y = SkPin32(y, bounds.fTop, bounds.fBottom - 1); return *src.getAddr32(x, y); }