void SkScan::FillPath(const SkPath& path, const SkRegion& origClip, SkBlitter* blitter) { if (origClip.isEmpty()) { return; } // Our edges are fixed-point, and don't like the bounds of the clip to // exceed that. Here we trim the clip just so we don't overflow later on const SkRegion* clipPtr = &origClip; SkRegion finiteClip; if (clip_to_limit(origClip, &finiteClip)) { if (finiteClip.isEmpty()) { return; } clipPtr = &finiteClip; } // don't reference "origClip" any more, just use clipPtr SkRect bounds = path.getBounds(); bool irPreClipped = false; if (!SkRectPriv::MakeLargeS32().contains(bounds)) { if (!bounds.intersect(SkRectPriv::MakeLargeS32())) { bounds.setEmpty(); } irPreClipped = true; } SkIRect ir = conservative_round_to_int(bounds); if (ir.isEmpty()) { if (path.isInverseFillType()) { blitter->blitRegion(*clipPtr); } return; } SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped); blitter = clipper.getBlitter(); if (blitter) { // we have to keep our calls to blitter in sorted order, so we // must blit the above section first, then the middle, then the bottom. if (path.isInverseFillType()) { sk_blit_above(blitter, ir, *clipPtr); } SkASSERT(clipper.getClipRect() == nullptr || *clipper.getClipRect() == clipPtr->getBounds()); sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom, 0, clipper.getClipRect() == nullptr); if (path.isInverseFillType()) { sk_blit_below(blitter, ir, *clipPtr); } } else { // what does it mean to not have a blitter if path.isInverseFillType??? } }
static void copyToMask(const SkRegion& rgn, SkMask* mask) { mask->fFormat = SkMask::kA8_Format; if (rgn.isEmpty()) { mask->fBounds.setEmpty(); mask->fRowBytes = 0; mask->fImage = nullptr; return; } mask->fBounds = rgn.getBounds(); mask->fRowBytes = mask->fBounds.width(); mask->fImage = SkMask::AllocImage(mask->computeImageSize()); sk_bzero(mask->fImage, mask->computeImageSize()); SkImageInfo info = SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(), kAlpha_8_SkColorType, kPremul_SkAlphaType); SkBitmap bitmap; bitmap.installPixels(info, mask->fImage, mask->fRowBytes); // canvas expects its coordinate system to always be 0,0 in the top/left // so we translate the rgn to match that before drawing into the mask. // SkRegion tmpRgn(rgn); tmpRgn.translate(-rgn.getBounds().fLeft, -rgn.getBounds().fTop); SkCanvas canvas(bitmap); canvas.clipRegion(tmpRgn); canvas.drawColor(SK_ColorBLACK); }
void SkDeferredCanvas::DeferredDevice::contentsCleared() { if (!fRecordingCanvas->isDrawingToLayer()) { fFreshFrame = true; // TODO: find a way to transfer the state stack and layers // to the new recording canvas. For now, purging only works // with an empty stack. if (fRecordingCanvas->getSaveCount() == 0) { // Save state that is trashed by the purge SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter(); SkSafeRef(drawFilter); // So that it survives the purge SkMatrix matrix = fRecordingCanvas->getTotalMatrix(); SkRegion clipRegion = fRecordingCanvas->getTotalClip(); // beginRecording creates a new recording canvas and discards the // old one, hence purging deferred draw ops. fRecordingCanvas = fPicture.beginRecording( fImmediateDevice->width(), fImmediateDevice->height(), 0); // Restore pre-purge state if (!clipRegion.isEmpty()) { fRecordingCanvas->clipRegion(clipRegion, SkRegion::kReplace_Op); } if (!matrix.isIdentity()) { fRecordingCanvas->setMatrix(matrix); } if (drawFilter) { fRecordingCanvas->setDrawFilter(drawFilter)->unref(); } } } }
static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) { // initialize the struct state->clipRectCount = 0; // capture the matrix for (int i = 0; i < 9; i++) { state->matrix[i] = matrix.get(i); } /* * capture the clip * * storage is allocated on the stack for the first 4 rects. This value was * chosen somewhat arbitrarily, but does allow us to represent simple clips * and some more common complex clips (e.g. a clipRect with a sub-rect * clipped out of its interior) without needing to malloc any additional memory. */ SkSWriter32<4*sizeof(ClipRect)> clipWriter; if (!clip.isEmpty()) { // only returns the b/w clip so aa clips fail SkRegion::Iterator clip_iterator(clip); for (; !clip_iterator.done(); clip_iterator.next()) { // this assumes the SkIRect is stored in l,t,r,b ordering which // matches the ordering of our ClipRect struct clipWriter.writeIRect(clip_iterator.rect()); state->clipRectCount++; } } // allocate memory for the clip then and copy them to the struct state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten()); clipWriter.flatten(state->clipRects); }
void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval, bool showVisualIndicator, bool isPictureAfterFirstLayout) { if (!layer || isPictureAfterFirstLayout) { // TODO: move this into TreeManager m_zoomManager.swapPages(); // reset zoom state m_tiledPageA->discardTextures(); m_tiledPageB->discardTextures(); m_layersRenderingMode = kAllTextures; } // copy content from old composited root to new if (layer) { XLOG("new base layer %p, (inval region empty %d) with child %p", layer, inval.isEmpty(), layer->getChild(0)); layer->setState(this); layer->markAsDirty(inval); // TODO: set in webview.cpp } m_treeManager.updateWithTree(layer, isPictureAfterFirstLayout); m_glExtras.setDrawExtra(0); #ifdef MEASURES_PERF if (m_measurePerfs && !showVisualIndicator) dumpMeasures(); m_measurePerfs = showVisualIndicator; #endif TilesManager::instance()->setShowVisualIndicator(showVisualIndicator); }
void SkDeferredCanvas::DeferredDevice::purgePending() { // TODO: find a way to transfer the state stack and layers // to the new recording canvas. For now, purge only works // with an empty stack. if (fRecordingCanvas->getSaveCount() > 1) return; // Save state that is trashed by the purge SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter(); SkSafeRef(drawFilter); // So that it survives the purge SkMatrix matrix = fRecordingCanvas->getTotalMatrix(); SkRegion clipRegion = fRecordingCanvas->getTotalClip(); // beginRecording creates a new recording canvas and discards the old one, // hence purging deferred draw ops. fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(), fImmediateDevice->height(), SkPicture::kUsePathBoundsForClip_RecordingFlag); // Restore pre-purge state if (!clipRegion.isEmpty()) { fRecordingCanvas->clipRegion(clipRegion, SkRegion::kReplace_Op); } if (!matrix.isIdentity()) { fRecordingCanvas->setMatrix(matrix); } if (drawFilter) { fRecordingCanvas->setDrawFilter(drawFilter)->unref(); } }
bool SkAndroidFrameworkUtils::clipWithStencil(SkCanvas* canvas) { SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); if (clipRegion.isEmpty()) { return false; } SkBaseDevice* device = canvas->getDevice(); if (!device) { return false; } GrRenderTargetContext* rtc = device->accessRenderTargetContext(); if (!rtc) { return false; } GrPaint grPaint; grPaint.setXPFactory(GrDisableColorXPFactory::Get()); GrNoClip noClip; static constexpr GrUserStencilSettings kDrawToStencil( GrUserStencilSettings::StaticInit< 0x1, GrUserStencilTest::kAlways, 0x1, GrUserStencilOp::kReplace, GrUserStencilOp::kReplace, 0x1>() ); rtc->drawRegion(noClip, std::move(grPaint), GrAA::kNo, SkMatrix::I(), clipRegion, GrStyle::SimpleFill(), &kDrawToStencil); return true; }
void Tile::markAsDirty(const SkRegion& dirtyArea) { if (dirtyArea.isEmpty()) return; android::AutoMutex lock(m_atomicSync); m_dirtyArea.op(dirtyArea, SkRegion::kUnion_Op); // Check if we actually intersect with the area bool intersect = false; SkRegion::Iterator cliperator(dirtyArea); SkRect realTileRect; SkRect dirtyRect; while (!cliperator.done()) { dirtyRect.set(cliperator.rect()); if (intersectWithRect(m_x, m_y, TilesManager::tileWidth(), TilesManager::tileHeight(), m_scale, dirtyRect, realTileRect)) { intersect = true; break; } cliperator.next(); } if (!intersect) return; markAsDirtyInternal(); }
void PicturePile::drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, int index) { // TODO: Add some debug visualizations of this if (index < 0 || clipRegion.isEmpty()) return; PictureContainer& pc = m_pile[index]; IntRect intersection = clipRegion.getBounds(); intersection.intersect(pc.area); if (pc.picture && !intersection.isEmpty()) { // SAMSUNG CHANGE ++ : Animation GIF frame remain in Base Picture // As the previous logic : Base Picture draws picture already drawn by other pile due to only using RECT. // It uses PATH and RECT, both now. SkPath pathClip; clipRegion.getBoundaryPath(&pathClip); // SAMSUNG CHANGE -- clipRegion.op(intersection, SkRegion::kDifference_Op); drawWithClipRecursive(canvas, clipRegion, index - 1); int saved = canvas->save(); canvas->clipRect(intersection); canvas->clipPath(pathClip); // SAMSUNG CHANGE : Animation GIF frame remain in Base Picture canvas->translate(pc.area.x(), pc.area.y()); canvas->drawPicture(*pc.picture); canvas->restoreToCount(saved); } else drawWithClipRecursive(canvas, clipRegion, index - 1); }
void RegionLayerDrawExtra::drawGL(GLExtras* glExtras, const LayerAndroid* layer) { SkRegion* region = getHighlightRegionsForLayer(layer); if (!region || region->isEmpty()) return; const TransformationMatrix* transform = layer ? layer->drawTransform() : 0; glExtras->drawRegion(*region, true, false, transform, m_highlightColor); }
void RegionLayerDrawExtra::draw(SkCanvas* canvas, LayerAndroid* layer) { SkRegion* region = getHighlightRegionsForLayer(layer); if (!region || region->isEmpty()) return; SkRegion::Iterator rgnIter(*region); SkPaint paint; paint.setColor(m_highlightColor.rgb()); while (!rgnIter.done()) { const SkIRect& rect = rgnIter.rect(); canvas->drawIRect(rect, paint); rgnIter.next(); } }
void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { SkRegion rgn; this->build_rgn(&rgn, op); { SkRegion tmp, tmp2(rgn); tmp = tmp2; tmp.translate(5, -3); { char buffer[1000]; size_t size = tmp.flatten(NULL); SkASSERT(size <= sizeof(buffer)); size_t size2 = tmp.flatten(buffer); SkASSERT(size == size2); SkRegion tmp3; size2 = tmp3.unflatten(buffer); SkASSERT(size == size2); SkASSERT(tmp3 == tmp); } rgn.translate(20, 30, &tmp); SkASSERT(rgn.isEmpty() || tmp != rgn); tmp.translate(-20, -30); SkASSERT(tmp == rgn); } this->drawOrig(canvas, true); SkPaint paint; paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24)); paint_rgn(canvas, rgn, paint); paint.setStyle(SkPaint::kStroke_Style); paint.setColor(color); paint_rgn(canvas, rgn, paint); }
void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip, const SkClipStack& clipStack, SkRegion* updateClip) { int x = fDevice->getOrigin().x(); int y = fDevice->getOrigin().y(); int width = fDevice->width(); int height = fDevice->height(); if ((x | y) == 0) { fMatrix = &totalMatrix; fClip = totalClip; } else { fMatrixStorage = totalMatrix; fMatrixStorage.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); fMatrix = &fMatrixStorage; totalClip.translate(-x, -y, &fClip); } fClip.op(0, 0, width, height, SkRegion::kIntersect_Op); // intersect clip, but don't translate it (yet) if (updateClip) { updateClip->op(x, y, x + width, y + height, SkRegion::kDifference_Op); } fDevice->setMatrixClip(*fMatrix, fClip, clipStack); #ifdef SK_DEBUG if (!fClip.isEmpty()) { SkIRect deviceR; deviceR.set(0, 0, width, height); SkASSERT(deviceR.contains(fClip.getBounds())); } #endif // default is to assume no external matrix fMVMatrix = NULL; fExtMatrix = NULL; }
static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) { SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds); bool result = !region->isEmpty(); return boolTojboolean(result); }
void TileGrid::markAsDirty(const SkRegion& invalRegion) { ALOGV("TG %p markAsDirty, current region empty %d, new empty %d", this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty()); m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op); }
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(); } } }