static void test_clip_expansion(skiatest::Reporter* reporter) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); // The following expanding clip should not be skipped. canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); // Draw something so the optimizer doesn't just fold the world. SkPaint p; p.setColor(SK_ColorBLUE); canvas->drawPaint(p); sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); ClipCountingCanvas testCanvas(10, 10); picture->playback(&testCanvas); // Both clips should be present on playback. REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); }
static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d, skiatest::Reporter*, CanvasTestStep*) { SkPictureRecorder recorder; SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight), nullptr, 0); testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1)); testCanvas->clipRect(d.fRect); testCanvas->drawRect(d.fRect, d.fPaint); canvas->drawPicture(recorder.finishRecordingAsPicture()); }
static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, jlong nativeWindow, jobject canvas, jobject dirtyRect) { if (!nativeWindow) { return JNI_FALSE; } ANativeWindow_Buffer buffer; Rect rect; if (dirtyRect) { rect.left = GET_INT(dirtyRect, gRectClassInfo.left); rect.top = GET_INT(dirtyRect, gRectClassInfo.top); rect.right = GET_INT(dirtyRect, gRectClassInfo.right); rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom); } else { rect.set(Rect(0x3FFF, 0x3FFF)); } sp<ANativeWindow> window((ANativeWindow*) nativeWindow); int32_t status = native_window_lock(window.get(), &buffer, &rect); if (status) return JNI_FALSE; ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format); SkBitmap bitmap; bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount); if (buffer.format == WINDOW_FORMAT_RGBX_8888) { bitmap.setIsOpaque(true); } if (buffer.width > 0 && buffer.height > 0) { bitmap.setPixels(buffer.bits); } else { bitmap.setPixels(NULL); } SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format); SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); swapCanvasPtr(env, canvas, nativeCanvas); SkRect clipRect; clipRect.set(rect.left, rect.top, rect.right, rect.bottom); nativeCanvas->clipRect(clipRect); if (dirtyRect) { INVOKEV(dirtyRect, gRectClassInfo.set, int(rect.left), int(rect.top), int(rect.right), int(rect.bottom)); } return JNI_TRUE; }
bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { if (!obj->isListBox()) return true; paintCombo(obj, info, rect); RenderStyle* style = obj->style(); if (style) style->setColor(Color::transparent); Node* node = obj->node(); if (!node || !node->hasTagName(HTMLNames::selectTag)) return true; HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node); // The first item may be visible. Make sure it does not draw. // If it has a style, it overrides the RenderListBox's style, so we // need to make sure both are set to transparent. node = select->item(0); if (node) { RenderObject* renderer = node->renderer(); if (renderer) { RenderStyle* renderStyle = renderer->style(); if (renderStyle) renderStyle->setColor(Color::transparent); } } // Find the first selected option, and draw its text. // FIXME: In a later change, if there is more than one item selected, // draw a string that says "X items" like iPhone Safari does int index = select->selectedIndex(); node = select->item(index); if (!node || !node->hasTagName(HTMLNames::optionTag)) return true; HTMLOptionElement* option = static_cast<HTMLOptionElement*>(node); String label = option->textIndentedToRespectGroupLabel(); SkRect r(rect); SkPaint paint; paint.setAntiAlias(true); paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); // Values for text size and positioning determined by trial and error paint.setTextSize(r.height() - SkIntToScalar(6)); SkCanvas* canvas = getCanvasFromInfo(info); int saveCount = canvas->save(); r.fRight -= SkIntToScalar(RenderSkinCombo::extraWidth()); canvas->clipRect(r); canvas->drawText(label.characters(), label.length() << 1, r.fLeft + SkIntToScalar(5), r.fBottom - SkIntToScalar(5), paint); canvas->restoreToCount(saveCount); return true; }
void View::draw(SkCanvas & canvas) { SkAutoCanvasRestore restore(&canvas, true); canvas.concat(m_props.matrix()); canvas.clipRect(m_props.localRect(), SkRegion::kIntersect_Op, true); onDraw(canvas); for (View* v : m_children) { v->draw(canvas); } }
void GrLayerHoister::DrawLayersToAtlas(GrContext* context, const SkTDArray<GrHoistedLayer>& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), NULL)); SkCanvas* atlasCanvas = surface->getCanvas(); SkPaint clearPaint; clearPaint.setColor(SK_ColorTRANSPARENT); clearPaint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; const SkPicture* pict = atlased[i].fPicture; const SkIPoint offset = atlased[i].fOffset; SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) SkASSERT(!layerPaint || !layerPaint->getImageFilter()); atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), SkIntToScalar(layer->rect().fTop), SkIntToScalar(layer->rect().width()), SkIntToScalar(layer->rect().height())); atlasCanvas->clipRect(bound); // Since 'clear' doesn't respect the clip we need to draw a rect atlasCanvas->drawRect(bound, clearPaint); // '-offset' maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.preTranslate(bound.fLeft, bound.fTop); initialCTM.preConcat(atlased[i].fPreMat); atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, bound, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); }
static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d, skiatest::Reporter*, CanvasTestStep*) { SkPictureRecorder recorder; SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight), NULL, 0); testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1)); testCanvas->clipRect(d.fRect); testCanvas->drawRect(d.fRect, d.fPaint); SkAutoTUnref<SkPicture> testPicture(recorder.endRecording()); canvas->drawPicture(testPicture); }
DEF_TEST(SkLiteRecorder, r) { sk_sp<SkLiteDL> p { SkLiteDL::New({2,2,3,3}) }; SkLiteRecorder rec; SkCanvas* c = &rec; rec.reset(p.get()); c->save(); c->clipRect(SkRect{2,3,4,5}, SkCanvas::kIntersect_Op, true); c->drawRect(SkRect{0,0,9,9}, SkPaint{}); c->restore(); }
void GrLayerHoister::DrawLayersToAtlas(GrContext* context, const SkTDArray<GrHoistedLayer>& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), &props)); SkCanvas* atlasCanvas = surface->getCanvas(); for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture(); if (!pict) { // TODO: can we assume / assert this? continue; } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) SkASSERT(!layerPaint || !layerPaint->getImageFilter()); SkASSERT(!layer->filter()); atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect const SkRect bound = SkRect::Make(layer->rect()); atlasCanvas->clipRect(bound); atlasCanvas->clear(0); // '-offset' maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.preTranslate(bound.fLeft, bound.fTop); initialCTM.preConcat(atlased[i].fPreMat); atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); }
void write(SkWStream* stream) { SkDocument* document = SkDocument::CreatePDF(stream); for (unsigned i = 0; i < mPages.size(); i++) { PageRecord* page = mPages[i]; SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight, &(page->mContentRect)); canvas->clipRect(page->mContentRect); canvas->translate(page->mContentRect.left(), page->mContentRect.top()); canvas->drawPicture(*page->mPicture); document->endPage(); } document->close(); }
static void DrawRoundRect() { #ifdef SK_SCALAR_IS_FIXED bool ret = false; SkPaint paint; SkBitmap bitmap; SkCanvas canvas; SkMatrix matrix; matrix.reset(); bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812); bitmap.allocPixels(); canvas.setBitmapDevice(bitmap); // set up clipper SkRect skclip; skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708)); ret = canvas.clipRect(skclip); SkASSERT(ret); matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28)); matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50)); matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171)); matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043)); matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968)); matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876)); matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0)); matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0)); ret = canvas.concat(matrix); paint.setAntiAlias(true); paint.setColor(0xb2202020); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SkFloatToFixed(68.13)); SkRect r; r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541)); canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint); #endif }
static void fuzz_drawRect(Fuzz* fuzz) { SkPaint p; init_paint(fuzz, &p); sk_sp<SkSurface> surface; init_surface(fuzz, &surface); SkScalar a, b, c, d; fuzz->next(&a, &b, &c, &d); SkRect r; r = SkRect::MakeXYWH(a, b, c, d); SkCanvas* cnv = surface->getCanvas(); cnv->drawRect(r, p); bool bl; fuzz->next(&bl); fuzz->next(&a, &b, &c, &d); r = SkRect::MakeXYWH(a, b, c, d); cnv->clipRect(r, kIntersect_SkClipOp, bl); }
void LayerTextureUpdaterSkPicture::updateTextureRect(GraphicsContext3D* context, TextureAllocator* allocator, ManagedTexture* texture, const IntRect& sourceRect, const IntRect& destRect) { // Make sure SKIA uses the correct GL context. context->makeContextCurrent(); // Notify SKIA to sync its internal GL state. context->grContext()->resetContext(); // Create an accelerated canvas to draw on. FrameBuffer buffer; SkCanvas* canvas = buffer.initialize(context, allocator, texture); canvas->clipRect(SkRect(destRect)); // Translate the origin of contentRect to that of destRect. // Note that destRect is defined relative to sourceRect. canvas->translate(contentRect().x() - sourceRect.x() + destRect.x(), contentRect().y() - sourceRect.y() + destRect.y()); canvas->drawPicture(m_picture); // Flush SKIA context so that all the rendered stuff appears on the texture. context->grContext()->flush(); }
DEF_TEST(test_fuzz_crbug_698714, reporter) { auto surface(SkSurface::MakeRasterN32Premul(500, 500)); SkCanvas* canvas = surface->getCanvas(); SkPaint paint; paint.setAntiAlias(true); SkPath path; path.setFillType(SkPath::kWinding_FillType); path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0,0 path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43430143)); //195.263f, 195.005f path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43434343)); //195.263f, 195.263f path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x434300be)); //-7.2741e-07f, 195.003f // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 3.85088e-29f,1.86082e-39f path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341), SkBits2Float(0xb74343bd), SkBits2Float(0x01434343), SkBits2Float(0x10434343), SkBits2Float(0x00144332)); // 4.11823e-38f, 195.263f, 195.263f, 195.263f, -7.2741e-07f, 195.263f path.cubicTo(SkBits2Float(0x016037c0), SkBits2Float(0x43434343), SkBits2Float(0x43434343), SkBits2Float(0x43434343), SkBits2Float(0xb5434343), SkBits2Float(0x43434343)); // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 195.263f, -2 path.cubicTo(SkBits2Float(0x43434344), SkBits2Float(0x43434341), SkBits2Float(0xb74343bd), SkBits2Float(0x01434343), SkBits2Float(0x43434343), SkBits2Float(0xc0000014)); // -5.87228e+06f, 3.7773e-07f, 3.60231e-13f, -6.64511e+06f,2.77692e-15f, 2.48803e-15f path.cubicTo(SkBits2Float(0xcab33535), SkBits2Float(0x34cacaca), SkBits2Float(0x2acacaca), SkBits2Float(0xcacacae3), SkBits2Float(0x27481927), SkBits2Float(0x27334805)); path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x43434343)); //-7.2741e-07f, 195.263f // 195.263f, 195.263f, -1.16387e-05f, 195.212f, 195.263f, -2 path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341), SkBits2Float(0xb74343b9), SkBits2Float(0x43433643), SkBits2Float(0x43434343), SkBits2Float(0xc0000014)); path.lineTo(SkBits2Float(0xc7004343), SkBits2Float(0x27480527)); //-32835.3f, 2.77584e-15f path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0,0 path.close(); canvas->clipRect({0, 0, 65, 202}); canvas->drawPath(path, paint); }
// construct the pattern removed by the SkPictureRecord::remove_save_layer2 // optimization, i.e.: // SAVE_LAYER (with NULL == bounds) // SAVE // CLIP_RECT // DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT // RESTORE // RESTORE // // saveLayerHasPaint - control if the saveLayer has a paint (the optimization // takes a different path if this is false) // dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization // takes a different path if this is false) // colorsMatch - control if the saveLayer and dbmr2r paint colors // match (the optimization will fail if they do not) static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard, bool saveLayerHasPaint, bool dbmr2rHasPaint, bool colorsMatch) { // Create the pattern that should trigger the optimization preOptPattern->setCount(8); (*preOptPattern)[0] = SAVE; (*preOptPattern)[1] = SAVE_LAYER; (*preOptPattern)[2] = SAVE; (*preOptPattern)[3] = CLIP_RECT; (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; (*preOptPattern)[5] = RESTORE; (*preOptPattern)[6] = RESTORE; (*preOptPattern)[7] = RESTORE; if (colorsMatch) { // Create the pattern that should appear after the optimization postOptPattern->setCount(8); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = SAVE; (*postOptPattern)[3] = CLIP_RECT; (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[5] = RESTORE; (*postOptPattern)[6] = RESTORE; (*postOptPattern)[7] = RESTORE; } else { // Create the pattern that appears if the optimization doesn't fire postOptPattern->setCount(10); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = SAVE_LAYER; (*postOptPattern)[3] = SAVE; (*postOptPattern)[4] = CLIP_RECT; (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[6] = RESTORE; (*postOptPattern)[7] = RESTORE; (*postOptPattern)[8] = RESTORE; (*postOptPattern)[9] = RESTORE; } SkPicture* result = new SkPicture; // have to disable the optimizations while generating the picture SkCanvas* canvas = result->beginRecording(100, 100, SkPicture::kDisableRecordOptimizations_RecordingFlag); SkPaint saveLayerPaint; saveLayerPaint.setColor(0xCC000000); // saveLayer's 'bounds' parameter must be NULL for this optimization if (saveLayerHasPaint) { canvas->saveLayer(NULL, &saveLayerPaint); } else { canvas->saveLayer(NULL, NULL); } canvas->save(); SkRect rect = { 10, 10, 90, 90 }; canvas->clipRect(rect); // The dbmr2r's paint must be opaque SkPaint dbmr2rPaint; if (colorsMatch) { dbmr2rPaint.setColor(0xFF000000); } else { dbmr2rPaint.setColor(0xFFFF0000); } if (dbmr2rHasPaint) { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); } else { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); } canvas->restore(); canvas->restore(); result->endRecording(); return result; }
void GrLayerHoister::DrawLayers(const SkTDArray<GrHoistedLayer>& atlased, const SkTDArray<GrHoistedLayer>& nonAtlased, const SkTDArray<GrHoistedLayer>& recycled, GrReplacements* replacements) { // Render the atlased layers that require it if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), NULL)); SkCanvas* atlasCanvas = surface->getCanvas(); SkPaint paint; paint.setColor(SK_ColorTRANSPARENT); paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); for (int i = 0; i < atlased.count(); ++i) { GrCachedLayer* layer = atlased[i].fLayer; const SkPicture* pict = atlased[i].fPicture; const SkIPoint offset = atlased[i].fOffset; atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), SkIntToScalar(layer->rect().fTop), SkIntToScalar(layer->rect().width()), SkIntToScalar(layer->rect().height())); atlasCanvas->clipRect(bound); // Since 'clear' doesn't respect the clip we need to draw a rect // TODO: ensure none of the atlased layers contain a clear call! atlasCanvas->drawRect(bound, paint); // info.fCTM maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.postTranslate(bound.fLeft, bound.fTop); atlasCanvas->translate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); atlasCanvas->translate(bound.fLeft, bound.fTop); atlasCanvas->concat(atlased[i].fCTM); SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, bound, layer->start()+1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); } // Render the non-atlased layers that require it for (int i = 0; i < nonAtlased.count(); ++i) { GrCachedLayer* layer = nonAtlased[i].fLayer; const SkPicture* pict = nonAtlased[i].fPicture; const SkIPoint offset = nonAtlased[i].fOffset; // Each non-atlased layer has its own GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( layer->texture()->asRenderTarget(), NULL)); SkCanvas* layerCanvas = surface->getCanvas(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), SkIntToScalar(layer->rect().fTop), SkIntToScalar(layer->rect().width()), SkIntToScalar(layer->rect().height())); layerCanvas->clipRect(bound); // TODO: still useful? layerCanvas->clear(SK_ColorTRANSPARENT); SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); layerCanvas->translate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); layerCanvas->concat(nonAtlased[i].fCTM); SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas, bound, layer->start()+1, layer->stop(), initialCTM); layerCanvas->flush(); } convert_layers_to_replacements(atlased, replacements); convert_layers_to_replacements(nonAtlased, replacements); convert_layers_to_replacements(recycled, replacements); }
sk_sp<SkSpecialImage> SkXfermodeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint backgroundOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset)); SkIPoint foregroundOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset)); SkIRect foregroundBounds = SkIRect::EmptyIRect(); if (foreground) { foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(), foreground->width(), foreground->height()); } SkIRect srcBounds = SkIRect::EmptyIRect(); if (background) { srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(), background->width(), background->height()); } srcBounds.join(foregroundBounds); if (srcBounds.isEmpty()) { return nullptr; } SkIRect bounds; if (!this->applyCropRect(ctx, srcBounds, &bounds)) { return nullptr; } offset->fX = bounds.left(); offset->fY = bounds.top(); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { return this->filterImageGPU(source, background, backgroundOffset, foreground, foregroundOffset, bounds); } #endif const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), kPremul_SkAlphaType); sk_sp<SkSpecialSurface> surf(source->makeSurface(info)); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); canvas->clear(0x0); // can't count on background to fully clear the background canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); if (background) { background->draw(canvas, SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY), &paint); } paint.setXfermode(fMode); if (foreground) { foreground->draw(canvas, SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY), &paint); } canvas->clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op); paint.setColor(SK_ColorTRANSPARENT); canvas->drawPaint(paint); return surf->makeImageSnapshot(); }
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) { sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject)); if (!isSurfaceValid(surface)) { doThrowIAE(env); return 0; } Rect dirtyRect; Rect* dirtyRectPtr = NULL; if (dirtyRectObj) { dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left); dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top); dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right); dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom); dirtyRectPtr = &dirtyRect; } ANativeWindow_Buffer outBuffer; status_t err = surface->lock(&outBuffer, dirtyRectPtr); if (err < 0) { const char* const exception = (err == NO_MEMORY) ? OutOfResourcesException : "java/lang/IllegalArgumentException"; jniThrowException(env, exception, NULL); return 0; } // Associate a SkCanvas object to this surface env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format); SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height, convertPixelFormat(outBuffer.format), kPremul_SkAlphaType); if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) { info.fAlphaType = kOpaque_SkAlphaType; } SkBitmap bitmap; ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); bitmap.setInfo(info, bpr); if (outBuffer.width > 0 && outBuffer.height > 0) { bitmap.setPixels(outBuffer.bits); } else { // be safe with an empty bitmap. bitmap.setPixels(NULL); } env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap)); if (dirtyRectPtr) { SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj); nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) ); } if (dirtyRectObj) { env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left); env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top); env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right); env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom); } // Create another reference to the surface and return it. This reference // should be passed to nativeUnlockCanvasAndPost in place of mNativeObject, // because the latter could be replaced while the surface is locked. sp<Surface> lockedSurface(surface); lockedSurface->incStrong(&sRefBaseOwner); return (jlong) lockedSurface.get(); }
void clip(const SkRect& r) { fCanvas->clipRect(r); }
// This is only used to draw borders. void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) { if (paintingDisabled()) return; StrokeStyle style = strokeStyle(); if (style == NoStroke) return; SkPaint paint; SkCanvas* canvas = GC2Canvas(this); const int idx = SkAbs32(point2.x() - point1.x()); const int idy = SkAbs32(point2.y() - point1.y()); // special-case horizontal and vertical lines that are really just dots if (m_data->setup_paint_stroke(&paint, NULL) && (0 == idx || 0 == idy)) { const SkScalar diameter = paint.getStrokeWidth(); const SkScalar radius = SkScalarHalf(diameter); SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); SkScalar dx, dy; int count; SkRect bounds; if (0 == idy) { // horizontal bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); x += radius; dx = diameter * 2; dy = 0; count = idx; } else { // vertical bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); y += radius; dx = 0; dy = diameter * 2; count = idy; } // the actual count is the number of ONs we hit alternating // ON(diameter), OFF(diameter), ... { SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); // now computer the number of cells (ON and OFF) count = SkScalarRound(width); // now compute the number of ONs count = (count + 1) >> 1; } SkAutoMalloc storage(count * sizeof(SkPoint)); SkPoint* verts = (SkPoint*)storage.get(); // now build the array of vertices to past to drawPoints for (int i = 0; i < count; i++) { verts[i].set(x, y); x += dx; y += dy; } paint.setStyle(SkPaint::kFill_Style); paint.setPathEffect(NULL); // clipping to bounds is not required for correctness, but it does // allow us to reject the entire array of points if we are completely // offscreen. This is common in a webpage for android, where most of // the content is clipped out. If drawPoints took an (optional) bounds // parameter, that might even be better, as we would *just* use it for // culling, and not both wacking the canvas' save/restore stack. canvas->save(SkCanvas::kClip_SaveFlag); canvas->clipRect(bounds); canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); canvas->restore(); } else {
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 }