bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) { // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(path, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip; clip.setPath(path, base, doAA); return this->op(clip, op); } } else { base.setRect(0, 0, size.width(), size.height()); if (SkRegion::kReplace_Op == op) { return this->setPath(path, base, doAA); } else { SkRasterClip clip; clip.setPath(path, base, doAA); return this->op(clip, op); } } }
bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& bounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, path.isInverseFillType())) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = bounds; break; case kContinue_MutateResult: { SkRect bounds = path.getBounds(); matrix.mapRect(&bounds); ir = bounds.roundOut(); break; } } return this->op(ir, op); } // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; SkPath devPath; if (matrix.isIdentity()) { devPath = path; } else { path.transform(matrix, &devPath); devPath.setIsVolatile(true); } if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(devPath, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip(fForceConservativeRects); clip.setPath(devPath, base, doAA); return this->op(clip, op); } } else { base.setRect(bounds); if (SkRegion::kReplace_Op == op) { return this->setPath(devPath, base, doAA); } else { SkRasterClip clip(fForceConservativeRects); clip.setPath(devPath, base, doAA); return this->op(clip, op); } } }
bool PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, const Point &aPoint, const Matrix &aTransform) const { Matrix inverse = aTransform; inverse.Invert(); Point transformed = inverse * aPoint; SkPaint paint; StrokeOptionsToPaint(paint, aStrokeOptions); SkPath strokePath; paint.getFillPath(mPath, &strokePath); Rect bounds = aTransform.TransformBounds(SkRectToRect(strokePath.getBounds())); if (aPoint.x < bounds.x || aPoint.y < bounds.y || aPoint.x > bounds.XMost() || aPoint.y > bounds.YMost()) { return false; } SkRegion pointRect; pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)), int32_t(SkFloatToScalar(transformed.y - 1.f)), int32_t(SkFloatToScalar(transformed.x + 1.f)), int32_t(SkFloatToScalar(transformed.y + 1.f))); SkRegion pathRegion; return pathRegion.setPath(strokePath, pointRect); }
static BaseLayerAndroid* nativeDeserializeViewState(JNIEnv* env, jobject, jint version, jobject jstream, jbyteArray jstorage) { SkStream* stream = CreateJavaInputStreamAdaptor(env, jstream, jstorage); if (!stream) return 0; Color color = stream->readU32(); SkPicture* picture = new SkPicture(stream); PictureLayerContent* content = new PictureLayerContent(picture); BaseLayerAndroid* layer = new BaseLayerAndroid(content); layer->setBackgroundColor(color); SkRegion dirtyRegion; dirtyRegion.setRect(0, 0, content->width(), content->height()); layer->markAsDirty(dirtyRegion); SkSafeUnref(content); SkSafeUnref(picture); int childCount = stream->readS32(); for (int i = 0; i < childCount; i++) { LayerAndroid* childLayer = deserializeLayer(version, stream); if (childLayer) layer->addChild(childLayer); } delete stream; return layer; }
void SkPageFlipper::inval(const SkRegion& rgn) { SkRegion r; r.setRect(0, 0, fWidth, fHeight); if (r.op(rgn, SkRegion::kIntersect_Op)) { fDirty1->op(r, SkRegion::kUnion_Op); } }
bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) { // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, path.isInverseFillType())) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = SkIRect::MakeSize(size); break; case kContinue_MutateResult: ir = path.getBounds().roundOut(); break; } return this->op(ir, op); } if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(path, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip(fForceConservativeRects); clip.setPath(path, base, doAA); return this->op(clip, op); } } else { base.setRect(0, 0, size.width(), size.height()); if (SkRegion::kReplace_Op == op) { return this->setPath(path, base, doAA); } else { SkRasterClip clip(fForceConservativeRects); clip.setPath(path, base, doAA); return this->op(clip, op); } } }
bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); SkIRect bounds(devBounds); this->applyClipRestriction(op, &bounds); // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; SkPath devPath; if (matrix.isIdentity()) { devPath = path; } else { path.transform(matrix, &devPath); devPath.setIsVolatile(true); } if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(devPath, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip; clip.setPath(devPath, base, doAA); return this->op(clip, op); } } else { base.setRect(bounds); if (SkRegion::kReplace_Op == op) { return this->setPath(devPath, base, doAA); } else { SkRasterClip clip; clip.setPath(devPath, base, doAA); return this->op(clip, op); } } }
void draw(SkCanvas* canvas) { SkRegion region; SkRegion::Iterator iter(region); auto r1 = iter.rect(); SkDebugf("rect={%d,%d,%d,%d}\n", r1.fLeft, r1.fTop, r1.fRight, r1.fBottom); region.setRect({1, 2, 3, 4}); iter.rewind(); auto r2 = iter.rect(); SkDebugf("rect={%d,%d,%d,%d}\n", r2.fLeft, r2.fTop, r2.fRight, r2.fBottom); }
bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, const SkIRect* clipBounds, SkMask* mask, SkMask::CreateMode mode) { if (fLayers.empty()) return false; if (SkMask::kJustRenderImage_CreateMode != mode) { if (!compute_bounds(fLayers, path, matrix, clipBounds, &mask->fBounds)) return false; } if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { mask->fFormat = SkMask::kA8_Format; mask->fRowBytes = SkToU16(mask->fBounds.width()); mask->fImage = SkMask::AllocImage(mask->computeImageSize()); memset(mask->fImage, 0, mask->computeImageSize()); } if (SkMask::kJustComputeBounds_CreateMode != mode) { SkBitmap device; SkDraw draw; SkMatrix translatedMatrix; // this translates us to our local pixels SkMatrix drawMatrix; // this translates the path by each layer's offset SkRegion rectClip; rectClip.setRect(0, 0, mask->fBounds.width(), mask->fBounds.height()); translatedMatrix = matrix; translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft), -SkIntToScalar(mask->fBounds.fTop)); device.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), mask->fBounds.height(), mask->fRowBytes); device.setPixels(mask->fImage); draw.fBitmap = &device; draw.fMatrix = &drawMatrix; draw.fClip = &rectClip; // we set the matrixproc in the loop, as the matrix changes each time (potentially) draw.fBounder = NULL; SkDeque::Iter iter(fLayers); SkLayerRasterizer_Rec* rec; while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { drawMatrix = translatedMatrix; drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY); draw.drawPath(path, rec->fPaint); } } return true; }
RegionContainBench(Proc proc, const char name[]) { fProc = proc; fName.printf("region_contains_%s", name); SkRandom rand; for (int i = 0; i < COUNT; i++) { fA.op(randrect(rand, i), SkRegion::kXOR_Op); } fB.setRect(0, 0, H, W); }
void GLExtras::drawCursorRings() { SkRegion region; for (size_t i = 0; i < m_ring->rings().size(); i++) { IntRect rect = m_ring->rings().at(i); if (i == 0) region.setRect(rect); else region.op(rect, SkRegion::kUnion_Op); } drawRegion(region, m_ring->m_isPressed, !m_ring->m_isButton, false); }
AAClipBuilderBench(void* param, bool doPath, bool doAA) : INHERITED(param) { fDoPath = doPath; fDoAA = doAA; fName.printf("aaclip_build_%s_%s", doPath ? "path" : "rect", doAA ? "AA" : "BW"); fRegion.setRect(0, 0, 640, 480); fRect.set(fRegion.getBounds()); fRect.inset(SK_Scalar1/4, SK_Scalar1/4); fPath.addRoundRect(fRect, SkIntToScalar(20), SkIntToScalar(20)); }
bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) { SkRegion rgn; SkRegion clip; SkPath::FillType originalFillType = originalPath->getFillType(); const SkPath* path = originalPath; SkPath scaledPath; int scale = 1; SkRect bounds; // FIXME: This #ifdef can go away once we're firmly using the new Skia. // During the transition, this makes the code compatible with both versions. #ifdef SK_USE_OLD_255_TO_256 bounds = originalPath->getBounds(); #else originalPath->computeBounds(&bounds, SkPath::kFast_BoundsType); #endif // We can immediately return false if the point is outside the bounding rect if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) return false; originalPath->setFillType(ft); // Skia has trouble with coordinates close to the max signed 16-bit values // If we have those, we need to scale. // // TODO: remove this code once Skia is patched to work properly with large // values const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); if (biggestCoord > kMaxCoordinate) { scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); SkMatrix m; m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); originalPath->transform(m, &scaledPath); path = &scaledPath; } int x = static_cast<int>(floorf(point.x() / scale)); int y = static_cast<int>(floorf(point.y() / scale)); clip.setRect(x, y, x + 1, y + 1); bool contains = rgn.setPath(*path, clip); originalPath->setFillType(originalFillType); return contains; }
RegionContainBench(void* param, Proc proc, const char name[]) : INHERITED(param) { fProc = proc; fName.printf("region_contains_%s", name); SkRandom rand; for (int i = 0; i < COUNT; i++) { fA.op(randrect(rand, i), SkRegion::kXOR_Op); } fB.setRect(0, 0, H, W); fIsRendering = false; }
bool SkRasterClip::setPath(const SkPath& path, const SkRasterClip& clip, bool doAA) { if (clip.isBW()) { return this->setPath(path, clip.bwRgn(), doAA); } else { SkRegion tmp; tmp.setRect(clip.getBounds()); if (!this->setPath(path, clip, doAA)) { return false; } return this->op(clip, SkRegion::kIntersect_Op); } }
SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { SkCanvas* canvas = this->INHERITED::setupCanvas(width, height); SkASSERT(fPicture); // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This // is mostly important for tiles on the right and bottom edges as they may go over this area and // the picture may have some commands that draw outside of this area and so should not actually // be written. // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set // by INHERITED::setupCanvas. SkRegion clipRegion; clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight()); canvas->clipRegion(clipRegion); return canvas; }
bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) { SkRegion rgn; SkRegion clip; SkPath::FillType originalFillType = originalPath->getFillType(); const SkPath* path = originalPath; SkPath scaledPath; int scale = 1; SkRect bounds = originalPath->getBounds(); // We can immediately return false if the point is outside the bounding // rect. We don't use bounds.contains() here, since it would exclude // points on the right and bottom edges of the bounding rect, and we want // to include them. SkScalar fX = SkFloatToScalar(point.x()); SkScalar fY = SkFloatToScalar(point.y()); if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) return false; originalPath->setFillType(ft); // Skia has trouble with coordinates close to the max signed 16-bit values // If we have those, we need to scale. // // TODO: remove this code once Skia is patched to work properly with large // values const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); if (biggestCoord > kMaxCoordinate) { scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); SkMatrix m; m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); originalPath->transform(m, &scaledPath); path = &scaledPath; } int x = static_cast<int>(floorf(point.x() / scale)); int y = static_cast<int>(floorf(point.y() / scale)); clip.setRect(x - 1, y - 1, x + 1, y + 1); bool contains = rgn.setPath(*path, clip); originalPath->setFillType(originalFillType); return contains; }
void mbe_scissoring(mbe_t *canvas, int n_areas, area_t **areas) { SkRegion region; area_t *area; co_aix right, bottom; int i; for(i = 0; i < n_areas; i++) { area = areas[i]; right = area->x + area->w; bottom = area->y + area->h; region.setRect(area->x, area->y, right, bottom); canvas->canvas->clipRegion(region, SkRegion::kIntersect_Op); } }
bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, SkPath::FillType ft) { SkRect bounds = originalPath.getBounds(); // We can immediately return false if the point is outside the bounding // rect. We don't use bounds.contains() here, since it would exclude // points on the right and bottom edges of the bounding rect, and we want // to include them. SkScalar fX = SkFloatToScalar(point.x()); SkScalar fY = SkFloatToScalar(point.y()); if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) return false; // Scale the path to a large size before hit testing for two reasons: // 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down. // TODO: when Skia is patched to work properly with large values, this will not be necessary. // 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy. // 3) Scale the x/y axis separately so an extreme large/small scale factor on one axis won't // ruin the resolution of the other axis. SkScalar biggestCoordX = std::max(bounds.fRight, -bounds.fLeft); SkScalar biggestCoordY = std::max(bounds.fBottom, -bounds.fTop); if (SkScalarNearlyZero(biggestCoordX) || SkScalarNearlyZero(biggestCoordY)) return false; biggestCoordX = std::max(biggestCoordX, std::fabs(fX) + 1); biggestCoordY = std::max(biggestCoordY, std::fabs(fY) + 1); const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15); SkScalar scaleX = kMaxCoordinate / biggestCoordX; SkScalar scaleY = kMaxCoordinate / biggestCoordY; SkRegion rgn; SkRegion clip; SkMatrix m; SkPath scaledPath(originalPath); scaledPath.setFillType(ft); m.setScale(scaleX, scaleY); scaledPath.transform(m, 0); int x = static_cast<int>(floorf(0.5f + point.x() * scaleX)); int y = static_cast<int>(floorf(0.5f + point.y() * scaleY)); clip.setRect(x - 1, y - 1, x + 1, y + 1); return rgn.setPath(scaledPath, clip); }
bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) { SkRect bounds = originalPath->getBounds(); // We can immediately return false if the point is outside the bounding // rect. We don't use bounds.contains() here, since it would exclude // points on the right and bottom edges of the bounding rect, and we want // to include them. SkScalar fX = SkFloatToScalar(point.x()); SkScalar fY = SkFloatToScalar(point.y()); if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) return false; // Scale the path to a large size before hit testing for two reasons: // 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down. // TODO: when Skia is patched to work properly with large values, this will not be necessary. // 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy. SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); if (SkScalarNearlyZero(biggestCoord)) return false; biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1); const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15); SkScalar scale = SkScalarDiv(kMaxCoordinate, biggestCoord); SkRegion rgn; SkRegion clip; SkMatrix m; SkPath scaledPath; SkPath::FillType originalFillType = originalPath->getFillType(); originalPath->setFillType(ft); m.setScale(scale, scale); originalPath->transform(m, &scaledPath); int x = static_cast<int>(floorf(0.5f + point.x() * scale)); int y = static_cast<int>(floorf(0.5f + point.y() * scale)); clip.setRect(x - 1, y - 1, x + 1, y + 1); bool contains = rgn.setPath(scaledPath, clip); originalPath->setFillType(originalFillType); return contains; }
void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect) { // rect is in the render object coordinates if (!m_image && !drawsContent()) { LOG("(%x) setNeedsDisplay(%.2f,%.2f,%.2f,%.2f) doesn't have content, bypass...", this, rect.x(), rect.y(), rect.width(), rect.height()); return; } SkRegion region; region.setRect(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); m_dirtyRegion.op(region, SkRegion::kUnion_Op); m_needsRepaint = true; askForSync(); }
virtual void recordCanvas(SkCanvas* canvas) { const SkPoint translateDelta = getTranslateDelta(); for (int i = 0; i < M; i++) { SkColor color = SK_ColorYELLOW + (i % 255); SkIRect rect = SkIRect::MakeWH(i,i); canvas->save(); // set the clip to the given region SkRegion region; region.setRect(rect); canvas->clipRegion(region); // fill the clip with a color SkPaint paint; paint.setColor(color); canvas->drawPaint(paint); // set a matrix on the canvas SkMatrix matrix; matrix.setRotate(SkIntToScalar(i % 360)); canvas->setMatrix(matrix); // create a simple bitmap SkBitmap bitmap; bitmap.setConfig(SkBitmap::kRGB_565_Config, 10, 10); bitmap.allocPixels(); // draw a single color into the bitmap SkCanvas bitmapCanvas(bitmap); bitmapCanvas.drawColor(SkColorSetA(color, i % 255)); // draw the bitmap onto the canvas canvas->drawBitmapMatrix(bitmap, matrix); canvas->restore(); canvas->translate(translateDelta.fX, translateDelta.fY); } }
bool PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const { Matrix inverse = aTransform; inverse.Invert(); Point transformed = inverse * aPoint; Rect bounds = GetBounds(aTransform); if (aPoint.x < bounds.x || aPoint.y < bounds.y || aPoint.x > bounds.XMost() || aPoint.y > bounds.YMost()) { return false; } SkRegion pointRect; pointRect.setRect(SkFloatToScalar(transformed.x - 1), SkFloatToScalar(transformed.y - 1), SkFloatToScalar(transformed.x + 1), SkFloatToScalar(transformed.y + 1)); SkRegion pathRegion; return pathRegion.setPath(mPath, pointRect); }
static SkRegion TestRegion() { SkRegion region; SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1); region.setRect(rect); return region; }
static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) { SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); bool result = dst->setRect(left, top, right, bottom); return boolTojboolean(result); }
void SkScalerContext::getImage(const SkGlyph& origGlyph) { const SkGlyph* glyph = &origGlyph; SkGlyph tmpGlyph; if (fMaskFilter) { // restore the prefilter bounds tmpGlyph.init(origGlyph.fID); // need the original bounds, sans our maskfilter SkMaskFilter* mf = fMaskFilter; fMaskFilter = NULL; // temp disable this->getMetrics(&tmpGlyph); fMaskFilter = mf; // restore tmpGlyph.fImage = origGlyph.fImage; // we need the prefilter bounds to be <= filter bounds SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); glyph = &tmpGlyph; } if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { SkPath devPath, fillPath; SkMatrix fillToDevMatrix; this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); if (fRasterizer) { SkMask mask; glyph->toMask(&mask); mask.fFormat = SkMask::kA8_Format; sk_bzero(glyph->fImage, mask.computeImageSize()); if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, fMaskFilter, &mask, SkMask::kJustRenderImage_CreateMode)) { return; } } else { SkBitmap bm; SkBitmap::Config config; SkMatrix matrix; SkRegion clip; SkPaint paint; SkDraw draw; if (SkMask::kA8_Format == fRec.fMaskFormat) { config = SkBitmap::kA8_Config; paint.setAntiAlias(true); } else { SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat); config = SkBitmap::kA1_Config; paint.setAntiAlias(false); } clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); matrix.setTranslate(-SkIntToScalar(glyph->fLeft), -SkIntToScalar(glyph->fTop)); bm.setConfig(config, glyph->fWidth, glyph->fHeight, glyph->rowBytes()); bm.setPixels(glyph->fImage); sk_bzero(glyph->fImage, bm.height() * bm.rowBytes()); draw.fClip = &clip; draw.fMatrix = &matrix; draw.fBitmap = &bm; draw.fBounder = NULL; draw.drawPath(devPath, paint); } } else { this->getGlyphContext(*glyph)->generateImage(*glyph); } if (fMaskFilter) { SkMask srcM, dstM; SkMatrix matrix; // the src glyph image shouldn't be 3D SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); glyph->toMask(&srcM); fRec.getMatrixFrom2x2(&matrix); if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) { int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); int dstRB = origGlyph.rowBytes(); int srcRB = dstM.fRowBytes; const uint8_t* src = (const uint8_t*)dstM.fImage; uint8_t* dst = (uint8_t*)origGlyph.fImage; if (SkMask::k3D_Format == dstM.fFormat) { // we have to copy 3 times as much height *= 3; } // clean out our glyph, since it may be larger than dstM //sk_bzero(dst, height * dstRB); while (--height >= 0) { memcpy(dst, src, width); src += srcRB; dst += dstRB; } SkMask::FreeImage(dstM.fImage); } } // check to see if we should filter the alpha channel if (NULL == fMaskFilter && fRec.fMaskFormat != SkMask::kBW_Format && fRec.fMaskFormat != SkMask::kLCD16_Format && fRec.fMaskFormat != SkMask::kLCD32_Format && (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0) { const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable; if (NULL != table) { uint8_t* dst = (uint8_t*)origGlyph.fImage; unsigned rowBytes = origGlyph.rowBytes(); for (int y = origGlyph.fHeight - 1; y >= 0; --y) { for (int x = origGlyph.fWidth - 1; x >= 0; --x) { dst[x] = table[dst[x]]; } dst += rowBytes; } } } }
bool Surface::tryUpdateSurface(Surface* oldSurface) { if (!needsTexture() || !oldSurface->needsTexture()) return false; // merge surfaces based on first layer ID if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId()) return false; m_surfaceBacking = oldSurface->m_surfaceBacking; SkSafeRef(m_surfaceBacking); ALOGV("%p taking old SurfBack %p from surface %p, nt %d", this, m_surfaceBacking, oldSurface, oldSurface->needsTexture()); if (!m_surfaceBacking) { // no SurfBack to inval, so don't worry about it. return true; } SkRegion invalRegion; bool fullInval = false; if (singleLayer() && oldSurface->singleLayer()) { // both are single matching layers, simply apply inval SkRegion* layerInval = getFirstLayer()->getInvalRegion(); invalRegion = *layerInval; if (isBase()) { // the base layer paints outside it's content area to ensure the // viewport is convered, so fully invalidate all tiles if its size // changes to ensure no stale content remains LayerContent* newContent = getFirstLayer()->content(); LayerContent* oldContent = oldSurface->getFirstLayer()->content(); fullInval = newContent->width() != oldContent->width() || newContent->height() != oldContent->height(); } } else { fullInval = m_layers.size() != oldSurface->m_layers.size(); if (!fullInval) { for (unsigned int i = 0; i < m_layers.size(); i++) { if ((m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId()) || (m_layers[i]->fullContentAreaMapped() != oldSurface->m_layers[i]->fullContentAreaMapped())) { // layer list has changed, fully invalidate // TODO: partially invalidate based on layer size/position fullInval = true; break; } else if (!m_layers[i]->getInvalRegion()->isEmpty()) { // merge layer inval - translate the layer's inval region into surface coordinates // TODO: handle scale/3d transform mapping FloatRect layerPos = m_layers[i]->fullContentAreaMapped(); m_layers[i]->getInvalRegion()->translate(layerPos.x(), layerPos.y()); invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op); } } } } if (fullInval) invalRegion.setRect(-1e8, -1e8, 2e8, 2e8); m_surfaceBacking->markAsDirty(invalRegion); return true; }
void GLExtras::drawRegion(const SkRegion& region, bool fill, bool drawBorder, const TransformationMatrix* drawMat, Color color) { if (region.isEmpty()) return; if (fill) { SkRegion::Iterator rgnIter(region); while (!rgnIter.done()) { const SkIRect& ir = rgnIter.rect(); SkRect r; r.set(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); drawRing(r, color, drawMat); rgnIter.next(); } } if (fill && !drawBorder) return; SkPath path; if (!region.getBoundaryPath(&path)) return; SkPath::Iter iter(path, true); SkPath::Verb verb; SkPoint pts[4]; SkRegion clip; SkIRect startRect; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { if (verb == SkPath::kLine_Verb) { SkRect r; r.set(pts, 2); SkIRect line; int borderWidth = RING_BORDER_WIDTH; if (!fill) borderWidth *= 2; line.fLeft = r.fLeft - borderWidth; line.fRight = r.fRight + borderWidth; line.fTop = r.fTop - borderWidth; line.fBottom = r.fBottom + borderWidth; if (clip.intersects(line)) { clip.op(line, SkRegion::kReverseDifference_Op); if (clip.isEmpty()) continue; // Nothing to draw, continue line = clip.getBounds(); if (SkIRect::Intersects(startRect, line)) { clip.op(startRect, SkRegion::kDifference_Op); if (clip.isEmpty()) continue; // Nothing to draw, continue line = clip.getBounds(); } } else { clip.setRect(line); } r.set(line.fLeft, line.fTop, line.fRight, line.fBottom); drawRing(r, color, drawMat); if (startRect.isEmpty()) { startRect.set(line.fLeft, line.fTop, line.fRight, line.fBottom); } } if (verb == SkPath::kMove_Verb) { startRect.setEmpty(); } } }
bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) { SkRegion tmp; tmp.setRect(clip); return this->setPath(path, tmp, doAA); }
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())); }