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 void 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 36 SkThreadedBMPDevice::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 61 void SkThreadedBMPDevice::flush() { 62 fQueue.reset(); 63 } 64 65 SkThreadedBMPDevice::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 75 SkIRect 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 84 SkDraw SkThreadedBMPDevice::DrawState::getDraw() const { 85 SkDraw draw; 86 draw.fDst = fDst; 87 draw.fMatrix = &fMatrix; 88 draw.fRC = &fRC; 89 return draw; 90 } 91 92 SkThreadedBMPDevice::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 100 static 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 110 void 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 117 void 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 125 void 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 132 void 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 148 void 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 157 void 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 168 void 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 175 void 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 184 void 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 193 void 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 204 void 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