From 4c21dc5ddf3b482293ed34eead876d8d61a662c3 Mon Sep 17 00:00:00 2001 From: reed Date: Thu, 25 Jun 2015 12:32:03 -0700 Subject: add drawImageNine this also exposes nine-patch drawing directly to devices, and creates a shared iterator for unrolling a nine-patch into single rect->rect draws. BUG=skia: Review URL: https://codereview.chromium.org/1211583003 --- src/core/SkCanvas.cpp | 123 ++++++++++++++++++++--------------------- src/core/SkDevice.cpp | 21 +++++++ src/core/SkNinePatchIter.cpp | 72 ++++++++++++++++++++++++ src/core/SkNinePatchIter.h | 42 ++++++++++++++ src/core/SkPictureFlat.h | 3 +- src/core/SkPicturePlayback.cpp | 7 +++ src/core/SkPictureRecord.cpp | 17 +++++- src/core/SkPictureRecord.h | 2 + src/core/SkRecordDraw.cpp | 4 ++ src/core/SkRecorder.cpp | 5 ++ src/core/SkRecorder.h | 2 + src/core/SkRecords.h | 5 ++ 12 files changed, 237 insertions(+), 66 deletions(-) create mode 100644 src/core/SkNinePatchIter.cpp create mode 100644 src/core/SkNinePatchIter.h (limited to 'src/core') diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index d7c62e8e11..88339ff4f3 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -16,6 +16,7 @@ #include "SkErrorInternals.h" #include "SkImage.h" #include "SkMetaData.h" +#include "SkNinePatchIter.h" #include "SkPathOps.h" #include "SkPatchUtils.h" #include "SkPicture.h" @@ -1771,8 +1772,19 @@ void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRe this->onDrawImageRect(image, src, dst, paint); } +void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, + const SkPaint* paint) { + if (dst.isEmpty()) { + return; + } + if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) { + this->drawImageRect(image, NULL, dst, paint); + } + this->onDrawImageNine(image, center, dst, paint); +} + void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) { - if (bitmap.empty()) { + if (bitmap.drawsNothing()) { return; } this->onDrawBitmap(bitmap, dx, dy, paint); @@ -1780,7 +1792,7 @@ void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, cons void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags) { - if (bitmap.empty()) { + if (bitmap.drawsNothing() || dst.isEmpty()) { return; } this->onDrawBitmapRect(bitmap, src, dst, paint, flags); @@ -1788,14 +1800,17 @@ void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, c void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { - if (bitmap.empty()) { + if (bitmap.drawsNothing() || dst.isEmpty()) { return; } + if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) { + this->drawBitmapRectToRect(bitmap, NULL, dst, paint); + } this->onDrawBitmapNine(bitmap, center, dst, paint); } void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { - if (bitmap.empty()) { + if (bitmap.drawsNothing()) { return; } this->onDrawSprite(bitmap, left, top, paint); @@ -2116,15 +2131,13 @@ void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const this->internalDrawBitmapRect(bitmap, src, dst, paint, flags); } -void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap, - const SkIRect& center, const SkRect& dst, - const SkPaint* paint) { - if (bitmap.drawsNothing()) { - return; - } +void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, + const SkPaint* paint) { + TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()"); + + SkRect storage; + const SkRect* bounds = &dst; if (NULL == paint || paint->canComputeFastBounds()) { - SkRect storage; - const SkRect* bounds = &dst; if (paint) { bounds = &paint->computeFastBounds(dst, &storage); } @@ -2132,58 +2145,19 @@ void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap, return; } } - - const int32_t w = bitmap.width(); - const int32_t h = bitmap.height(); - - SkIRect c = center; - // pin center to the bounds of the bitmap - c.fLeft = SkMax32(0, center.fLeft); - c.fTop = SkMax32(0, center.fTop); - c.fRight = SkPin32(center.fRight, c.fLeft, w); - c.fBottom = SkPin32(center.fBottom, c.fTop, h); - - const SkScalar srcX[4] = { - 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w) - }; - const SkScalar srcY[4] = { - 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h) - }; - SkScalar dstX[4] = { - dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft), - dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight - }; - SkScalar dstY[4] = { - dst.fTop, dst.fTop + SkIntToScalar(c.fTop), - dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom - }; - - if (dstX[1] > dstX[2]) { - dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width()); - dstX[2] = dstX[1]; - } - - if (dstY[1] > dstY[2]) { - dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height()); - dstY[2] = dstY[1]; + + SkLazyPaint lazy; + if (NULL == paint) { + paint = lazy.init(); } - - for (int y = 0; y < 3; y++) { - SkRect s, d; - - s.fTop = srcY[y]; - s.fBottom = srcY[y+1]; - d.fTop = dstY[y]; - d.fBottom = dstY[y+1]; - for (int x = 0; x < 3; x++) { - s.fLeft = srcX[x]; - s.fRight = srcX[x+1]; - d.fLeft = dstX[x]; - d.fRight = dstX[x+1]; - this->internalDrawBitmapRect(bitmap, &s, d, paint, - kNone_DrawBitmapRectFlag); - } + + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) + + while (iter.next()) { + iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint()); } + + LOOPER_END } void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, @@ -2191,8 +2165,29 @@ void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, c TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()"); SkDEBUGCODE(bitmap.validate();) - // Need a device entry-point, so gpu can use a mesh - this->internalDrawBitmapNine(bitmap, center, dst, paint); + SkRect storage; + const SkRect* bounds = &dst; + if (NULL == paint || paint->canComputeFastBounds()) { + if (paint) { + bounds = &paint->computeFastBounds(dst, &storage); + } + if (this->quickReject(*bounds)) { + return; + } + } + + SkLazyPaint lazy; + if (NULL == paint) { + paint = lazy.init(); + } + + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) + + while (iter.next()) { + iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint()); + } + + LOOPER_END } class SkDeviceFilteredPaint { diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 8f7f50c863..90161b4eb3 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -11,6 +11,7 @@ #include "SkDrawFilter.h" #include "SkImage_Base.h" #include "SkMetaData.h" +#include "SkNinePatchIter.h" #include "SkPatchUtils.h" #include "SkPathMeasure.h" #include "SkRasterClip.h" @@ -161,6 +162,26 @@ void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const } } +void SkBaseDevice::drawImageNine(const SkDraw& draw, const SkImage* image, const SkIRect& center, + const SkRect& dst, const SkPaint& paint) { + SkNinePatchIter iter(image->width(), image->height(), center, dst); + + SkRect srcR, dstR; + while (iter.next(&srcR, &dstR)) { + this->drawImageRect(draw, image, &srcR, dstR, paint); + } +} + +void SkBaseDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint& paint) { + SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst); + + SkRect srcR, dstR; + while (iter.next(&srcR, &dstR)) { + this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint, SkCanvas::kNone_DrawBitmapRectFlag); + } +} + void SkBaseDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int count, SkXfermode::Mode mode, const SkPaint& paint) { diff --git a/src/core/SkNinePatchIter.cpp b/src/core/SkNinePatchIter.cpp new file mode 100644 index 0000000000..c6bd16dcdb --- /dev/null +++ b/src/core/SkNinePatchIter.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkNinePatchIter.h" +#include "SkRect.h" + +bool SkNinePatchIter::Valid(int width, int height, const SkIRect& center) { + return !center.isEmpty() && SkIRect::MakeWH(width, height).contains(center); +} + +SkNinePatchIter::SkNinePatchIter(int w, int h, const SkIRect& c, const SkRect& dst) { + SkASSERT(SkIRect::MakeWH(w, h).contains(c)); + + fSrcX[0] = 0; + fSrcX[1] = SkIntToScalar(c.fLeft); + fSrcX[2] = SkIntToScalar(c.fRight); + fSrcX[3] = SkIntToScalar(w); + + fSrcY[0] = 0; + fSrcY[1] = SkIntToScalar(c.fTop); + fSrcY[2] = SkIntToScalar(c.fBottom); + fSrcY[3] = SkIntToScalar(h); + + fDstX[0] = dst.fLeft; + fDstX[1] = dst.fLeft + SkIntToScalar(c.fLeft); + fDstX[2] = dst.fRight - SkIntToScalar(w - c.fRight); + fDstX[3] = dst.fRight; + + fDstY[0] = dst.fTop; + fDstY[1] = dst.fTop + SkIntToScalar(c.fTop); + fDstY[2] = dst.fBottom - SkIntToScalar(h - c.fBottom); + fDstY[3] = dst.fBottom; + + if (fDstX[1] > fDstX[2]) { + fDstX[1] = fDstX[0] + (fDstX[3] - fDstX[0]) * c.fLeft / (w - c.width()); + fDstX[2] = fDstX[1]; + } + + if (fDstY[1] > fDstY[2]) { + fDstY[1] = fDstY[0] + (fDstY[3] - fDstY[0]) * c.fTop / (h - c.height()); + fDstY[2] = fDstY[1]; + } + + fCurrX = fCurrY = 0; + fDone = false; +} + +bool SkNinePatchIter::next(SkRect* src, SkRect* dst) { + if (fDone) { + return false; + } + + const int x = fCurrX; + const int y = fCurrY; + SkASSERT(x >= 0 && x < 4); + SkASSERT(y >= 0 && y < 4); + + src->set(fSrcX[x], fSrcY[y], fSrcX[x + 1], fSrcY[y + 1]); + dst->set(fDstX[x], fDstY[y], fDstX[x + 1], fDstY[y + 1]); + if (4 == ++fCurrX) { + fCurrX = 0; + fCurrY += 1; + if (fCurrY >= 4) { + fDone = true; + } + } + return true; +} diff --git a/src/core/SkNinePatchIter.h b/src/core/SkNinePatchIter.h new file mode 100644 index 0000000000..2d7e9a8b19 --- /dev/null +++ b/src/core/SkNinePatchIter.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkNinePatchIter_DEFINED +#define SkNinePatchIter_DEFINED + +#include "SkScalar.h" + +struct SkIRect; +struct SkRect; + +/** + * Disect a ninepatch request into an sequence of src-rect / dst-rect pairs + */ +class SkNinePatchIter { +public: + static bool Valid(int imageWidth, int imageHeight, const SkIRect& center); + + SkNinePatchIter(int imageWidth, int imageHeight, const SkIRect& center, const SkRect& dst); + + /** + * While it returns true, use src/dst to draw the image/bitmap + */ + bool next(SkRect* src, SkRect* dst); + +private: + SkScalar fSrcX[4]; + SkScalar fSrcY[4]; + SkScalar fDstX[4]; + SkScalar fDstY[4]; + + int fCurrX; + int fCurrY; + bool fDone; +}; + +#endif + diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index 07411002ae..d421c47603 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -72,8 +72,9 @@ enum DrawType { DRAW_IMAGE, DRAW_IMAGE_RECT, DRAW_ATLAS, + DRAW_IMAGE_NINE, - LAST_DRAWTYPE_ENUM = DRAW_ATLAS + LAST_DRAWTYPE_ENUM = DRAW_IMAGE_NINE }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index a1bb1ba293..1cf1a1bfb4 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -242,6 +242,13 @@ void SkPicturePlayback::handleOp(SkReader32* reader, const SkPoint& loc = reader->skipT(); canvas->drawImage(image, loc.fX, loc.fY, paint); } break; + case DRAW_IMAGE_NINE: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkImage* image = fPictureData->getImage(reader); + const SkIRect& center = reader->skipT(); + const SkRect& dst = reader->skipT(); + canvas->drawImageNine(image, center, dst, paint); + } break; case DRAW_IMAGE_RECT: { const SkPaint* paint = fPictureData->getPaint(reader); const SkImage* image = fPictureData->getImage(reader); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 0fbf98089a..5f85b47aac 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -101,6 +101,7 @@ static inline size_t get_paint_offset(DrawType op, size_t opSize) { 1, // DRAW_IMAGE - right after op code 1, // DRAW_IMAGE_RECT - right after op code 1, // DRAW_ATLAS - right after op code + 1, // DRAW_IMAGE_NINE - right after op code }; SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1, @@ -584,7 +585,7 @@ void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint) { - // id + paint_index + bitmap_index + bool_for_src + // id + paint_index + image_index + bool_for_src size_t size = 4 * kUInt32Size; if (src) { size += sizeof(*src); // + rect @@ -601,6 +602,20 @@ void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, c this->validate(initialOffset, size); } +void SkPictureRecord::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst, + const SkPaint* paint) { + // id + paint_index + image_index + center + dst + size_t size = 3 * kUInt32Size + sizeof(SkIRect) + sizeof(SkRect); + + size_t initialOffset = this->addDraw(DRAW_IMAGE_NINE, &size); + SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE_NINE, size) == fWriter.bytesWritten()); + this->addPaintPtr(paint); + this->addImage(img); + this->addIRect(center); + this->addRect(dst); + this->validate(initialOffset, size); +} + void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { // op + paint index + bitmap id + center + dst rect diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index cee5ea35f5..e2d5f7d1df 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -188,6 +188,8 @@ protected: void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint*) override; + void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, + 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; diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index 944443c8ee..af71624474 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -97,6 +97,7 @@ DRAW(DrawBitmapRectToRectBleed, DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); DRAW(DrawImage, drawImage(r.image, r.left, r.top, r.paint)); DRAW(DrawImageRect, drawImageRect(r.image, r.src, r.dst, r.paint)); +DRAW(DrawImageNine, drawImageNine(r.image, r.center, r.dst, r.paint)); DRAW(DrawOval, drawOval(r.oval, r.paint)); DRAW(DrawPaint, drawPaint(r.paint)); DRAW(DrawPath, drawPath(r.path, r.paint)); @@ -413,6 +414,9 @@ private: Bounds bounds(const DrawImageRect& op) const { return this->adjustAndMap(op.dst, op.paint); } + Bounds bounds(const DrawImageNine& op) const { + return this->adjustAndMap(op.dst, op.paint); + } Bounds bounds(const DrawBitmapRectToRect& op) const { return this->adjustAndMap(op.dst, op.paint); } diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index 812fd9d49c..c27405575c 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -222,6 +222,11 @@ void SkRecorder::onDrawImageRect(const SkImage* image, const SkRect* src, APPEND(DrawImageRect, this->copy(paint), image, this->copy(src), dst); } +void SkRecorder::onDrawImageNine(const SkImage* image, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + 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), delay_copy(bitmap), left, top); } diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 53c658fa6f..3809ae239d 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -100,6 +100,8 @@ public: void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint*) override; + void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, + 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; diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h index 381f91438b..8e778bf7a2 100644 --- a/src/core/SkRecords.h +++ b/src/core/SkRecords.h @@ -44,6 +44,7 @@ namespace SkRecords { M(DrawDrawable) \ M(DrawImage) \ M(DrawImageRect) \ + M(DrawImageNine) \ M(DrawDRRect) \ M(DrawOval) \ M(DrawPaint) \ @@ -288,6 +289,10 @@ RECORD4(DrawImageRect, Optional, paint, RefBox, image, Optional, src, SkRect, dst); +RECORD4(DrawImageNine, Optional, paint, + RefBox, image, + SkIRect, center, + SkRect, dst); RECORD2(DrawOval, SkPaint, paint, SkRect, oval); RECORD1(DrawPaint, SkPaint, paint); RECORD2(DrawPath, SkPaint, paint, PreCachedPath, path); -- cgit v1.2.3