void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) { SkASSERT(!bounds.isEmpty()); SkIRect dilatedBounds = bounds; dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height()); dilatedBounds.offset(fInfo.fOffset); if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) { return; } // Note: SkIRects are non-inclusive of the right() column and bottom() row, // hence the "-1"s in the computations of maxTileX and maxTileY. int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), fXTileCount - 1), 0); int maxTileX = SkMax32(SkMin32((dilatedBounds.right() - 1) / fInfo.fTileInterval.width(), fXTileCount - 1), 0); int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), fYTileCount -1), 0); int maxTileY = SkMax32(SkMin32((dilatedBounds.bottom() -1) / fInfo.fTileInterval.height(), fYTileCount -1), 0); for (int x = minTileX; x <= maxTileX; x++) { for (int y = minTileY; y <= maxTileY; y++) { this->tile(x, y).push(data); } } fInsertionCount++; }
void SkTileGrid::insert(void* data, const SkRect& fbounds, bool) { SkASSERT(!fbounds.isEmpty()); SkIRect dilatedBounds; if (fbounds.isLargest()) { // Dilating the largest SkIRect will overflow. Other nearly-largest rects may overflow too, // but we don't make active use of them like we do the largest. dilatedBounds.setLargest(); } else { fbounds.roundOut(&dilatedBounds); dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height()); dilatedBounds.offset(fInfo.fOffset); } const SkIRect gridBounds = { 0, 0, fInfo.fTileInterval.width() * fXTiles, fInfo.fTileInterval.height() * fYTiles }; if (!SkIRect::Intersects(dilatedBounds, gridBounds)) { return; } // Note: SkIRects are non-inclusive of the right() column and bottom() row, // hence the "-1"s in the computations of maxX and maxY. int minX = SkMax32(0, SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), fXTiles - 1)); int minY = SkMax32(0, SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), fYTiles - 1)); int maxX = SkMax32(0, SkMin32((dilatedBounds.right() - 1) / fInfo.fTileInterval.width(), fXTiles - 1)); int maxY = SkMax32(0, SkMin32((dilatedBounds.bottom() - 1) / fInfo.fTileInterval.height(), fYTiles - 1)); Entry entry = { fCount++, data }; for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { fTiles[y * fXTiles + x].push(entry); } } }
bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkIRect bounds = src; SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctm.mapVectors(&radius, 1); bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y())); if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) { return false; } *dst = bounds; return true; }
bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkIRect bounds = src; SkVector scale = SkVector::Make(fScale, fScale); ctm.mapVectors(&scale, 1); bounds.outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf), SkScalarCeilToInt(scale.fY * SK_ScalarHalf)); if (getColorInput()) { return getColorInput()->filterBounds(bounds, ctm, dst); } *dst = bounds; return true; }
bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkIRect bounds = src; if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { return false; } SkVector sigma = SkVector::Make(fSigma.width(), fSigma.height()); ctm.mapVectors(&sigma, 1); bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); *dst = bounds; return true; }
bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkIRect bounds = src; if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) { return false; } SkVector offsetVec = SkVector::Make(fDx, fDy); ctm.mapVectors(&offsetVec, 1); bounds.offset(-SkScalarCeilToInt(offsetVec.x()), -SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); bounds.join(src); *dst = bounds; return true; }
SkIRect SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection direction) const { SkVector offsetVec = SkVector::Make(fDx, fDy); if (kReverse_MapDirection == direction) { offsetVec.negate(); } ctm.mapVectors(&offsetVec, 1); SkIRect dst = src.makeOffset(SkScalarCeilToInt(offsetVec.x()), SkScalarCeilToInt(offsetVec.y())); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctm.mapVectors(&sigma, 1); dst.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))), SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3)))); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { dst.join(src); } return dst; }
void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) { SkASSERT(!bounds.isEmpty()); SkIRect dilatedBounds = bounds; dilatedBounds.outset(1,1); // Consideration for filtering and AA if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) { return; } int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fTileWidth, fXTileCount - 1), 0); int maxTileX = SkMax32(SkMin32(dilatedBounds.right() / fTileWidth, fXTileCount - 1), 0); int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fTileHeight, fYTileCount -1), 0); int maxTileY = SkMax32(SkMin32(dilatedBounds.bottom() / fTileHeight, fYTileCount -1), 0); for (int x = minTileX; x <= maxTileX; x++) { for (int y = minTileY; y <= maxTileY; y++) { this->tile(x, y).push(data); } } fInsertionCount++; }
DEF_TEST(CanvasState_test_complex_clips, reporter) { const int WIDTH = 400; const int HEIGHT = 400; const int SPACER = 10; SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4); layerRect.inset(2*SPACER, 2*SPACER); SkIRect clipRect = layerRect; clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER); clipRect.outset(SPACER, SPACER); SkIRect regionBounds = clipRect; regionBounds.offset(clipRect.width() + (2*SPACER), 0); SkIRect regionInterior = regionBounds; regionInterior.inset(SPACER*3, SPACER*3); SkRegion clipRegion; clipRegion.setRect(regionBounds); clipRegion.op(regionInterior, SkRegion::kDifference_Op); const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op, SkRegion::kIntersect_Op, SkRegion::kReplace_Op, }; const SkCanvas::SaveLayerFlags flags[] = { static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), 0, static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag), }; REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags)); bool (*drawFn)(SkCanvasState* state, int32_t l, int32_t t, int32_t r, int32_t b, int32_t clipOp, int32_t regionRects, int32_t* rectCoords); OpenLibResult openLibResult(reporter); if (openLibResult.handle() != nullptr) { *(void**) (&drawFn) = dlsym(openLibResult.handle(), "complex_clips_draw_from_canvas_state"); } else { drawFn = complex_clips_draw_from_canvas_state; } REPORTER_ASSERT(reporter, drawFn); if (!drawFn) { return; } SkBitmap bitmaps[2]; for (int i = 0; i < 2; ++i) { bitmaps[i].allocN32Pixels(WIDTH, HEIGHT); SkCanvas canvas(bitmaps[i]); canvas.drawColor(SK_ColorRED); SkRegion localRegion = clipRegion; SkPaint paint; paint.setAlpha(128); for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) { SkRect layerBounds = SkRect::Make(layerRect); canvas.saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint, flags[j])); if (i) { SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, state); SkRegion::Iterator iter(localRegion); SkTDArray<int32_t> rectCoords; for (; !iter.done(); iter.next()) { const SkIRect& rect = iter.rect(); *rectCoords.append() = rect.fLeft; *rectCoords.append() = rect.fTop; *rectCoords.append() = rect.fRight; *rectCoords.append() = rect.fBottom; } bool success = drawFn(state, clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, clipOps[j], rectCoords.count() / 4, rectCoords.begin()); REPORTER_ASSERT(reporter, success); SkCanvasStateUtils::ReleaseCanvasState(state); } else { complex_clips_draw(&canvas, clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, clipOps[j], localRegion); } canvas.restore(); // translate the canvas and region for the next iteration canvas.translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER)))); localRegion.translate(0, 2*(layerRect.height() + SPACER)); } } // now we memcmp the two bitmaps REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize()); REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(), bitmaps[1].getPixels(), bitmaps[0].getSize())); }
bool addPathToAtlas(GrVertexBatch::Target* target, FlushInfo* flushInfo, GrBatchAtlas* atlas, ShapeData* shapeData, const GrShape& shape, bool antiAlias, uint32_t dimension, SkScalar scale) const { const SkRect& bounds = shape.bounds(); // generate bounding rect for bitmap draw SkRect scaledBounds = bounds; // scale to mip level size scaledBounds.fLeft *= scale; scaledBounds.fTop *= scale; scaledBounds.fRight *= scale; scaledBounds.fBottom *= scale; // move the origin to an integer boundary (gives better results) SkScalar dx = SkScalarFraction(scaledBounds.fLeft); SkScalar dy = SkScalarFraction(scaledBounds.fTop); scaledBounds.offset(-dx, -dy); // get integer boundary SkIRect devPathBounds; scaledBounds.roundOut(&devPathBounds); // pad to allow room for antialiasing const int intPad = SkScalarCeilToInt(kAntiAliasPad); // pre-move origin (after outset, will be 0,0) int width = devPathBounds.width(); int height = devPathBounds.height(); devPathBounds.fLeft = intPad; devPathBounds.fTop = intPad; devPathBounds.fRight = intPad + width; devPathBounds.fBottom = intPad + height; devPathBounds.outset(intPad, intPad); // draw path to bitmap SkMatrix drawMatrix; drawMatrix.setTranslate(-bounds.left(), -bounds.top()); drawMatrix.postScale(scale, scale); drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); // setup bitmap backing SkASSERT(devPathBounds.fLeft == 0); SkASSERT(devPathBounds.fTop == 0); SkAutoPixmapStorage dst; if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) { return false; } sk_bzero(dst.writable_addr(), dst.getSafeSize()); // rasterize path SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setAntiAlias(antiAlias); SkDraw draw; sk_bzero(&draw, sizeof(draw)); SkRasterClip rasterClip; rasterClip.setRect(devPathBounds); draw.fRC = &rasterClip; draw.fMatrix = &drawMatrix; draw.fDst = dst; SkPath path; shape.asPath(&path); draw.drawPathCoverage(path, paint); // generate signed distance field devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); width = devPathBounds.width(); height = devPathBounds.height(); // TODO We should really generate this directly into the plot somehow SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); // Generate signed distance field SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), (const unsigned char*)dst.addr(), dst.width(), dst.height(), dst.rowBytes()); // add to atlas SkIPoint16 atlasLocation; GrBatchAtlas::AtlasID id; if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) { this->flush(target, flushInfo); if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) { return false; } } // add to cache shapeData->fKey.set(shape, dimension); shapeData->fScale = scale; shapeData->fID = id; // change the scaled rect to match the size of the inset distance field scaledBounds.fRight = scaledBounds.fLeft + SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); scaledBounds.fBottom = scaledBounds.fTop + SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); // shift the origin to the correct place relative to the distance field // need to also restore the fractional translation scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); shapeData->fBounds = scaledBounds; // origin we render from is inset from distance field edge atlasLocation.fX += SK_DistanceFieldInset; atlasLocation.fY += SK_DistanceFieldInset; shapeData->fAtlasLocation = atlasLocation; fShapeCache->add(shapeData); fShapeList->addToTail(shapeData); #ifdef DF_PATH_TRACKING ++g_NumCachedPaths; #endif return true; }
static void test_complex_clips(skiatest::Reporter* reporter) { #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG const int WIDTH = 400; const int HEIGHT = 400; const int SPACER = 10; SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4); layerRect.inset(2*SPACER, 2*SPACER); SkIRect clipRect = layerRect; clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER); clipRect.outset(SPACER, SPACER); SkIRect regionBounds = clipRect; regionBounds.offset(clipRect.width() + (2*SPACER), 0); SkIRect regionInterior = regionBounds; regionInterior.inset(SPACER*3, SPACER*3); SkRegion clipRegion; clipRegion.setRect(regionBounds); clipRegion.op(regionInterior, SkRegion::kDifference_Op); const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op, SkRegion::kIntersect_Op, SkRegion::kReplace_Op, }; const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag, SkCanvas::kARGB_ClipLayer_SaveFlag, SkCanvas::kARGB_NoClipLayer_SaveFlag, }; REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags)); const int layerCombinations = sizeof(flags) / sizeof(SkCanvas::SaveFlags); SkBitmap bitmaps[2]; for (int i = 0; i < 2; ++i) { bitmaps[i].allocN32Pixels(WIDTH, HEIGHT); SkCanvas canvas(bitmaps[i]); canvas.drawColor(SK_ColorRED); SkRegion localRegion = clipRegion; for (int j = 0; j < layerCombinations; ++j) { SkRect layerBounds = SkRect::Make(layerRect); canvas.saveLayerAlpha(&layerBounds, 128, flags[j]); SkCanvasState* state = NULL; SkCanvas* tmpCanvas = NULL; if (i) { state = SkCanvasStateUtils::CaptureCanvasState(&canvas); REPORTER_ASSERT(reporter, state); tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state); REPORTER_ASSERT(reporter, tmpCanvas); } else { tmpCanvas = SkRef(&canvas); } tmpCanvas->save(); tmpCanvas->clipRect(SkRect::Make(clipRect), clipOps[j]); tmpCanvas->drawColor(SK_ColorBLUE); tmpCanvas->restore(); tmpCanvas->clipRegion(localRegion, clipOps[j]); tmpCanvas->drawColor(SK_ColorBLUE); tmpCanvas->unref(); SkCanvasStateUtils::ReleaseCanvasState(state); canvas.restore(); // translate the canvas and region for the next iteration canvas.translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER)))); localRegion.translate(0, 2*(layerRect.height() + SPACER)); } } // now we memcmp the two bitmaps REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize()); REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(), bitmaps[1].getPixels(), bitmaps[0].getSize())); #endif }
void draw(SkCanvas* canvas) { SkIRect rect = { 10, 14, 50, 73 }; rect.outset(5, 13); SkDebugf("rect: %d, %d, %d, %d\n", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); }