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