diff options
34 files changed, 575 insertions, 23 deletions
diff --git a/bench/ImageBench.cpp b/bench/ImageBench.cpp index 097ed91d5d..40de28121f 100644 --- a/bench/ImageBench.cpp +++ b/bench/ImageBench.cpp @@ -62,3 +62,117 @@ private: typedef Benchmark INHERITED; }; DEF_BENCH( return new Image2RasterBench; ) + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkOffsetImageFilter.h" + +#if SK_SUPPORT_GPU +#include "SkGrPixelRef.h" +#endif + +enum MyDrawType { + kSprite_Type, + kBitmap_Type, + kImage_Type, +}; + +/* + * Want to time drawing images/bitmaps via drawSprite, and via drawBitmap/drawImage but with + * a non-scaling matrix and a clip that is tight to the image bounds. In this scenario, we + * should be able to match the speed of drawSprite. + * + * An optimal result should be that all three types: sprite/bitmap/image draw at the same speed. + */ +class ImageFilterSpriteBench : public Benchmark { + SkAutoTUnref<SkImage> fImage; + SkBitmap fBitmap; + SkString fName; + MyDrawType fType; + +public: + ImageFilterSpriteBench(MyDrawType dt, const char suffix[]) : fType(dt) { + fName.printf("image-filter-sprite-draw-%s", suffix); + } + + bool isSuitableFor(Backend backend) override { + return kGPU_Backend == backend || kRaster_Backend == backend; + } + +protected: + bool isVisual() override { + return true; + } + + const char* onGetName() override { + return fName.c_str(); + } + + void onPerCanvasPreDraw(SkCanvas* canvas) override { + const SkImageInfo info = SkImageInfo::MakeN32Premul(500, 500); + SkAutoTUnref<SkSurface> surface(canvas->newSurface(info)); + + surface->getCanvas()->drawColor(SK_ColorRED); + fImage.reset(surface->newImageSnapshot()); + + fBitmap.setInfo(info); + if (fImage->getTexture()) { +#if SK_SUPPORT_GPU + fBitmap.setPixelRef(new SkGrPixelRef(info, fImage->getTexture()))->unref(); +#endif + } else { + SkPixmap pmap; + if (!fImage->peekPixels(&pmap)) { + sk_throw(); + } + fBitmap.installPixels(pmap); + } + } + + void onPerCanvasPostDraw(SkCanvas*) override { + // Release the image and raster surface here to prevent out of order destruction + // between these and the gpu interface. + fImage.reset(nullptr); + fBitmap.reset(); + } + + void onDraw(int loops, SkCanvas* canvas) override { + // This clip is important; it allows the drawImage/drawBitmap code to fall into the + // fast (sprite) case, since the imagefilter's output should match. + // + // When we address skbug.com/4526 we should be able to remove the need for this clip. + // + canvas->clipRect(SkRect::MakeIWH(fImage->width(), fImage->height())); + + const SkScalar kDelta = 10; + SkPaint paint; + for (int i = 0; i < loops; i++) { + for (int inner = 0; inner < 10; ++inner) { + // build the filter everytime, so we don't accidentally draw a cached version, + // since the point of this bench is to time the actual imagefilter + // handling/overhead. + SkAutoTUnref<SkImageFilter> filter(SkOffsetImageFilter::Create(kDelta, kDelta)); + paint.setImageFilter(filter); + + switch (fType) { + case kSprite_Type: + canvas->drawSprite(fBitmap, 0, 0, &paint); + break; + case kBitmap_Type: + canvas->drawBitmap(fBitmap, 0, 0, &paint); + break; + case kImage_Type: + canvas->drawImage(fImage, 0, 0, &paint); + break; + } + } + } + } + +private: + typedef Benchmark INHERITED; +}; +DEF_BENCH( return new ImageFilterSpriteBench(kSprite_Type, "sprite"); ) +DEF_BENCH( return new ImageFilterSpriteBench(kBitmap_Type, "bitmap"); ) +DEF_BENCH( return new ImageFilterSpriteBench(kImage_Type, "image"); ) + diff --git a/gm/dropshadowimagefilter.cpp b/gm/dropshadowimagefilter.cpp index 512c82de01..81824ab797 100644 --- a/gm/dropshadowimagefilter.cpp +++ b/gm/dropshadowimagefilter.cpp @@ -67,6 +67,29 @@ static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { canvas->restore(); } +static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.allocN32Pixels(bounds.width(), bounds.height()); + bm.eraseColor(SK_ColorRED); + SkCanvas c(bm); + + SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44); + paint.setColor(SK_ColorGREEN); + c.drawRect(SkRect::Make(cropRect), paint); + + paint.setImageFilter(imf); + SkPoint loc = { r.fLeft, r.fTop }; + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, + SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), + &paint); +} + /////////////////////////////////////////////////////////////////////////////// class DropShadowImageFilterGM : public skiagm::GM { @@ -90,7 +113,7 @@ protected: virtual void onDraw(SkCanvas* canvas) { void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = { - draw_bitmap, draw_path, draw_paint, draw_text + draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text }; SkAutoTUnref<SkColorFilter> cf( diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp index fbe4681da4..0101a9c2a0 100644 --- a/gm/imagefiltersbase.cpp +++ b/gm/imagefiltersbase.cpp @@ -165,6 +165,26 @@ static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { canvas->drawBitmap(bm, 0, 0, &paint); } +static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + paint.setImageFilter(imf); + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.allocN32Pixels(bounds.width(), bounds.height()); + bm.eraseColor(SK_ColorTRANSPARENT); + SkCanvas c(bm); + draw_path(&c, r, nullptr); + + SkPoint loc = { r.fLeft, r.fTop }; + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, + SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), + &paint); +} + /////////////////////////////////////////////////////////////////////////////// class ImageFiltersBaseGM : public skiagm::GM { @@ -190,6 +210,7 @@ protected: draw_paint, draw_line, draw_rect, draw_path, draw_text, draw_bitmap, + draw_sprite }; SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED, diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp index 4c6ef62356..745e8984db 100644 --- a/gm/imagefilterscropped.cpp +++ b/gm/imagefilterscropped.cpp @@ -65,6 +65,29 @@ static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { canvas->drawBitmap(bm, 0, 0, &paint); } +static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { + SkPaint paint; + + SkIRect bounds; + r.roundOut(&bounds); + + SkBitmap bm; + bm.allocN32Pixels(bounds.width(), bounds.height()); + bm.eraseColor(SK_ColorRED); + SkCanvas c(bm); + + SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44); + paint.setColor(SK_ColorGREEN); + c.drawRect(SkRect::Make(cropRect), paint); + + paint.setImageFilter(imf); + SkPoint loc = { r.fLeft, r.fTop }; + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, + SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), + &paint); +} + /////////////////////////////////////////////////////////////////////////////// class ImageFiltersCroppedGM : public skiagm::GM { @@ -112,7 +135,7 @@ protected: virtual void onDraw(SkCanvas* canvas) override { void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = { - draw_bitmap, draw_path, draw_paint, draw_text + draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text }; SkAutoTUnref<SkColorFilter> cf( diff --git a/gm/spritebitmap.cpp b/gm/spritebitmap.cpp index 4f221d49fc..b199371e89 100644 --- a/gm/spritebitmap.cpp +++ b/gm/spritebitmap.cpp @@ -22,8 +22,8 @@ static void make_bm(SkBitmap* bm) { canvas.drawCircle(50, 50, 50, paint); } -static void draw_1_bitmap(SkCanvas* canvas, const SkBitmap& bm, bool doClip, - int dx, int dy, SkImageFilter* filter = nullptr) { +static void draw_2_bitmaps(SkCanvas* canvas, const SkBitmap& bm, bool doClip, + int dx, int dy, SkImageFilter* filter = nullptr) { SkAutoCanvasRestore acr(canvas, true); SkPaint paint; @@ -35,6 +35,15 @@ static void draw_1_bitmap(SkCanvas* canvas, const SkBitmap& bm, bool doClip, paint.setImageFilter(filter); clipR.inset(5, 5); + if (doClip) { + canvas->save(); + canvas->clipRect(clipR); + } + canvas->drawSprite(bm, dx, dy, &paint); + if (doClip) { + canvas->restore(); + } + canvas->translate(SkIntToScalar(bm.width() + 20), 0); if (doClip) { @@ -74,13 +83,13 @@ protected: SkScalar sigma = 8; SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(sigma, sigma)); - draw_1_bitmap(canvas, bm, false, dx, dy); + draw_2_bitmaps(canvas, bm, false, dx, dy); dy += bm.height() + 20; - draw_1_bitmap(canvas, bm, false, dx, dy, filter); + draw_2_bitmaps(canvas, bm, false, dx, dy, filter); dy += bm.height() + 20; - draw_1_bitmap(canvas, bm, true, dx, dy); + draw_2_bitmaps(canvas, bm, true, dx, dy); dy += bm.height() + 20; - draw_1_bitmap(canvas, bm, true, dx, dy, filter); + draw_2_bitmaps(canvas, bm, true, dx, dy, filter); } private: @@ -187,6 +196,22 @@ protected: canvas->translate(spacer, 0); show_image(canvas, image2, offset2); // not snug + // Try drawing the original w/ the filter, to see that it "draws" the same as + // when we have manually applied the filter (above). + { + SkPaint paint; + paint.setImageFilter(filter); + + SkBitmap bm; + image0->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode); + SkPoint loc = { 0, 0 }; + canvas->translate(spacer, 0); + canvas->getTotalMatrix().mapPoints(&loc, 1); + canvas->drawSprite(bm, (int)loc.x(), (int)loc.y(), &paint); // like snug + + canvas->translate(spacer, 0); + canvas->drawImage(image0, 0, 0, &paint); // like not snug + } canvas->restore(); canvas->translate(0, spacer); @@ -197,3 +222,152 @@ private: typedef GM INHERITED; }; DEF_GM( return new ApplyFilterGM; ) + +////////////////////// + +#include "SkDisplacementMapEffect.h" +#include "SkMatrixConvolutionImageFilter.h" + +static SkPMColor max_component(SkPMColor a, SkPMColor b) { + int dr = SkAbs32(SkGetPackedR32(a) - SkGetPackedR32(b)); + int dg = SkAbs32(SkGetPackedG32(a) - SkGetPackedG32(b)); + int db = SkAbs32(SkGetPackedB32(a) - SkGetPackedB32(b)); + int d = SkTMax(dr, SkTMax(dg, db)); + d = 0xFF - d; + return SkPackARGB32(0xFF, d, d, d); +} + +static SkImage* compute_diff(SkImage* a, SkImage* b) { + SkASSERT(a->width() == b->width() && a->height() == b->height()); + const SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height()); + SkBitmap bma, bmb, bmdiff; + bma.allocPixels(info); + bmb.allocPixels(info); + bmdiff.allocPixels(info); + + a->readPixels(info, bma.getPixels(), bma.rowBytes(), 0, 0); + b->readPixels(info, bmb.getPixels(), bmb.rowBytes(), 0, 0); + for (int y = 0; y < info.height(); ++y) { + for (int x = 0; x < info.width(); ++x) { + *bmdiff.getAddr32(x, y) = max_component(*bma.getAddr32(x, y), *bmb.getAddr32(x, y)); + } + } + bmdiff.setImmutable(); // avoid the copy + return SkImage::NewFromBitmap(bmdiff); +} + +static SkImage* make_native_red_oval(SkCanvas* rootCanvas) { + SkImageInfo info = SkImageInfo::MakeN32Premul(160, 90); + SkAutoTUnref<SkSurface> surface(rootCanvas->newSurface(info)); + if (!surface) { + surface.reset(SkSurface::NewRaster(info)); + } + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorRED); + surface->getCanvas()->drawOval(SkRect::MakeWH(160, 90), paint); + return surface->newImageSnapshot(); +} + + +static SkSurface* make_surface(SkCanvas* factory, const SkImageInfo& info) { + SkSurface* surface = factory->newSurface(info); + if (!surface) { + surface = SkSurface::NewRaster(info); + } + return surface; +} + +template <typename DrawProc> SkImage* snapshot(SkCanvas* canvas, const SkImageInfo& info, + DrawProc p) { + SkAutoTUnref<SkSurface> surface(make_surface(canvas, info)); + p(surface->getCanvas()); + return surface->newImageSnapshot(); +} + +/** + * Try drawing an image+imagefilter in two different ways + * 1. as drawSprite + * 2. as drawImage + clipped to image bounds + * The two should draw the same. To try to visualize this, we draw a 4th column of the difference + * between the two versions. If it is all black (where there is alpha), they drew the same! + */ +class DrawWithFilterGM : public skiagm::GM { +public: + DrawWithFilterGM() {} + +protected: + SkString onShortName() override { + return SkString("draw-with-filter"); + } + + SkISize onISize() override { + return SkISize::Make(780, 780); + } + + void onDraw(SkCanvas* canvas) override { + SkAutoTUnref<SkImage> image0(make_native_red_oval(canvas)); + SkAutoTUnref<SkImage> image1(make_native_red_oval(canvas)); + + const ImageFilterFactory factories[] = { + IFCCast([]{ return SkBlurImageFilter::Create(8, 8); }), + IFCCast([]{ SkAutoTUnref<SkColorFilter> cf(SkModeColorFilter::Create(SK_ColorBLUE, + SkXfermode::kSrcIn_Mode)); + return SkColorFilterImageFilter::Create(cf); + }), + IFCCast([]{ return SkDilateImageFilter::Create(8, 8); }), + IFCCast([]{ return SkErodeImageFilter::Create(8, 8); }), + IFCCast([]{ return SkOffsetImageFilter::Create(8, 8); }), + + IFCCast([]{ return (SkImageFilter*)SkDisplacementMapEffect::Create( + SkDisplacementMapEffect::kR_ChannelSelectorType, + SkDisplacementMapEffect::kG_ChannelSelectorType, + 10, nullptr); }), + IFCCast([]{ + const SkScalar kernel[] = { 1, 1, 1, 1, -7, 1, 1, 1, 1 }; + return (SkImageFilter*)SkMatrixConvolutionImageFilter::Create( + SkISize::Make(3, 3), + kernel, 1, 0, + SkIPoint::Make(0, 0), + SkMatrixConvolutionImageFilter::kClamp_TileMode, + true); }), + }; + + const SkScalar dx = 180; + const SkScalar dy = 110; + const SkImageInfo info = SkImageInfo::MakeN32Premul(image0->width(), image0->height()); + + canvas->translate(20, 20); + for (auto&& factory : factories) { + SkAutoTUnref<SkImageFilter> filter(factory()); + SkPaint paint; + paint.setImageFilter(filter); + + SkAutoTUnref<SkImage> snap0(snapshot(canvas, info, [&](SkCanvas* c) { + c->drawImage(image0, 0, 0, &paint); + })); + canvas->drawImage(snap0, 0, 0); + + SkAutoTUnref<SkImage> snap1(snapshot(canvas, info, [&](SkCanvas* c) { + SkBitmap bm; + image1->asLegacyBitmap(&bm, SkImage::kRO_LegacyBitmapMode); + c->drawSprite(bm, 0, 0, &paint); + })); + canvas->drawImage(snap1, dx, 0); + + SkAutoTUnref<SkImage> diff(snapshot(canvas, info, [&](SkCanvas* c) { + SkAutoTUnref<SkImage> diff(compute_diff(snap0, snap1)); + c->drawImage(diff, 0, 0); + })); + canvas->drawImage(diff, 2*dx, 0); + + canvas->translate(0, dy); + } + } + +private: + typedef GM INHERITED; +}; +DEF_GM( return new DrawWithFilterGM; ) + diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 6be13f2a52..14f0aa5250 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -911,6 +911,19 @@ public: void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint = NULL); + /** Draw the specified bitmap, with its top/left corner at (x,y), + NOT transformed by the current matrix. Note: if the paint + contains a maskfilter that generates a mask which extends beyond the + bitmap's original width/height, then the bitmap will be drawn as if it + were in a Shader with CLAMP mode. Thus the color outside of the original + width/height will be the edge color replicated. + @param bitmap The bitmap to be drawn + @param left The position of the left side of the bitmap being drawn + @param top The position of the top side of the bitmap being drawn + @param paint The paint used to draw the bitmap, or NULL + */ + void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint = NULL); + /** Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted based on the Align setting in the paint. @param text The text to be drawn @@ -1279,9 +1292,7 @@ protected: SrcRectConstraint); virtual void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*); -#ifdef SK_SUPPORT_LEGACY_ONDRAWSPRITE - virtual void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) {} -#endif + virtual void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*); enum ClipEdgeStyle { kHard_ClipEdgeStyle, diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h index ab9b8a089c..b856647aee 100644 --- a/include/private/SkRecords.h +++ b/include/private/SkRecords.h @@ -63,6 +63,7 @@ namespace SkRecords { M(DrawTextOnPath) \ M(DrawRRect) \ M(DrawRect) \ + M(DrawSprite) \ M(DrawTextBlob) \ M(DrawAtlas) \ M(DrawVertices) @@ -314,6 +315,11 @@ RECORD(DrawRRect, kDraw_Tag, RECORD(DrawRect, kDraw_Tag, SkPaint paint; SkRect rect); +RECORD(DrawSprite, kDraw_Tag|kHasImage_Tag, + Optional<SkPaint> paint; + ImmutableBitmap bitmap; + int left; + int top); RECORD(DrawText, kDraw_Tag|kHasText_Tag, SkPaint paint; PODArray<char> text; diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h index 41395e055a..b853ba686b 100644 --- a/include/utils/SkDumpCanvas.h +++ b/include/utils/SkDumpCanvas.h @@ -108,6 +108,7 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/include/utils/SkLuaCanvas.h b/include/utils/SkLuaCanvas.h index 270be8a6d3..f07af0695b 100644 --- a/include/utils/SkLuaCanvas.h +++ b/include/utils/SkLuaCanvas.h @@ -54,6 +54,7 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h index 02f8c35d36..a60836be57 100644 --- a/include/utils/SkNWayCanvas.h +++ b/include/utils/SkNWayCanvas.h @@ -65,6 +65,7 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/include/utils/SkPaintFilterCanvas.h b/include/utils/SkPaintFilterCanvas.h index 0ad7e251b6..d693758797 100644 --- a/include/utils/SkPaintFilterCanvas.h +++ b/include/utils/SkPaintFilterCanvas.h @@ -71,6 +71,7 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 414e94ed59..814375687a 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1441,8 +1441,36 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, LOOPER_END } -///////////////////////////////////////////////////////////////////////////// +void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { + if (gTreatSpriteAsBitmap) { + this->save(); + this->resetMatrix(); + this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint); + this->restore(); + return; + } + + TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()"); + if (bitmap.drawsNothing()) { + return; + } + SkDEBUGCODE(bitmap.validate();) + + SkPaint tmp; + if (nullptr == paint) { + paint = &tmp; + } + LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) + + while (iter.next()) { + const SkIPoint pos = { x - iter.getX(), y - iter.getY() }; + iter.fDevice->drawBitmapAsSprite(iter, bitmap, pos.x(), pos.y(), looper.paint()); + } + LOOPER_END +} + +///////////////////////////////////////////////////////////////////////////// void SkCanvas::translate(SkScalar dx, SkScalar dy) { SkMatrix m; m.setTranslate(dx, dy); @@ -1985,6 +2013,13 @@ void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, con this->onDrawBitmapNine(bitmap, center, dst, paint); } +void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { + if (bitmap.drawsNothing()) { + return; + } + this->onDrawSprite(bitmap, left, top, paint); +} + void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int count, SkXfermode::Mode mode, const SkRect* cull, const SkPaint* paint) { diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 9b27a3d1e9..b994071cbb 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -368,11 +368,11 @@ void SkPicturePlayback::handleOp(SkReader32* reader, canvas->drawRRect(rrect, paint); } break; case DRAW_SPRITE: { - /* const SkPaint* paint = */ fPictureData->getPaint(reader); - /* const SkBitmap bitmap = */ shallow_copy(fPictureData->getBitmap(reader)); - /* int left = */ reader->readInt(); - /* int top = */ reader->readInt(); - // drawSprite removed dec-2015 + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader)); + int left = reader->readInt(); + int top = reader->readInt(); + canvas->drawSprite(bitmap, left, top, paint); } break; case DRAW_TEXT: { const SkPaint& paint = *fPictureData->getPaint(reader); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index f9ab0c93c1..2b56b74dad 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -626,6 +626,19 @@ void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce this->validate(initialOffset, size); } +void SkPictureRecord::onDrawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint) { + // op + paint index + bitmap index + left + top + size_t size = 5 * kUInt32Size; + size_t initialOffset = this->addDraw(DRAW_SPRITE, &size); + SkASSERT(initialOffset+get_paint_offset(DRAW_SPRITE, size) == fWriter.bytesWritten()); + this->addPaintPtr(paint); + this->addBitmap(bitmap); + this->addInt(left); + this->addInt(top); + this->validate(initialOffset, size); +} + void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { // op + paint index + length + 'length' worth of chars + x + y diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index 3dcaa6ce4c..7e21fab112 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -192,6 +192,7 @@ protected: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index f512ecf015..849e8f92b2 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -111,6 +111,7 @@ DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); DRAW(DrawRect, drawRect(r.rect, r.paint)); +DRAW(DrawSprite, drawSprite(r.bitmap.shallowCopy(), r.left, r.top, r.paint)); DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint)); @@ -388,6 +389,14 @@ private: Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw. + Bounds bounds(const DrawSprite& op) const { // Ignores the matrix, but respects the clip. + SkRect rect = Bounds::MakeXYWH(op.left, op.top, op.bitmap.width(), op.bitmap.height()); + if (!rect.intersect(fCurrentClipBounds)) { + return Bounds::MakeEmpty(); + } + return rect; + } + Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); } Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); } Bounds bounds(const DrawRRect& op) const { diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index c7a826f463..d90b2c025a 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -240,6 +240,10 @@ void SkRecorder::onDrawImageNine(const SkImage* image, const SkIRect& center, APPEND(DrawImageNine, this->copy(paint), image, center, dst); } +void SkRecorder::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { + APPEND(DrawSprite, this->copy(paint), bitmap, left, top); +} + void SkRecorder::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { APPEND(DrawText, diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 6bde375db4..92197a0a3c 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -106,6 +106,7 @@ public: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h index 1820a4941a..c4217f3bf9 100644 --- a/src/pipe/SkGPipePriv.h +++ b/src/pipe/SkGPipePriv.h @@ -57,6 +57,7 @@ enum DrawOps { kDrawPosTextH_DrawOp, kDrawRect_DrawOp, kDrawRRect_DrawOp, + kDrawSprite_DrawOp, kDrawText_DrawOp, kDrawTextBlob_DrawOp, kDrawTextOnPath_DrawOp, diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp index 24f3e2d1ea..4d57a6cca9 100644 --- a/src/pipe/SkGPipeRead.cpp +++ b/src/pipe/SkGPipeRead.cpp @@ -654,6 +654,17 @@ static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, } } +static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + BitmapHolder holder(reader, op32, state); + bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag); + const SkIPoint* point = skip<SkIPoint>(reader); + const SkBitmap* bitmap = holder.getBitmap(); + if (state->shouldDraw()) { + canvas->drawSprite(*bitmap, point->fX, point->fY, hasPaint ? &state->paint() : nullptr); + } +} + static void drawImage_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, SkGPipeState* state) { unsigned slot = DrawOp_unpackData(op32); unsigned flags = DrawOp_unpackFlags(op32); @@ -880,6 +891,7 @@ static const ReadProc gReadTable[] = { drawPosTextH_rp, drawRect_rp, drawRRect_rp, + drawSprite_rp, drawText_rp, drawTextBlob_rp, drawTextOnPath_rp, diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp index 2cab09c9e1..0fccb80d0a 100644 --- a/src/pipe/SkGPipeWrite.cpp +++ b/src/pipe/SkGPipeWrite.cpp @@ -284,6 +284,7 @@ protected: const SkPaint*) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, @@ -827,6 +828,16 @@ void SkGPipeCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, } } +void SkGPipeCanvas::onDrawSprite(const SkBitmap& bm, int left, int top, const SkPaint* paint) { + NOTIFY_SETUP(this); + size_t opBytesNeeded = sizeof(int32_t) * 2; + + if (this->commonDrawBitmap(bm, kDrawSprite_DrawOp, 0, opBytesNeeded, paint)) { + fWriter.write32(left); + fWriter.write32(top); + } +} + bool SkGPipeCanvas::commonDrawImage(const SkImage* image, DrawOps op, unsigned flags, size_t opBytesNeeded, const SkPaint* paint) { if (fDone) { diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp index a5005610ae..8d0209d964 100644 --- a/src/utils/SkDumpCanvas.cpp +++ b/src/utils/SkDumpCanvas.cpp @@ -397,6 +397,13 @@ void SkDumpCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, cons bs.c_str(), rs.c_str()); } +void SkDumpCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { + SkString str; + bitmap.toString(&str); + this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s %d %d)", str.c_str(), + x, y); +} + void SkDumpCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkString str; diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp index 4916decccb..ada76666fe 100644 --- a/src/utils/SkLuaCanvas.cpp +++ b/src/utils/SkLuaCanvas.cpp @@ -245,6 +245,13 @@ void SkLuaCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const } } +void SkLuaCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { + AUTO_LUA("drawSprite"); + if (paint) { + lua.pushPaint(*paint, "paint"); + } +} + void SkLuaCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { AUTO_LUA("drawText"); diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp index 05909a33fa..fb8d0ee13e 100644 --- a/src/utils/SkNWayCanvas.cpp +++ b/src/utils/SkNWayCanvas.cpp @@ -224,6 +224,13 @@ void SkNWayCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, cons } } +void SkNWayCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { + Iter iter(fList); + while (iter.next()) { + iter->drawSprite(bitmap, x, y, paint); + } +} + void SkNWayCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { Iter iter(fList); diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp index ce1e4b7dbf..dff1514acb 100644 --- a/src/utils/SkPaintFilterCanvas.cpp +++ b/src/utils/SkPaintFilterCanvas.cpp @@ -110,6 +110,12 @@ void SkPaintFilterCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& ce this->INHERITED::onDrawBitmapNine(bm, center, dst, apf.paint()); } +void SkPaintFilterCanvas::onDrawSprite(const SkBitmap& bm, int left, int top, + const SkPaint* paint) { + AutoPaintFilter apf(this, kBitmap_Type, paint); + this->INHERITED::onDrawSprite(bm, left, top, apf.paint()); +} + void SkPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, diff --git a/src/utils/android/SkAndroidSDKCanvas.cpp b/src/utils/android/SkAndroidSDKCanvas.cpp index 9e1463d966..e6f802fc5b 100644 --- a/src/utils/android/SkAndroidSDKCanvas.cpp +++ b/src/utils/android/SkAndroidSDKCanvas.cpp @@ -156,6 +156,13 @@ void SkAndroidSDKCanvas::onDrawBitmapNine(const SkBitmap& bitmap, FILTER_PTR(paint); fProxyTarget->drawBitmapNine(bitmap, center, dst, filteredPaint); } +void SkAndroidSDKCanvas::onDrawSprite(const SkBitmap& bitmap, + int left, + int top, + const SkPaint* paint) { + FILTER_PTR(paint); + fProxyTarget->drawSprite(bitmap, left, top, filteredPaint); +} void SkAndroidSDKCanvas::onDrawVertices(VertexMode vMode, int vertexCount, const SkPoint vertices[], diff --git a/src/utils/android/SkAndroidSDKCanvas.h b/src/utils/android/SkAndroidSDKCanvas.h index 1605db44f6..08b73f144c 100644 --- a/src/utils/android/SkAndroidSDKCanvas.h +++ b/src/utils/android/SkAndroidSDKCanvas.h @@ -46,6 +46,8 @@ protected: const SkPaint* paint, SkCanvas::SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) override; + void onDrawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint) override; void onDrawVertices(VertexMode vMode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xMode, const uint16_t indices[], int indexCount, diff --git a/src/utils/debugger/SkDebugCanvas.cpp b/src/utils/debugger/SkDebugCanvas.cpp index 86937392a6..f163cc5470 100644 --- a/src/utils/debugger/SkDebugCanvas.cpp +++ b/src/utils/debugger/SkDebugCanvas.cpp @@ -484,6 +484,10 @@ void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint)); } +void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { + this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint)); +} + void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint)); diff --git a/src/utils/debugger/SkDebugCanvas.h b/src/utils/debugger/SkDebugCanvas.h index 7de2b59437..612ee5756f 100644 --- a/src/utils/debugger/SkDebugCanvas.h +++ b/src/utils/debugger/SkDebugCanvas.h @@ -205,6 +205,7 @@ protected: const SkPaint*, SrcRectConstraint) override; void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, const SkPaint*) override; + void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override; void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; diff --git a/src/utils/debugger/SkDrawCommand.cpp b/src/utils/debugger/SkDrawCommand.cpp index 9ebb81e74a..330c2c9183 100644 --- a/src/utils/debugger/SkDrawCommand.cpp +++ b/src/utils/debugger/SkDrawCommand.cpp @@ -48,6 +48,7 @@ const char* SkDrawCommand::GetCommandString(OpType type) { case kDrawPosTextH_OpType: return "DrawPosTextH"; case kDrawRect_OpType: return "DrawRect"; case kDrawRRect_OpType: return "DrawRRect"; + case kDrawSprite_OpType: return "DrawSprite"; case kDrawText_OpType: return "DrawText"; case kDrawTextBlob_OpType: return "DrawTextBlob"; case kDrawTextOnPath_OpType: return "DrawTextOnPath"; @@ -798,6 +799,36 @@ bool SkDrawDRRectCommand::render(SkCanvas* canvas) const { return true; } +SkDrawSpriteCommand::SkDrawSpriteCommand(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint) + : INHERITED(kDrawSprite_OpType) { + fBitmap = bitmap; + fLeft = left; + fTop = top; + if (paint) { + fPaint = *paint; + fPaintPtr = &fPaint; + } else { + fPaintPtr = nullptr; + } + + fInfo.push(SkObjectParser::BitmapToString(bitmap)); + fInfo.push(SkObjectParser::IntToString(left, "Left: ")); + fInfo.push(SkObjectParser::IntToString(top, "Top: ")); + if (paint) { + fInfo.push(SkObjectParser::PaintToString(*paint)); + } +} + +void SkDrawSpriteCommand::execute(SkCanvas* canvas) const { + canvas->drawSprite(fBitmap, fLeft, fTop, fPaintPtr); +} + +bool SkDrawSpriteCommand::render(SkCanvas* canvas) const { + render_bitmap(canvas, fBitmap); + return true; +} + SkDrawTextCommand::SkDrawTextCommand(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) : INHERITED(kDrawText_OpType) { diff --git a/src/utils/debugger/SkDrawCommand.h b/src/utils/debugger/SkDrawCommand.h index 9da05eeedb..c7e5f008a4 100644 --- a/src/utils/debugger/SkDrawCommand.h +++ b/src/utils/debugger/SkDrawCommand.h @@ -41,6 +41,7 @@ public: kDrawPosTextH_OpType, kDrawRect_OpType, kDrawRRect_OpType, + kDrawSprite_OpType, kDrawText_OpType, kDrawTextBlob_OpType, kDrawTextOnPath_OpType, @@ -517,6 +518,21 @@ private: typedef SkDrawCommand INHERITED; }; +class SkDrawSpriteCommand : public SkDrawCommand { +public: + SkDrawSpriteCommand(const SkBitmap& bitmap, int left, int top, const SkPaint* paint); + void execute(SkCanvas* canvas) const override; + bool render(SkCanvas* canvas) const override; +private: + SkBitmap fBitmap; + int fLeft; + int fTop; + SkPaint fPaint; + SkPaint* fPaintPtr; + + typedef SkDrawCommand INHERITED; +}; + class SkDrawVerticesCommand : public SkDrawCommand { public: SkDrawVerticesCommand(SkCanvas::VertexMode vmode, int vertexCount, diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index 1bf8cbdb73..6a6a7a92d6 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -977,7 +977,7 @@ static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) { SkPaint paint; paint.setImageFilter(blur); - canvas->drawBitmap(bitmap, 0, 0, &paint); + canvas->drawSprite(bitmap, 0, 0, &paint); } DEF_TEST(HugeBlurImageFilter, reporter) { @@ -1067,7 +1067,7 @@ static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* re SkPaint paint; paint.setImageFilter(xfermodeNoFg); - canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite + canvas->drawSprite(bitmap, 0, 0, &paint); uint32_t pixel; SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType); @@ -1075,12 +1075,12 @@ static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* re REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); paint.setImageFilter(xfermodeNoBg); - canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite + canvas->drawSprite(bitmap, 0, 0, &paint); canvas->readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); paint.setImageFilter(xfermodeNoFgNoBg); - canvas->drawBitmap(bitmap, 0, 0, &paint); // drawSprite + canvas->drawSprite(bitmap, 0, 0, &paint); canvas->readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); } @@ -1128,7 +1128,7 @@ DEF_TEST(ImageFilterNestedSaveLayer, reporter) { canvas.clear(0x0); canvas.readPixels(info, &pixel, 4, 25, 25); canvas.saveLayer(&bounds1, nullptr); - canvas.drawBitmap(bitmap, 20, 20, &filterPaint); // drawSprite + canvas.drawSprite(bitmap, 20, 20, &filterPaint); canvas.restore(); canvas.readPixels(info, &pixel, 4, 25, 25); diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index 45a0505773..a81733e38a 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -1194,7 +1194,7 @@ static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint); canvas->drawBitmapNine(bitmap, irect, rect, &paint); - canvas->drawBitmap(bitmap, 1, 1); // drawSprite + canvas->drawSprite(bitmap, 1, 1); } static void test_draw_bitmaps(SkCanvas* canvas) { diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index f0d9b17591..6859bce6bc 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -460,6 +460,7 @@ static void test_copy_on_write(skiatest::Reporter* reporter, SkSurface* surface) EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0)) EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, testRect, nullptr)) EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, nullptr)) + EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, nullptr)) EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint)) EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \ testPaint)) |