Home | History | Annotate | Download | only in core
      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 
     15 // Calling init(j, k) would initialize the j-th element on k-th thread. It returns false if it's
     16 // already initiailized.
     17 bool SkThreadedBMPDevice::DrawQueue::initColumn(int column, int thread) {
     18     return fElements[column].tryInitOnce(&fThreadAllocs[thread]);
     19 }
     20 
     21 // Calling work(i, j, k) would draw j-th element the i-th tile on k-th thead. If the element still
     22 // needs to be initialized, drawFn will return false without drawing.
     23 bool SkThreadedBMPDevice::DrawQueue::work2D(int row, int column, int thread) {
     24     return fElements[column].tryDraw(fDevice->fTileBounds[row], &fThreadAllocs[thread]);
     25 }
     26 
     27 void SkThreadedBMPDevice::DrawQueue::reset() {
     28     if (fTasks) {
     29         fTasks->finish();
     30     }
     31 
     32     fThreadAllocs.reset(fDevice->fThreadCnt);
     33     fSize = 0;
     34 
     35     // using TaskGroup2D = SkSpinningTaskGroup2D;
     36     using TaskGroup2D = SkFlexibleTaskGroup2D;
     37 
     38     fTasks.reset(new TaskGroup2D(this, fDevice->fTileCnt, fDevice->fExecutor,
     39                                  fDevice->fThreadCnt));
     40     fTasks->start();
     41 }
     42 
     43 SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap,
     44                                          int tiles,
     45                                          int threads,
     46                                          SkExecutor* executor)
     47         : INHERITED(bitmap)
     48         , fTileCnt(tiles)
     49         , fThreadCnt(threads <= 0 ? tiles : threads)
     50         , fQueue(this)
     51 {
     52     if (executor == nullptr) {
     53         fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt);
     54         executor = fInternalExecutor.get();
     55     }
     56     fExecutor = executor;
     57 
     58     // Tiling using stripes for now; we'll explore better tiling in the future.
     59     int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
     60     int w = bitmap.width();
     61     int top = 0;
     62     for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
     63         fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
     64     }
     65     fQueue.reset();
     66 }
     67 
     68 void SkThreadedBMPDevice::flush() {
     69     fQueue.reset();
     70     fAlloc.reset();
     71 }
     72 
     73 SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) {
     74     // we need fDst to be set, and if we're actually drawing, to dirty the genID
     75     if (!dev->accessPixels(&fDst)) {
     76         // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
     77         fDst.reset(dev->imageInfo(), nullptr, 0);
     78     }
     79     fMatrix = dev->ctm();
     80     fRC = dev->fRCStack.rc();
     81 }
     82 
     83 SkIRect SkThreadedBMPDevice::transformDrawBounds(const SkRect& drawBounds) const {
     84     if (drawBounds == SkRectPriv::MakeLargest()) {
     85         return SkRectPriv::MakeILarge();
     86     }
     87     SkRect transformedBounds;
     88     this->ctm().mapRect(&transformedBounds, drawBounds);
     89     return transformedBounds.roundOut();
     90 }
     91 
     92 SkDraw SkThreadedBMPDevice::DrawState::getDraw() const {
     93     SkDraw draw;
     94     draw.fDst = fDst;
     95     draw.fMatrix = &fMatrix;
     96     draw.fRC = &fRC;
     97     return draw;
     98 }
     99 
    100 SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds)
    101         : fTileRC(ds.fRC) {
    102     fDst = ds.fDst;
    103     fMatrix = &ds.fMatrix;
    104     fTileRC.op(tileBounds, SkRegion::kIntersect_Op);
    105     fRC = &fTileRC;
    106 }
    107 
    108 static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
    109     SkRect result;
    110     if (p.canComputeFastBounds()) {
    111         result = p.computeFastBounds(r, &result);
    112     } else {
    113         result = SkRectPriv::MakeLargest();
    114     }
    115     return result;
    116 }
    117 
    118 void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
    119     SkRect drawBounds = SkRectPriv::MakeLargest();
    120     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    121         TileDraw(ds, tileBounds).drawPaint(paint);
    122     });
    123 }
    124 
    125 void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
    126         const SkPoint pts[], const SkPaint& paint) {
    127     SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    128     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    129         TileDraw(ds, tileBounds).drawPoints(mode, count, pts, paint, nullptr);
    130     });
    131 }
    132 
    133 void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
    134     SkRect drawBounds = get_fast_bounds(r, paint);
    135     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    136         TileDraw(ds, tileBounds).drawRect(r, paint);
    137     });
    138 }
    139 
    140 void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
    141 #ifdef SK_IGNORE_BLURRED_RRECT_OPT
    142     SkPath  path;
    143 
    144     path.addRRect(rrect);
    145     // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
    146     // required to override drawRRect.
    147     this->drawPath(path, paint, nullptr, false);
    148 #else
    149     SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
    150     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    151         TileDraw(ds, tileBounds).drawRRect(rrect, paint);
    152     });
    153 #endif
    154 }
    155 
    156 void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
    157         const SkMatrix* prePathMatrix, bool pathIsMutable) {
    158     SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest()
    159                                                  : get_fast_bounds(path.getBounds(), paint);
    160     if (path.countVerbs() < 4) { // when path is small, init-once has too much overhead
    161         fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) {
    162             TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false);
    163         });
    164     } else {
    165         fQueue.push(drawBounds, [=](SkArenaAlloc* alloc, DrawElement* elem) {
    166             SkInitOnceData data = {alloc, elem};
    167             elem->getDraw().drawPath(path, paint, prePathMatrix, false, false, nullptr, &data);
    168         });
    169     }
    170 }
    171 
    172 void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
    173         const SkPaint& paint) {
    174     SkMatrix matrix = SkMatrix::MakeTrans(x, y);
    175     LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
    176     SkRect drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
    177     matrix.mapRect(&drawBounds);
    178     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    179         TileDraw(ds, tileBounds).drawBitmap(bitmap, matrix, nullptr, paint);
    180     });
    181 }
    182 
    183 void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
    184     SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
    185     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    186         TileDraw(ds, tileBounds).drawSprite(bitmap, x, y, paint);
    187     });
    188 }
    189 
    190 void SkThreadedBMPDevice::drawText(const void* text, size_t len, SkScalar x, SkScalar y,
    191         const SkPaint& paint) {
    192     SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    193     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    194         TileDraw(ds, tileBounds).drawText((const char*)text, len, x, y, paint,
    195                                           &this->surfaceProps());
    196     });
    197 }
    198 
    199 void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
    200         int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
    201     SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    202     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    203         TileDraw(ds, tileBounds).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset,
    204                                              paint, &surfaceProps());
    205     });
    206 }
    207 
    208 void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
    209         const SkPaint& paint) {
    210     SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    211     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    212         TileDraw(ds, tileBounds).drawVertices(vertices->mode(), vertices->vertexCount(),
    213                                               vertices->positions(), vertices->texCoords(),
    214                                               vertices->colors(), bmode, vertices->indices(),
    215                                               vertices->indexCount(), paint);
    216     });
    217 }
    218 
    219 void SkThreadedBMPDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) {
    220     SkASSERT(!paint.getImageFilter());
    221     SkRect drawBounds = SkRect::MakeXYWH(x, y, device->width(), device->height());
    222     // copy the bitmap because it may deleted after this call
    223     SkBitmap* bitmap = fAlloc.make<SkBitmap>(static_cast<SkBitmapDevice*>(device)->fBitmap);
    224     fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
    225         TileDraw(ds, tileBounds).drawSprite(*bitmap, x, y, paint);
    226     });
    227 }
    228