123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308 |
- /*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "src/core/SkDraw.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkMatrix.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPathEffect.h"
- #include "include/core/SkRRect.h"
- #include "include/core/SkShader.h"
- #include "include/core/SkString.h"
- #include "include/core/SkStrokeRec.h"
- #include "include/private/SkColorData.h"
- #include "include/private/SkMacros.h"
- #include "include/private/SkTemplates.h"
- #include "include/private/SkTo.h"
- #include "src/core/SkArenaAlloc.h"
- #include "src/core/SkAutoBlitterChoose.h"
- #include "src/core/SkBlendModePriv.h"
- #include "src/core/SkBlitter.h"
- #include "src/core/SkDevice.h"
- #include "src/core/SkDrawProcs.h"
- #include "src/core/SkMaskFilterBase.h"
- #include "src/core/SkMatrixUtils.h"
- #include "src/core/SkPathPriv.h"
- #include "src/core/SkRasterClip.h"
- #include "src/core/SkRectPriv.h"
- #include "src/core/SkScan.h"
- #include "src/core/SkStroke.h"
- #include "src/core/SkTLazy.h"
- #include "src/core/SkUtils.h"
- #include <utility>
- static SkPaint make_paint_with_image(
- const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
- SkPaint paint(origPaint);
- paint.setShader(SkMakeBitmapShaderForPaint(origPaint, bitmap, SkTileMode::kClamp,
- SkTileMode::kClamp, matrix,
- kNever_SkCopyPixelsMode));
- return paint;
- }
- ///////////////////////////////////////////////////////////////////////////////
- SkDraw::SkDraw() {}
- bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
- if (fRC->isEmpty()) {
- return false;
- }
- SkMatrix inverse;
- if (!fMatrix->invert(&inverse)) {
- return false;
- }
- SkIRect devBounds = fRC->getBounds();
- // outset to have slop for antialasing and hairlines
- devBounds.outset(1, 1);
- inverse.mapRect(localBounds, SkRect::Make(devBounds));
- return true;
- }
- ///////////////////////////////////////////////////////////////////////////////
- void SkDraw::drawPaint(const SkPaint& paint) const {
- SkDEBUGCODE(this->validate();)
- if (fRC->isEmpty()) {
- return;
- }
- SkIRect devRect;
- devRect.set(0, 0, fDst.width(), fDst.height());
- SkAutoBlitterChoose blitter(*this, nullptr, paint);
- SkScan::FillIRect(devRect, *fRC, blitter.get());
- }
- ///////////////////////////////////////////////////////////////////////////////
- struct PtProcRec {
- SkCanvas::PointMode fMode;
- const SkPaint* fPaint;
- const SkRegion* fClip;
- const SkRasterClip* fRC;
- // computed values
- SkRect fClipBounds;
- SkScalar fRadius;
- typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
- SkBlitter*);
- bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
- const SkRasterClip*);
- Proc chooseProc(SkBlitter** blitter);
- private:
- SkAAClipBlitterWrapper fWrapper;
- };
- static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- SkASSERT(rec.fClip->isRect());
- const SkIRect& r = rec.fClip->getBounds();
- for (int i = 0; i < count; i++) {
- int x = SkScalarFloorToInt(devPts[i].fX);
- int y = SkScalarFloorToInt(devPts[i].fY);
- if (r.contains(x, y)) {
- blitter->blitH(x, y, 1);
- }
- }
- }
- static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
- const SkPoint devPts[], int count,
- SkBlitter* blitter) {
- SkASSERT(rec.fRC->isRect());
- const SkIRect& r = rec.fRC->getBounds();
- uint32_t value;
- const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
- SkASSERT(dst);
- uint16_t* addr = dst->writable_addr16(0, 0);
- size_t rb = dst->rowBytes();
- for (int i = 0; i < count; i++) {
- int x = SkScalarFloorToInt(devPts[i].fX);
- int y = SkScalarFloorToInt(devPts[i].fY);
- if (r.contains(x, y)) {
- ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
- }
- }
- }
- static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
- const SkPoint devPts[], int count,
- SkBlitter* blitter) {
- SkASSERT(rec.fRC->isRect());
- const SkIRect& r = rec.fRC->getBounds();
- uint32_t value;
- const SkPixmap* dst = blitter->justAnOpaqueColor(&value);
- SkASSERT(dst);
- SkPMColor* addr = dst->writable_addr32(0, 0);
- size_t rb = dst->rowBytes();
- for (int i = 0; i < count; i++) {
- int x = SkScalarFloorToInt(devPts[i].fX);
- int y = SkScalarFloorToInt(devPts[i].fY);
- if (r.contains(x, y)) {
- ((SkPMColor*)((char*)addr + y * rb))[x] = value;
- }
- }
- }
- static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- for (int i = 0; i < count; i++) {
- int x = SkScalarFloorToInt(devPts[i].fX);
- int y = SkScalarFloorToInt(devPts[i].fY);
- if (rec.fClip->contains(x, y)) {
- blitter->blitH(x, y, 1);
- }
- }
- }
- static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- for (int i = 0; i < count; i += 2) {
- SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
- }
- }
- static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- SkScan::HairLine(devPts, count, *rec.fRC, blitter);
- }
- // aa versions
- static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- for (int i = 0; i < count; i += 2) {
- SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
- }
- }
- static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
- }
- // square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
- static SkRect make_square_rad(SkPoint center, SkScalar radius) {
- return {
- center.fX - radius, center.fY - radius,
- center.fX + radius, center.fY + radius
- };
- }
- static SkXRect make_xrect(const SkRect& r) {
- SkASSERT(SkRectPriv::FitsInFixed(r));
- return {
- SkScalarToFixed(r.fLeft), SkScalarToFixed(r.fTop),
- SkScalarToFixed(r.fRight), SkScalarToFixed(r.fBottom)
- };
- }
- static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- for (int i = 0; i < count; i++) {
- SkRect r = make_square_rad(devPts[i], rec.fRadius);
- if (r.intersect(rec.fClipBounds)) {
- SkScan::FillXRect(make_xrect(r), *rec.fRC, blitter);
- }
- }
- }
- static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
- int count, SkBlitter* blitter) {
- for (int i = 0; i < count; i++) {
- SkRect r = make_square_rad(devPts[i], rec.fRadius);
- if (r.intersect(rec.fClipBounds)) {
- SkScan::AntiFillXRect(make_xrect(r), *rec.fRC, blitter);
- }
- }
- }
- // If this guy returns true, then chooseProc() must return a valid proc
- bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
- const SkMatrix* matrix, const SkRasterClip* rc) {
- if ((unsigned)mode > (unsigned)SkCanvas::kPolygon_PointMode) {
- return false;
- }
- if (paint.getPathEffect()) {
- return false;
- }
- SkScalar width = paint.getStrokeWidth();
- SkScalar radius = -1; // sentinel value, a "valid" value must be > 0
- if (0 == width) {
- radius = 0.5f;
- } else if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
- matrix->isScaleTranslate() && SkCanvas::kPoints_PointMode == mode) {
- SkScalar sx = matrix->get(SkMatrix::kMScaleX);
- SkScalar sy = matrix->get(SkMatrix::kMScaleY);
- if (SkScalarNearlyZero(sx - sy)) {
- radius = SkScalarHalf(width * SkScalarAbs(sx));
- }
- }
- if (radius > 0) {
- SkRect clipBounds = SkRect::Make(rc->getBounds());
- // if we return true, the caller may assume that the constructed shapes can be represented
- // using SkFixed (after clipping), so we preflight that here.
- if (!SkRectPriv::FitsInFixed(clipBounds)) {
- return false;
- }
- fMode = mode;
- fPaint = &paint;
- fClip = nullptr;
- fRC = rc;
- fClipBounds = clipBounds;
- fRadius = radius;
- return true;
- }
- return false;
- }
- PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) {
- Proc proc = nullptr;
- SkBlitter* blitter = *blitterPtr;
- if (fRC->isBW()) {
- fClip = &fRC->bwRgn();
- } else {
- fWrapper.init(*fRC, blitter);
- fClip = &fWrapper.getRgn();
- blitter = fWrapper.getBlitter();
- *blitterPtr = blitter;
- }
- // for our arrays
- SkASSERT(0 == SkCanvas::kPoints_PointMode);
- SkASSERT(1 == SkCanvas::kLines_PointMode);
- SkASSERT(2 == SkCanvas::kPolygon_PointMode);
- SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
- if (fPaint->isAntiAlias()) {
- if (0 == fPaint->getStrokeWidth()) {
- static const Proc gAAProcs[] = {
- aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
- };
- proc = gAAProcs[fMode];
- } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
- SkASSERT(SkCanvas::kPoints_PointMode == fMode);
- proc = aa_square_proc;
- }
- } else { // BW
- if (fRadius <= 0.5f) { // small radii and hairline
- if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
- uint32_t value;
- const SkPixmap* bm = blitter->justAnOpaqueColor(&value);
- if (bm && kRGB_565_SkColorType == bm->colorType()) {
- proc = bw_pt_rect_16_hair_proc;
- } else if (bm && kN32_SkColorType == bm->colorType()) {
- proc = bw_pt_rect_32_hair_proc;
- } else {
- proc = bw_pt_rect_hair_proc;
- }
- } else {
- static Proc gBWProcs[] = {
- bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
- };
- proc = gBWProcs[fMode];
- }
- } else {
- proc = bw_square_proc;
- }
- }
- return proc;
- }
- // each of these costs 8-bytes of stack space, so don't make it too large
- // must be even for lines/polygon to work
- #define MAX_DEV_PTS 32
- void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
- const SkPoint pts[], const SkPaint& paint,
- SkBaseDevice* device) const {
- // if we're in lines mode, force count to be even
- if (SkCanvas::kLines_PointMode == mode) {
- count &= ~(size_t)1;
- }
- if ((long)count <= 0) {
- return;
- }
- SkASSERT(pts != nullptr);
- SkDEBUGCODE(this->validate();)
- // nothing to draw
- if (fRC->isEmpty()) {
- return;
- }
- PtProcRec rec;
- if (!device && rec.init(mode, paint, fMatrix, fRC)) {
- SkAutoBlitterChoose blitter(*this, nullptr, paint);
- SkPoint devPts[MAX_DEV_PTS];
- const SkMatrix* matrix = fMatrix;
- SkBlitter* bltr = blitter.get();
- PtProcRec::Proc proc = rec.chooseProc(&bltr);
- // we have to back up subsequent passes if we're in polygon mode
- const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
- do {
- int n = SkToInt(count);
- if (n > MAX_DEV_PTS) {
- n = MAX_DEV_PTS;
- }
- matrix->mapPoints(devPts, pts, n);
- if (!SkScalarsAreFinite(&devPts[0].fX, n * 2)) {
- return;
- }
- proc(rec, devPts, n, bltr);
- pts += n - backup;
- SkASSERT(SkToInt(count) >= n);
- count -= n;
- if (count > 0) {
- count += backup;
- }
- } while (count != 0);
- } else {
- switch (mode) {
- case SkCanvas::kPoints_PointMode: {
- // temporarily mark the paint as filling.
- SkPaint newPaint(paint);
- newPaint.setStyle(SkPaint::kFill_Style);
- SkScalar width = newPaint.getStrokeWidth();
- SkScalar radius = SkScalarHalf(width);
- if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
- if (device) {
- for (size_t i = 0; i < count; ++i) {
- SkRect r = SkRect::MakeLTRB(pts[i].fX - radius, pts[i].fY - radius,
- pts[i].fX + radius, pts[i].fY + radius);
- device->drawOval(r, newPaint);
- }
- } else {
- SkPath path;
- SkMatrix preMatrix;
- path.addCircle(0, 0, radius);
- for (size_t i = 0; i < count; i++) {
- preMatrix.setTranslate(pts[i].fX, pts[i].fY);
- // pass true for the last point, since we can modify
- // then path then
- path.setIsVolatile((count-1) == i);
- this->drawPath(path, newPaint, &preMatrix, (count-1) == i);
- }
- }
- } else {
- SkRect r;
- for (size_t i = 0; i < count; i++) {
- r.fLeft = pts[i].fX - radius;
- r.fTop = pts[i].fY - radius;
- r.fRight = r.fLeft + width;
- r.fBottom = r.fTop + width;
- if (device) {
- device->drawRect(r, newPaint);
- } else {
- this->drawRect(r, newPaint);
- }
- }
- }
- break;
- }
- case SkCanvas::kLines_PointMode:
- if (2 == count && paint.getPathEffect()) {
- // most likely a dashed line - see if it is one of the ones
- // we can accelerate
- SkStrokeRec rec(paint);
- SkPathEffect::PointData pointData;
- SkPath path;
- path.moveTo(pts[0]);
- path.lineTo(pts[1]);
- SkRect cullRect = SkRect::Make(fRC->getBounds());
- if (paint.getPathEffect()->asPoints(&pointData, path, rec,
- *fMatrix, &cullRect)) {
- // 'asPoints' managed to find some fast path
- SkPaint newP(paint);
- newP.setPathEffect(nullptr);
- newP.setStyle(SkPaint::kFill_Style);
- if (!pointData.fFirst.isEmpty()) {
- if (device) {
- device->drawPath(pointData.fFirst, newP);
- } else {
- this->drawPath(pointData.fFirst, newP);
- }
- }
- if (!pointData.fLast.isEmpty()) {
- if (device) {
- device->drawPath(pointData.fLast, newP);
- } else {
- this->drawPath(pointData.fLast, newP);
- }
- }
- if (pointData.fSize.fX == pointData.fSize.fY) {
- // The rest of the dashed line can just be drawn as points
- SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
- if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
- newP.setStrokeCap(SkPaint::kRound_Cap);
- } else {
- newP.setStrokeCap(SkPaint::kButt_Cap);
- }
- if (device) {
- device->drawPoints(SkCanvas::kPoints_PointMode,
- pointData.fNumPoints,
- pointData.fPoints,
- newP);
- } else {
- this->drawPoints(SkCanvas::kPoints_PointMode,
- pointData.fNumPoints,
- pointData.fPoints,
- newP,
- device);
- }
- break;
- } else {
- // The rest of the dashed line must be drawn as rects
- SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
- pointData.fFlags));
- SkRect r;
- for (int i = 0; i < pointData.fNumPoints; ++i) {
- r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
- pointData.fPoints[i].fY - pointData.fSize.fY,
- pointData.fPoints[i].fX + pointData.fSize.fX,
- pointData.fPoints[i].fY + pointData.fSize.fY);
- if (device) {
- device->drawRect(r, newP);
- } else {
- this->drawRect(r, newP);
- }
- }
- }
- break;
- }
- }
- // couldn't take fast path so fall through!
- case SkCanvas::kPolygon_PointMode: {
- count -= 1;
- SkPath path;
- SkPaint p(paint);
- p.setStyle(SkPaint::kStroke_Style);
- size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
- path.setIsVolatile(true);
- for (size_t i = 0; i < count; i += inc) {
- path.moveTo(pts[i]);
- path.lineTo(pts[i+1]);
- if (device) {
- device->drawPath(path, p, true);
- } else {
- this->drawPath(path, p, nullptr, true);
- }
- path.rewind();
- }
- break;
- }
- }
- }
- }
- static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
- SkASSERT(matrix.rectStaysRect());
- SkASSERT(SkPaint::kFill_Style != paint.getStyle());
- SkVector size;
- SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
- matrix.mapVectors(&size, &pt, 1);
- return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
- }
- static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
- SkPoint* strokeSize) {
- if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
- paint.getStrokeMiter() < SK_ScalarSqrt2) {
- return false;
- }
- *strokeSize = compute_stroke_size(paint, matrix);
- return true;
- }
- SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
- const SkMatrix& matrix,
- SkPoint* strokeSize) {
- RectType rtype;
- const SkScalar width = paint.getStrokeWidth();
- const bool zeroWidth = (0 == width);
- SkPaint::Style style = paint.getStyle();
- if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
- style = SkPaint::kFill_Style;
- }
- if (paint.getPathEffect() || paint.getMaskFilter() ||
- !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) {
- rtype = kPath_RectType;
- } else if (SkPaint::kFill_Style == style) {
- rtype = kFill_RectType;
- } else if (zeroWidth) {
- rtype = kHair_RectType;
- } else if (easy_rect_join(paint, matrix, strokeSize)) {
- rtype = kStroke_RectType;
- } else {
- rtype = kPath_RectType;
- }
- return rtype;
- }
- static const SkPoint* rect_points(const SkRect& r) {
- return reinterpret_cast<const SkPoint*>(&r);
- }
- static SkPoint* rect_points(SkRect& r) {
- return reinterpret_cast<SkPoint*>(&r);
- }
- static void draw_rect_as_path(const SkDraw& orig, const SkRect& prePaintRect,
- const SkPaint& paint, const SkMatrix* matrix) {
- SkDraw draw(orig);
- draw.fMatrix = matrix;
- SkPath tmp;
- tmp.addRect(prePaintRect);
- tmp.setFillType(SkPath::kWinding_FillType);
- draw.drawPath(tmp, paint, nullptr, true);
- }
- void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint,
- const SkMatrix* paintMatrix, const SkRect* postPaintRect) const {
- SkDEBUGCODE(this->validate();)
- // nothing to draw
- if (fRC->isEmpty()) {
- return;
- }
- const SkMatrix* matrix;
- SkMatrix combinedMatrixStorage;
- if (paintMatrix) {
- SkASSERT(postPaintRect);
- combinedMatrixStorage.setConcat(*fMatrix, *paintMatrix);
- matrix = &combinedMatrixStorage;
- } else {
- SkASSERT(!postPaintRect);
- matrix = fMatrix;
- }
- SkPoint strokeSize;
- RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
- if (kPath_RectType == rtype) {
- draw_rect_as_path(*this, prePaintRect, paint, matrix);
- return;
- }
- SkRect devRect;
- const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
- // skip the paintMatrix when transforming the rect by the CTM
- fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
- devRect.sort();
- // look for the quick exit, before we build a blitter
- SkRect bbox = devRect;
- if (paint.getStyle() != SkPaint::kFill_Style) {
- // extra space for hairlines
- if (paint.getStrokeWidth() == 0) {
- bbox.outset(1, 1);
- } else {
- // For kStroke_RectType, strokeSize is already computed.
- const SkPoint& ssize = (kStroke_RectType == rtype)
- ? strokeSize
- : compute_stroke_size(paint, *fMatrix);
- bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
- }
- }
- if (SkPathPriv::TooBigForMath(bbox)) {
- return;
- }
- if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
- draw_rect_as_path(*this, prePaintRect, paint, matrix);
- return;
- }
- SkIRect ir = bbox.roundOut();
- if (fRC->quickReject(ir)) {
- return;
- }
- SkAutoBlitterChoose blitterStorage(*this, matrix, paint);
- const SkRasterClip& clip = *fRC;
- SkBlitter* blitter = blitterStorage.get();
- // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
- // case we are also hairline (if we've gotten to here), which devolves to
- // effectively just kFill
- switch (rtype) {
- case kFill_RectType:
- if (paint.isAntiAlias()) {
- SkScan::AntiFillRect(devRect, clip, blitter);
- } else {
- SkScan::FillRect(devRect, clip, blitter);
- }
- break;
- case kStroke_RectType:
- if (paint.isAntiAlias()) {
- SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
- } else {
- SkScan::FrameRect(devRect, strokeSize, clip, blitter);
- }
- break;
- case kHair_RectType:
- if (paint.isAntiAlias()) {
- SkScan::AntiHairRect(devRect, clip, blitter);
- } else {
- SkScan::HairRect(devRect, clip, blitter);
- }
- break;
- default:
- SkDEBUGFAIL("bad rtype");
- }
- }
- void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
- if (srcM.fBounds.isEmpty()) {
- return;
- }
- const SkMask* mask = &srcM;
- SkMask dstM;
- if (paint.getMaskFilter() &&
- as_MFB(paint.getMaskFilter())->filterMask(&dstM, srcM, *fMatrix, nullptr)) {
- mask = &dstM;
- }
- SkAutoMaskFreeImage ami(dstM.fImage);
- SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
- SkBlitter* blitter = blitterChooser.get();
- SkAAClipBlitterWrapper wrapper;
- const SkRegion* clipRgn;
- if (fRC->isBW()) {
- clipRgn = &fRC->bwRgn();
- } else {
- wrapper.init(*fRC, blitter);
- clipRgn = &wrapper.getRgn();
- blitter = wrapper.getBlitter();
- }
- blitter->blitMaskRegion(*mask, *clipRgn);
- }
- static SkScalar fast_len(const SkVector& vec) {
- SkScalar x = SkScalarAbs(vec.fX);
- SkScalar y = SkScalarAbs(vec.fY);
- if (x < y) {
- using std::swap;
- swap(x, y);
- }
- return x + SkScalarHalf(y);
- }
- bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
- SkScalar* coverage) {
- SkASSERT(strokeWidth > 0);
- // We need to try to fake a thick-stroke with a modulated hairline.
- if (matrix.hasPerspective()) {
- return false;
- }
- SkVector src[2], dst[2];
- src[0].set(strokeWidth, 0);
- src[1].set(0, strokeWidth);
- matrix.mapVectors(dst, src, 2);
- SkScalar len0 = fast_len(dst[0]);
- SkScalar len1 = fast_len(dst[1]);
- if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
- if (coverage) {
- *coverage = SkScalarAve(len0, len1);
- }
- return true;
- }
- return false;
- }
- void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
- SkDEBUGCODE(this->validate());
- if (fRC->isEmpty()) {
- return;
- }
- {
- // TODO: Investigate optimizing these options. They are in the same
- // order as SkDraw::drawPath, which handles each case. It may be
- // that there is no way to optimize for these using the SkRRect path.
- SkScalar coverage;
- if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
- goto DRAW_PATH;
- }
- if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
- goto DRAW_PATH;
- }
- }
- if (paint.getMaskFilter()) {
- // Transform the rrect into device space.
- SkRRect devRRect;
- if (rrect.transform(*fMatrix, &devRRect)) {
- SkAutoBlitterChoose blitter(*this, nullptr, paint);
- if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fMatrix,
- *fRC, blitter.get())) {
- return; // filterRRect() called the blitter, so we're done
- }
- }
- }
- DRAW_PATH:
- // Now fall back to the default case of using a path.
- SkPath path;
- path.addRRect(rrect);
- this->drawPath(path, paint, nullptr, true);
- }
- SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
- // Not sure how to handle perspective differently, so we just don't try (yet)
- SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
- SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]);
- if (SkScalarsAreFinite(sx, sy)) {
- SkScalar scale = SkTMax(sx, sy);
- if (scale > 0) {
- return scale;
- }
- }
- return 1;
- }
- void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
- SkBlitter* customBlitter, bool doFill) const {
- if (SkPathPriv::TooBigForMath(devPath)) {
- return;
- }
- SkBlitter* blitter = nullptr;
- SkAutoBlitterChoose blitterStorage;
- if (nullptr == customBlitter) {
- blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
- } else {
- blitter = customBlitter;
- }
- if (paint.getMaskFilter()) {
- SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
- : SkStrokeRec::kHairline_InitStyle;
- if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
- return; // filterPath() called the blitter, so we're done
- }
- }
- void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*);
- if (doFill) {
- if (paint.isAntiAlias()) {
- proc = SkScan::AntiFillPath;
- } else {
- proc = SkScan::FillPath;
- }
- } else { // hairline
- if (paint.isAntiAlias()) {
- switch (paint.getStrokeCap()) {
- case SkPaint::kButt_Cap:
- proc = SkScan::AntiHairPath;
- break;
- case SkPaint::kSquare_Cap:
- proc = SkScan::AntiHairSquarePath;
- break;
- case SkPaint::kRound_Cap:
- proc = SkScan::AntiHairRoundPath;
- break;
- default:
- proc SK_INIT_TO_AVOID_WARNING;
- SkDEBUGFAIL("unknown paint cap type");
- }
- } else {
- switch (paint.getStrokeCap()) {
- case SkPaint::kButt_Cap:
- proc = SkScan::HairPath;
- break;
- case SkPaint::kSquare_Cap:
- proc = SkScan::HairSquarePath;
- break;
- case SkPaint::kRound_Cap:
- proc = SkScan::HairRoundPath;
- break;
- default:
- proc SK_INIT_TO_AVOID_WARNING;
- SkDEBUGFAIL("unknown paint cap type");
- }
- }
- }
- proc(devPath, *fRC, blitter);
- }
- void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
- const SkMatrix* prePathMatrix, bool pathIsMutable,
- bool drawCoverage, SkBlitter* customBlitter) const {
- SkDEBUGCODE(this->validate();)
- // nothing to draw
- if (fRC->isEmpty()) {
- return;
- }
- SkPath* pathPtr = (SkPath*)&origSrcPath;
- bool doFill = true;
- SkPath tmpPathStorage;
- SkPath* tmpPath = &tmpPathStorage;
- SkMatrix tmpMatrix;
- const SkMatrix* matrix = fMatrix;
- tmpPath->setIsVolatile(true);
- if (prePathMatrix) {
- if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) {
- SkPath* result = pathPtr;
- if (!pathIsMutable) {
- result = tmpPath;
- pathIsMutable = true;
- }
- pathPtr->transform(*prePathMatrix, result);
- pathPtr = result;
- } else {
- tmpMatrix.setConcat(*matrix, *prePathMatrix);
- matrix = &tmpMatrix;
- }
- }
- // at this point we're done with prePathMatrix
- SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
- SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
- {
- SkScalar coverage;
- if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
- if (SK_Scalar1 == coverage) {
- paint.writable()->setStrokeWidth(0);
- } else if (SkBlendMode_SupportsCoverageAsAlpha(origPaint.getBlendMode())) {
- U8CPU newAlpha;
- #if 0
- newAlpha = SkToU8(SkScalarRoundToInt(coverage *
- origPaint.getAlpha()));
- #else
- // this is the old technique, which we preserve for now so
- // we don't change previous results (testing)
- // the new way seems fine, its just (a tiny bit) different
- int scale = (int)(coverage * 256);
- newAlpha = origPaint.getAlpha() * scale >> 8;
- #endif
- SkPaint* writablePaint = paint.writable();
- writablePaint->setStrokeWidth(0);
- writablePaint->setAlpha(newAlpha);
- }
- }
- }
- if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
- SkRect cullRect;
- const SkRect* cullRectPtr = nullptr;
- if (this->computeConservativeLocalClipBounds(&cullRect)) {
- cullRectPtr = &cullRect;
- }
- doFill = paint->getFillPath(*pathPtr, tmpPath, cullRectPtr,
- ComputeResScaleForStroking(*fMatrix));
- pathPtr = tmpPath;
- }
- // avoid possibly allocating a new path in transform if we can
- SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath;
- // transform the path into device space
- pathPtr->transform(*matrix, devPathPtr);
- this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
- }
- void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {
- SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
- // nothing to draw
- if (fRC->isEmpty()) {
- return;
- }
- if (SkTreatAsSprite(*fMatrix, bitmap.dimensions(), paint)) {
- int ix = SkScalarRoundToInt(fMatrix->getTranslateX());
- int iy = SkScalarRoundToInt(fMatrix->getTranslateY());
- SkPixmap pmap;
- if (!bitmap.peekPixels(&pmap)) {
- return;
- }
- SkMask mask;
- mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height());
- mask.fFormat = SkMask::kA8_Format;
- mask.fRowBytes = SkToU32(pmap.rowBytes());
- // fImage is typed as writable, but in this case it is used read-only
- mask.fImage = (uint8_t*)pmap.addr8(0, 0);
- this->drawDevMask(mask, paint);
- } else { // need to xform the bitmap first
- SkRect r;
- SkMask mask;
- r.set(0, 0,
- SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
- fMatrix->mapRect(&r);
- r.round(&mask.fBounds);
- // set the mask's bounds to the transformed bitmap-bounds,
- // clipped to the actual device and further limited by the clip bounds
- {
- SkASSERT(fDst.bounds().contains(fRC->getBounds()));
- SkIRect devBounds = fDst.bounds();
- devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
- // need intersect(l, t, r, b) on irect
- if (!mask.fBounds.intersect(devBounds)) {
- return;
- }
- }
- mask.fFormat = SkMask::kA8_Format;
- mask.fRowBytes = SkAlign4(mask.fBounds.width());
- size_t size = mask.computeImageSize();
- if (0 == size) {
- // the mask is too big to allocated, draw nothing
- return;
- }
- // allocate (and clear) our temp buffer to hold the transformed bitmap
- SkAutoTMalloc<uint8_t> storage(size);
- mask.fImage = storage.get();
- memset(mask.fImage, 0, size);
- // now draw our bitmap(src) into mask(dst), transformed by the matrix
- {
- SkBitmap device;
- device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
- mask.fImage, mask.fRowBytes);
- SkCanvas c(device);
- // need the unclipped top/left for the translate
- c.translate(-SkIntToScalar(mask.fBounds.fLeft),
- -SkIntToScalar(mask.fBounds.fTop));
- c.concat(*fMatrix);
- // We can't call drawBitmap, or we'll infinitely recurse. Instead
- // we manually build a shader and draw that into our new mask
- SkPaint tmpPaint;
- tmpPaint.setAntiAlias(paint.isAntiAlias());
- tmpPaint.setDither(paint.isDither());
- tmpPaint.setFilterQuality(paint.getFilterQuality());
- SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap);
- SkRect rr;
- rr.set(0, 0, SkIntToScalar(bitmap.width()),
- SkIntToScalar(bitmap.height()));
- c.drawRect(rr, paintWithShader);
- }
- this->drawDevMask(mask, paint);
- }
- }
- static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
- const SkRect& srcR) {
- SkRect dstR;
- m.mapRect(&dstR, srcR);
- return c.quickReject(dstR.roundOut());
- }
- static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
- int width, int height) {
- SkRect r;
- r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
- return clipped_out(matrix, clip, r);
- }
- static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
- return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
- }
- void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
- const SkRect* dstBounds, const SkPaint& origPaint) const {
- SkDEBUGCODE(this->validate();)
- // nothing to draw
- if (fRC->isEmpty() ||
- bitmap.width() == 0 || bitmap.height() == 0 ||
- bitmap.colorType() == kUnknown_SkColorType) {
- return;
- }
- SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
- if (origPaint.getStyle() != SkPaint::kFill_Style) {
- paint.writable()->setStyle(SkPaint::kFill_Style);
- }
- SkMatrix matrix;
- matrix.setConcat(*fMatrix, prematrix);
- if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
- return;
- }
- if (bitmap.colorType() != kAlpha_8_SkColorType
- && SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
- //
- // It is safe to call lock pixels now, since we know the matrix is
- // (more or less) identity.
- //
- SkPixmap pmap;
- if (!bitmap.peekPixels(&pmap)) {
- return;
- }
- int ix = SkScalarRoundToInt(matrix.getTranslateX());
- int iy = SkScalarRoundToInt(matrix.getTranslateY());
- if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
- SkSTArenaAlloc<kSkBlitterContextSize> allocator;
- // blitter will be owned by the allocator.
- SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator);
- if (blitter) {
- SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
- *fRC, blitter);
- return;
- }
- // if !blitter, then we fall-through to the slower case
- }
- }
- // now make a temp draw on the stack, and use it
- //
- SkDraw draw(*this);
- draw.fMatrix = &matrix;
- if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
- draw.drawBitmapAsMask(bitmap, *paint);
- } else {
- SkPaint paintWithShader = make_paint_with_image(*paint, bitmap);
- const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
- if (dstBounds) {
- this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
- } else {
- draw.drawRect(srcBounds, paintWithShader);
- }
- }
- }
- void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const {
- SkDEBUGCODE(this->validate();)
- // nothing to draw
- if (fRC->isEmpty() ||
- bitmap.width() == 0 || bitmap.height() == 0 ||
- bitmap.colorType() == kUnknown_SkColorType) {
- return;
- }
- const SkIRect bounds = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
- if (fRC->quickReject(bounds)) {
- return; // nothing to draw
- }
- SkPaint paint(origPaint);
- paint.setStyle(SkPaint::kFill_Style);
- SkPixmap pmap;
- if (!bitmap.peekPixels(&pmap)) {
- return;
- }
- if (nullptr == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, pmap)) {
- // blitter will be owned by the allocator.
- SkSTArenaAlloc<kSkBlitterContextSize> allocator;
- SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, paint, pmap, x, y, &allocator);
- if (blitter) {
- SkScan::FillIRect(bounds, *fRC, blitter);
- return;
- }
- }
- SkMatrix matrix;
- SkRect r;
- // get a scalar version of our rect
- r.set(bounds);
- // create shader with offset
- matrix.setTranslate(r.fLeft, r.fTop);
- SkPaint paintWithShader = make_paint_with_image(paint, bitmap, &matrix);
- SkDraw draw(*this);
- matrix.reset();
- draw.fMatrix = &matrix;
- // call ourself with a rect
- draw.drawRect(r, paintWithShader);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- #ifdef SK_DEBUG
- void SkDraw::validate() const {
- SkASSERT(fMatrix != nullptr);
- SkASSERT(fRC != nullptr);
- const SkIRect& cr = fRC->getBounds();
- SkIRect br;
- br.set(0, 0, fDst.width(), fDst.height());
- SkASSERT(cr.isEmpty() || br.contains(cr));
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////////////////////
- #include "include/core/SkPath.h"
- #include "include/core/SkRegion.h"
- #include "src/core/SkBlitter.h"
- #include "src/core/SkDraw.h"
- bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
- const SkMaskFilter* filter, const SkMatrix* filterMatrix,
- SkIRect* bounds) {
- // init our bounds from the path
- *bounds = devPathBounds.makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
- SkIPoint margin = SkIPoint::Make(0, 0);
- if (filter) {
- SkASSERT(filterMatrix);
- SkMask srcM, dstM;
- srcM.fBounds = *bounds;
- srcM.fFormat = SkMask::kA8_Format;
- if (!as_MFB(filter)->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
- return false;
- }
- }
- // (possibly) trim the bounds to reflect the clip
- // (plus whatever slop the filter needs)
- if (clipBounds) {
- // Ugh. Guard against gigantic margins from wacky filters. Without this
- // check we can request arbitrary amounts of slop beyond our visible
- // clip, and bring down the renderer (at least on finite RAM machines
- // like handsets, etc.). Need to balance this invented value between
- // quality of large filters like blurs, and the corresponding memory
- // requests.
- static const int MAX_MARGIN = 128;
- if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
- SkMin32(margin.fY, MAX_MARGIN)))) {
- return false;
- }
- }
- return true;
- }
- static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
- SkStrokeRec::InitStyle style) {
- SkDraw draw;
- if (!draw.fDst.reset(mask)) {
- return;
- }
- SkRasterClip clip;
- SkMatrix matrix;
- SkPaint paint;
- clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height()));
- matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
- -SkIntToScalar(mask.fBounds.fTop));
- draw.fRC = &clip;
- draw.fMatrix = &matrix;
- paint.setAntiAlias(true);
- switch (style) {
- case SkStrokeRec::kHairline_InitStyle:
- SkASSERT(!paint.getStrokeWidth());
- paint.setStyle(SkPaint::kStroke_Style);
- break;
- case SkStrokeRec::kFill_InitStyle:
- SkASSERT(paint.getStyle() == SkPaint::kFill_Style);
- break;
- }
- draw.drawPath(devPath, paint);
- }
- bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
- const SkMaskFilter* filter, const SkMatrix* filterMatrix,
- SkMask* mask, SkMask::CreateMode mode,
- SkStrokeRec::InitStyle style) {
- if (devPath.isEmpty()) {
- return false;
- }
- if (SkMask::kJustRenderImage_CreateMode != mode) {
- if (!ComputeMaskBounds(devPath.getBounds(), clipBounds, filter,
- filterMatrix, &mask->fBounds))
- return false;
- }
- if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
- mask->fFormat = SkMask::kA8_Format;
- mask->fRowBytes = mask->fBounds.width();
- size_t size = mask->computeImageSize();
- if (0 == size) {
- // we're too big to allocate the mask, abort
- return false;
- }
- mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
- }
- if (SkMask::kJustComputeBounds_CreateMode != mode) {
- draw_into_mask(*mask, devPath, style);
- }
- return true;
- }
|