void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); SkRect dirty; mDamageAccumulator.finish(&dirty); // TODO: Re-enable after figuring out cause of b/22592975 // if (dirty.isEmpty() && Properties::skipEmptyFrames) { // mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); // return; // } mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } if (!dirty.isEmpty()) { mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { mCanvas->prepare(mOpaque); } Rect outBounds; mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); bool drew = mCanvas->finish(); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); if (drew) { swapBuffers(dirty, width, height); } // TODO: Use a fence for real completion? mCurrentFrameInfo->markFrameCompleted(); mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); }
void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { //ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", // SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } status_t status; if (!dirty.isEmpty()) { status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } Rect outBounds; status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); mCanvas->finish(); profiler().markPlaybackEnd(); if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } else { mEglManager.cancelFrame(); } profiler().finishFrame(); }
void onDraw(SkCanvas* canvas) override { SkPaint blurPaint; SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(5.0f, 5.0f)); blurPaint.setImageFilter(blur); const SkScalar tile_size = SkIntToScalar(128); SkRect bounds; if (!canvas->getClipBounds(&bounds)) { bounds.setEmpty(); } int ts = SkScalarCeilToInt(tile_size); SkImageInfo info = SkImageInfo::MakeN32Premul(ts, ts); SkAutoTUnref<SkSurface> tileSurface(canvas->newSurface(info)); if (!tileSurface.get()) { tileSurface.reset(SkSurface::NewRaster(info)); } SkCanvas* tileCanvas = tileSurface->getCanvas(); for (SkScalar y = bounds.top(); y < bounds.bottom(); y += tile_size) { for (SkScalar x = bounds.left(); x < bounds.right(); x += tile_size) { tileCanvas->save(); tileCanvas->clear(0); tileCanvas->translate(-x, -y); SkRect rect = SkRect::MakeWH(WIDTH, HEIGHT); tileCanvas->saveLayer(&rect, &blurPaint); SkRRect rrect = SkRRect::MakeRectXY(rect.makeInset(20, 20), 25, 25); tileCanvas->clipRRect(rrect, SkRegion::kDifference_Op, true); SkPaint paint; tileCanvas->drawRect(rect, paint); tileCanvas->restore(); tileCanvas->restore(); canvas->drawImage(tileSurface->makeImageSnapshot().get(), x, y); } } }
SkRect* operator()() const { SkRect* rect = SkNEW(SkRect); if (!fTypeface.onComputeBounds(rect)) { rect->setEmpty(); } return rect; }
SkRect FindCanvas::addMatchPos(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar xPos[], SkScalar /* y */) { SkRect r; r.setEmpty(); const SkPoint* temp = reinterpret_cast<const SkPoint*> (xPos); const SkPoint* points = &temp[index]; int countInBytes = count * sizeof(uint16_t); SkPaint::FontMetrics fontMetrics; paint.getFontMetrics(&fontMetrics); // Need to check each character individually, since the heights may be // different. for (int j = 0; j < count; j++) { SkRect bounds; bounds.fLeft = points[j].fX; bounds.fRight = bounds.fLeft + paint.measureText(&glyphs[j], sizeof(uint16_t), 0); SkScalar baseline = points[j].fY; bounds.fTop = baseline + fontMetrics.fAscent; bounds.fBottom = baseline + fontMetrics.fDescent; /* Accumulate and then add the resulting rect to mMatches */ r.join(bounds); } SkMatrix matrix = getTotalMatrix(); matrix.mapRect(&r); SkCanvas* canvas = getWorkingCanvas(); int saveCount = canvas->save(); canvas->concat(matrix); canvas->drawPosText(glyphs, countInBytes, points, paint); canvas->restoreToCount(saveCount); return r; }
virtual void onDraw(SkCanvas* canvas) { SkPaint paint; paint.setImageFilter(SkBlurImageFilter::Create(fSigmaX, fSigmaY))->unref(); const SkScalar tile_size = SkIntToScalar(128); SkRect bounds; if (!canvas->getClipBounds(&bounds)) { bounds.setEmpty(); } for (SkScalar y = bounds.top(); y < bounds.bottom(); y += tile_size) { for (SkScalar x = bounds.left(); x < bounds.right(); x += tile_size) { canvas->save(); canvas->clipRect(SkRect::MakeXYWH(x, y, tile_size, tile_size)); canvas->saveLayer(NULL, &paint); const char* str[] = { "The quick", "brown fox", "jumped over", "the lazy dog.", }; SkPaint textPaint; textPaint.setAntiAlias(true); textPaint.setTextSize(SkIntToScalar(100)); int posY = 0; for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) { posY += 100; canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0), SkIntToScalar(posY), textPaint); } canvas->restore(); canvas->restore(); } } }
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* draw_proc(void* context) { const int OVALW = 32; const int OVALH = 32; const SkBitmap* bm = static_cast<const SkBitmap*>(context); SkFlipPixelRef* ref = static_cast<SkFlipPixelRef*>(bm->pixelRef()); const int DSCALE = 1; SkScalar dx = SkIntToScalar(7) / DSCALE; SkScalar dy = SkIntToScalar(5) / DSCALE; SkScalar x = 0; SkScalar y = 0; SkPaint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorRED); SkRect oval; oval.setEmpty(); while (!gDone) { ref->inval(oval, true); oval.set(x, y, x + SkIntToScalar(OVALW), y + SkIntToScalar(OVALH)); ref->inval(oval, true); SkAutoFlipUpdate update(ref); if (!update.dirty().isEmpty()) { // this must be local to the loop, since it needs to forget the pixels // its writing to after each iteration, since we do the swap SkCanvas canvas(update.bitmap()); // SkDebugf("----- dirty [%d %d %d %d]\n", dirty.getBounds().fLeft, dirty.getBounds().fTop, dirty.getBounds().width(), dirty.getBounds().height()); canvas.clipRegion(update.dirty()); canvas.drawColor(0, SkXfermode::kClear_Mode); canvas.drawOval(oval, paint); } bounce(&x, &dx, WIDTH-OVALW); bounce(&y, &dy, HEIGHT-OVALH); #if 1 for (int i = 0; i < 1000; i++) { for (int j = 0; j < 10000; j++) { SkFixedMul(j, 10); } } #endif } return NULL; }
static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) { SkRect r; SkIRect ir; bool result = get_canvas(canvasHandle)->getClipBounds(&r); if (!result) { r.setEmpty(); } r.round(&ir); (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); return result ? JNI_TRUE : JNI_FALSE; }
// Returns true if there is a rectangular clip, with the result in |deviceClipRect|. static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect) { // Get the current clip in device coordinate space. if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType) return false; SkIRect deviceClipIRect; if (context->canvas()->getClipDeviceBounds(&deviceClipIRect)) deviceClipRect.set(deviceClipIRect); else deviceClipRect.setEmpty(); return true; }
void SkRecordDraw(const SkRecord& record, SkCanvas* canvas, SkPicture const* const drawablePicts[], SkDrawable* const drawables[], int drawableCount, const SkBBoxHierarchy* bbh, SkPicture::AbortCallback* callback) { SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); if (bbh) { // Draw only ops that affect pixels in the canvas's current clip. // The SkRecord and BBH were recorded in identity space. This canvas // is not necessarily in that same space. getClipBounds() returns us // this canvas' clip bounds transformed back into identity space, which // lets us query the BBH. SkRect query; if (!canvas->getClipBounds(&query)) { query.setEmpty(); } SkTDArray<int> ops; bbh->search(query, &ops); SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); for (int i = 0; i < ops.count(); i++) { if (callback && callback->abort()) { return; } // This visit call uses the SkRecords::Draw::operator() to call // methods on the |canvas|, wrapped by methods defined with the // DRAW() macro. record.visit(ops[i], draw); } } else { // Draw all ops. SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount); for (int i = 0; i < record.count(); i++) { if (callback && callback->abort()) { return; } // This visit call uses the SkRecords::Draw::operator() to call // methods on the |canvas|, wrapped by methods defined with the // DRAW() macro. record.visit(i, draw); } } }
static void* draw_proc(void* context) { const int OVALW = 32; const int OVALH = 32; const SkBitmap* bm = static_cast<const SkBitmap*>(context); SkFlipPixelRef* ref = static_cast<SkFlipPixelRef*>(bm->pixelRef()); const int DSCALE = 1; SkScalar dx = SkIntToScalar(7) / DSCALE; SkScalar dy = SkIntToScalar(5) / DSCALE; SkScalar x = 0; SkScalar y = 0; SkPaint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorRED); SkRect oval; oval.setEmpty(); SkRect clipR = SkRect::MakeWH(SkIntToScalar(bm->width()), SkIntToScalar(bm->height())); clipR.inset(SK_Scalar1/4, SK_Scalar1/4); while (!gDone) { ref->inval(oval, true); oval.set(x, y, x + SkIntToScalar(OVALW), y + SkIntToScalar(OVALH)); ref->inval(oval, true); SkAutoFlipUpdate update(ref); if (!update.dirty().isEmpty()) { // this must be local to the loop, since it needs to forget the pixels // its writing to after each iteration, since we do the swap SkCanvas canvas(update.bitmap()); canvas.clipRegion(update.dirty()); canvas.drawColor(0, SkXfermode::kClear_Mode); canvas.clipRect(clipR, SkRegion::kIntersect_Op, true); canvas.drawOval(oval, paint); } bounce(&x, &dx, WIDTH-OVALW); bounce(&y, &dy, HEIGHT-OVALH); } return NULL; }
void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context) { ASSERT(!m_canvasLayerStack.isEmpty()); if (m_canvasLayerStack.isEmpty()) return; const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); SkRect layerOpaqueRect = canvasLayer.opaqueRect; SkPaint layerPaint = canvasLayer.paint; // Apply the image mask. if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) layerOpaqueRect.setEmpty(); m_canvasLayerStack.removeLast(); applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); }
bool SkDrawPointsCommand::render(SkCanvas* canvas) const { canvas->clear(0xFFFFFFFF); canvas->save(); SkRect bounds; bounds.setEmpty(); for (unsigned int i = 0; i < fCount; ++i) { bounds.growToInclude(fPts[i].fX, fPts[i].fY); } xlate_and_scale_to_bounds(canvas, bounds); SkPaint p; p.setColor(SK_ColorBLACK); p.setStyle(SkPaint::kStroke_Style); canvas->drawPoints(fMode, fCount, fPts, p); canvas->restore(); return true; }
Rec(int saveCount, const SkPath& path, SkRegion::Op op) : fPath(path) { fRect.setEmpty(); fSaveCount = saveCount; fOp = op; fState = kPath_State; }
void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { // We set this first here but we may overwrite it later if we determine that the clip is // either wide-open or empty. fGenID = GetNextGenID(); // First, optimistically update the current Element's bound information // with the current clip's bound fIsIntersectionOfRects = false; switch (fType) { case kRect_Type: fFiniteBound = this->getRect(); fFiniteBoundType = kNormal_BoundsType; if (SkRegion::kReplace_Op == fOp || (SkRegion::kIntersect_Op == fOp && nullptr == prior) || (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects && prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) { fIsIntersectionOfRects = true; } break; case kRRect_Type: fFiniteBound = fRRect.getBounds(); fFiniteBoundType = kNormal_BoundsType; break; case kPath_Type: fFiniteBound = fPath.get()->getBounds(); if (fPath.get()->isInverseFillType()) { fFiniteBoundType = kInsideOut_BoundsType; } else { fFiniteBoundType = kNormal_BoundsType; } break; case kEmpty_Type: SkDEBUGFAIL("We shouldn't get here with an empty element."); break; } if (!fDoAA) { fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f), SkScalarRoundToScalar(fFiniteBound.fTop), SkScalarRoundToScalar(fFiniteBound.fRight), SkScalarRoundToScalar(fFiniteBound.fBottom)); } // Now determine the previous Element's bound information taking into // account that there may be no previous clip SkRect prevFinite; SkClipStack::BoundsType prevType; if (nullptr == prior) { // no prior clip means the entire plane is writable prevFinite.setEmpty(); // there are no pixels that cannot be drawn to prevType = kInsideOut_BoundsType; } else { prevFinite = prior->fFiniteBound; prevType = prior->fFiniteBoundType; } FillCombo combination = kPrev_Cur_FillCombo; if (kInsideOut_BoundsType == fFiniteBoundType) { combination = (FillCombo) (combination | 0x01); } if (kInsideOut_BoundsType == prevType) { combination = (FillCombo) (combination | 0x02); } SkASSERT(kInvPrev_InvCur_FillCombo == combination || kInvPrev_Cur_FillCombo == combination || kPrev_InvCur_FillCombo == combination || kPrev_Cur_FillCombo == combination); // Now integrate with clip with the prior clips switch (fOp) { case SkRegion::kDifference_Op: this->combineBoundsDiff(combination, prevFinite); break; case SkRegion::kXOR_Op: this->combineBoundsXOR(combination, prevFinite); break; case SkRegion::kUnion_Op: this->combineBoundsUnion(combination, prevFinite); break; case SkRegion::kIntersect_Op: this->combineBoundsIntersection(combination, prevFinite); break; case SkRegion::kReverseDifference_Op: this->combineBoundsRevDiff(combination, prevFinite); break; case SkRegion::kReplace_Op: // Replace just ignores everything prior // The current clip's bound information is already filled in // so nothing to do break; default: SkDebugf("SkRegion::Op error\n"); SkASSERT(0); break; } }
void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) { // Draw all the generated geometry. SkRandom random; GrRenderTarget* currentRT = nullptr; SkAutoTDelete<GrGpuCommandBuffer> commandBuffer; SkRect bounds = SkRect::MakeEmpty(); for (int i = 0; i < fBatches.count(); ++i) { if (!fBatches[i]) { continue; } if (fBatches[i]->renderTarget() != currentRT) { if (commandBuffer) { commandBuffer->end(); if (bounds.intersect(0, 0, SkIntToScalar(currentRT->width()), SkIntToScalar(currentRT->height()))) { SkIRect iBounds; bounds.roundOut(&iBounds); commandBuffer->submit(iBounds); } commandBuffer.reset(); } bounds.setEmpty(); currentRT = fBatches[i]->renderTarget(); if (currentRT) { static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo { GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore, GrColor_ILLEGAL }; commandBuffer.reset(fGpu->createCommandBuffer(currentRT, kBasicLoadStoreInfo, // Color kBasicLoadStoreInfo)); // Stencil } flushState->setCommandBuffer(commandBuffer); } if (commandBuffer) { bounds.join(fBatches[i]->bounds()); } if (fDrawBatchBounds) { const SkRect& batchBounds = fBatches[i]->bounds(); SkIRect iBatchBounds; batchBounds.roundOut(&iBatchBounds); // In multi-draw buffer all the batches use the same render target and we won't need to // get the batchs bounds. if (GrRenderTarget* rt = fBatches[i]->renderTarget()) { fGpu->drawDebugWireRect(rt, iBatchBounds, 0xFF000000 | random.nextU()); } } fBatches[i]->draw(flushState); } if (commandBuffer) { commandBuffer->end(); if (bounds.intersect(0, 0, SkIntToScalar(currentRT->width()), SkIntToScalar(currentRT->height()))) { SkIRect iBounds; bounds.roundOut(&iBounds); commandBuffer->submit(iBounds); } flushState->setCommandBuffer(nullptr); } fGpu->finishDrawTarget(); }