1/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkThreadedBMPDevice.h"
9
10#include "SkPath.h"
11#include "SkRectPriv.h"
12#include "SkTaskGroup.h"
13#include "SkVertices.h"
14
15void SkThreadedBMPDevice::DrawQueue::reset() {
16    if (fTasks) {
17        fTasks->finish();
18    }
19
20    fSize = 0;
21
22    // using TaskGroup2D = SkSpinningTaskGroup2D;
23    using TaskGroup2D = SkFlexibleTaskGroup2D;
24    auto draw2D = [this](int row, int column){
25        SkThreadedBMPDevice::DrawElement& element = fElements[column];
26        if (!SkIRect::Intersects(fDevice->fTileBounds[row], element.fDrawBounds)) {
27            return;
28        }
29        element.fDrawFn(nullptr, element.fDS, fDevice->fTileBounds[row]);
30    };
31    fTasks.reset(new TaskGroup2D(draw2D, fDevice->fTileCnt, fDevice->fExecutor,
32                                 fDevice->fThreadCnt));
33    fTasks->start();
34}
35
36SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap,
37                                         int tiles,
38                                         int threads,
39                                         SkExecutor* executor)
40        : INHERITED(bitmap)
41        , fTileCnt(tiles)
42        , fThreadCnt(threads <= 0 ? tiles : threads)
43        , fQueue(this)
44{
45    if (executor == nullptr) {
46        fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt);
47        executor = fInternalExecutor.get();
48    }
49    fExecutor = executor;
50
51    // Tiling using stripes for now; we'll explore better tiling in the future.
52    int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
53    int w = bitmap.width();
54    int top = 0;
55    for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
56        fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
57    }
58    fQueue.reset();
59}
60
61void SkThreadedBMPDevice::flush() {
62    fQueue.reset();
63}
64
65SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) {
66    // we need fDst to be set, and if we're actually drawing, to dirty the genID
67    if (!dev->accessPixels(&fDst)) {
68        // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
69        fDst.reset(dev->imageInfo(), nullptr, 0);
70    }
71    fMatrix = dev->ctm();
72    fRC = dev->fRCStack.rc();
73}
74
75SkIRect SkThreadedBMPDevice::transformDrawBounds(const SkRect& drawBounds) const {
76    if (drawBounds == SkRectPriv::MakeLargest()) {
77        return SkRectPriv::MakeILarge();
78    }
79    SkRect transformedBounds;
80    this->ctm().mapRect(&transformedBounds, drawBounds);
81    return transformedBounds.roundOut();
82}
83
84SkDraw SkThreadedBMPDevice::DrawState::getDraw() const {
85    SkDraw draw;
86    draw.fDst = fDst;
87    draw.fMatrix = &fMatrix;
88    draw.fRC = &fRC;
89    return draw;
90}
91
92SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds)
93        : fTileRC(ds.fRC) {
94    fDst = ds.fDst;
95    fMatrix = &ds.fMatrix;
96    fTileRC.op(tileBounds, SkRegion::kIntersect_Op);
97    fRC = &fTileRC;
98}
99
100static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
101    SkRect result;
102    if (p.canComputeFastBounds()) {
103        result = p.computeFastBounds(r, &result);
104    } else {
105        result = SkRectPriv::MakeLargest();
106    }
107    return result;
108}
109
110void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
111    SkRect drawBounds = SkRectPriv::MakeLargest();
112    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
113        TileDraw(ds, tileBounds).drawPaint(paint);
114    });
115}
116
117void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
118        const SkPoint pts[], const SkPaint& paint) {
119    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
120    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
121        TileDraw(ds, tileBounds).drawPoints(mode, count, pts, paint, nullptr);
122    });
123}
124
125void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
126    SkRect drawBounds = get_fast_bounds(r, paint);
127    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
128        TileDraw(ds, tileBounds).drawRect(r, paint);
129    });
130}
131
132void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
133#ifdef SK_IGNORE_BLURRED_RRECT_OPT
134    SkPath  path;
135
136    path.addRRect(rrect);
137    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
138    // required to override drawRRect.
139    this->drawPath(path, paint, nullptr, false);
140#else
141    SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
142    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
143        TileDraw(ds, tileBounds).drawRRect(rrect, paint);
144    });
145#endif
146}
147
148void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
149        const SkMatrix* prePathMatrix, bool pathIsMutable) {
150    SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest()
151                                                 : get_fast_bounds(path.getBounds(), paint);
152    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) {
153        TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false);
154    });
155}
156
157void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
158        const SkPaint& paint) {
159    SkMatrix matrix = SkMatrix::MakeTrans(x, y);
160    LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
161    SkRect drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
162    matrix.mapRect(&drawBounds);
163    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
164        TileDraw(ds, tileBounds).drawBitmap(bitmap, matrix, nullptr, paint);
165    });
166}
167
168void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
169    SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
170    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
171        TileDraw(ds, tileBounds).drawSprite(bitmap, x, y, paint);
172    });
173}
174
175void SkThreadedBMPDevice::drawText(const void* text, size_t len, SkScalar x, SkScalar y,
176        const SkPaint& paint) {
177    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
178    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
179        TileDraw(ds, tileBounds).drawText((const char*)text, len, x, y, paint,
180                                          &this->surfaceProps());
181    });
182}
183
184void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
185        int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
186    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
187    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
188        TileDraw(ds, tileBounds).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset,
189                                             paint, &surfaceProps());
190    });
191}
192
193void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
194        const SkPaint& paint) {
195    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
196    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
197        TileDraw(ds, tileBounds).drawVertices(vertices->mode(), vertices->vertexCount(),
198                                              vertices->positions(), vertices->texCoords(),
199                                              vertices->colors(), bmode, vertices->indices(),
200                                              vertices->indexCount(), paint);
201    });
202}
203
204void SkThreadedBMPDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) {
205    SkASSERT(!paint.getImageFilter());
206    SkRect drawBounds = SkRect::MakeXYWH(x, y, device->width(), device->height());
207    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
208        TileDraw(ds, tileBounds).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap,
209                                            x, y, paint);
210    });
211}
212