1/* 2 * Copyright 2015 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 "GrLatticeOp.h" 9#include "GrDefaultGeoProcFactory.h" 10#include "GrDrawOpTest.h" 11#include "GrMeshDrawOp.h" 12#include "GrOpFlushState.h" 13#include "GrResourceProvider.h" 14#include "GrSimpleMeshDrawOpHelper.h" 15#include "SkBitmap.h" 16#include "SkLatticeIter.h" 17#include "SkMatrixPriv.h" 18#include "SkPointPriv.h" 19#include "SkRect.h" 20 21static sk_sp<GrGeometryProcessor> create_gp() { 22 using namespace GrDefaultGeoProcFactory; 23 return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type, 24 LocalCoords::kHasExplicit_Type, SkMatrix::I()); 25} 26 27namespace { 28 29class NonAALatticeOp final : public GrMeshDrawOp { 30private: 31 using Helper = GrSimpleMeshDrawOpHelper; 32 33public: 34 DEFINE_OP_CLASS_ID 35 36 static const int kVertsPerRect = 4; 37 static const int kIndicesPerRect = 6; 38 39 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 40 int imageWidth, int imageHeight, 41 std::unique_ptr<SkLatticeIter> iter, const SkRect& dst) { 42 return Helper::FactoryHelper<NonAALatticeOp>(std::move(paint), viewMatrix, imageWidth, 43 imageHeight, std::move(iter), dst); 44 } 45 46 NonAALatticeOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 47 int imageWidth, int imageHeight, std::unique_ptr<SkLatticeIter> iter, 48 const SkRect& dst) 49 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kNone) { 50 Patch& patch = fPatches.push_back(); 51 patch.fViewMatrix = viewMatrix; 52 patch.fColor = color; 53 patch.fIter = std::move(iter); 54 patch.fDst = dst; 55 56 fImageWidth = imageWidth; 57 fImageHeight = imageHeight; 58 59 // setup bounds 60 this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 61 } 62 63 const char* name() const override { return "NonAALatticeOp"; } 64 65 void visitProxies(const VisitProxyFunc& func) const override { 66 fHelper.visitProxies(func); 67 } 68 69 SkString dumpInfo() const override { 70 SkString str; 71 72 for (int i = 0; i < fPatches.count(); ++i) { 73 str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 74 fPatches[i].fColor, fPatches[i].fDst.fLeft, fPatches[i].fDst.fTop, 75 fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom); 76 } 77 78 str += fHelper.dumpInfo(); 79 str += INHERITED::dumpInfo(); 80 return str; 81 } 82 83 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 84 85 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 86 GrPixelConfigIsClamped dstIsClamped) override { 87 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 88 GrProcessorAnalysisCoverage::kNone, 89 &fPatches.front().fColor); 90 } 91 92private: 93 void onPrepareDraws(Target* target) override { 94 sk_sp<GrGeometryProcessor> gp(create_gp()); 95 if (!gp) { 96 SkDebugf("Couldn't create GrGeometryProcessor\n"); 97 return; 98 } 99 100 size_t vertexStride = gp->getVertexStride(); 101 int patchCnt = fPatches.count(); 102 int numRects = 0; 103 for (int i = 0; i < patchCnt; i++) { 104 numRects += fPatches[i].fIter->numRectsToDraw(); 105 } 106 107 if (!numRects) { 108 return; 109 } 110 111 sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer(); 112 PatternHelper helper(GrPrimitiveType::kTriangles); 113 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect, 114 kIndicesPerRect, numRects); 115 if (!vertices || !indexBuffer) { 116 SkDebugf("Could not allocate vertices\n"); 117 return; 118 } 119 120 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 121 for (int i = 0; i < patchCnt; i++) { 122 const Patch& patch = fPatches[i]; 123 124 // Apply the view matrix here if it is scale-translate. Otherwise, we need to 125 // wait until we've created the dst rects. 126 bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate(); 127 if (isScaleTranslate) { 128 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix); 129 } 130 131 SkRect srcR, dstR; 132 intptr_t patchVerts = verts; 133 while (patch.fIter->next(&srcR, &dstR)) { 134 SkPoint* positions = reinterpret_cast<SkPoint*>(verts); 135 SkPointPriv::SetRectTriStrip(positions, dstR.fLeft, dstR.fTop, dstR.fRight, 136 dstR.fBottom, vertexStride); 137 138 // Setup local coords 139 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor); 140 SkPoint* coords = reinterpret_cast<SkPoint*>(verts + kLocalOffset); 141 SkPointPriv::SetRectTriStrip(coords, srcR.fLeft, srcR.fTop, srcR.fRight, 142 srcR.fBottom, vertexStride); 143 144 static const int kColorOffset = sizeof(SkPoint); 145 GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset); 146 for (int j = 0; j < 4; ++j) { 147 *vertColor = patch.fColor; 148 vertColor = (GrColor*)((intptr_t)vertColor + vertexStride); 149 } 150 verts += kVertsPerRect * vertexStride; 151 } 152 153 // If we didn't handle it above, apply the matrix here. 154 if (!isScaleTranslate) { 155 SkPoint* positions = reinterpret_cast<SkPoint*>(patchVerts); 156 SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, positions, vertexStride, 157 kVertsPerRect * patch.fIter->numRectsToDraw()); 158 } 159 } 160 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 161 } 162 163 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 164 NonAALatticeOp* that = t->cast<NonAALatticeOp>(); 165 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 166 return false; 167 } 168 169 SkASSERT(this->fImageWidth == that->fImageWidth && 170 this->fImageHeight == that->fImageHeight); 171 172 fPatches.move_back_n(that->fPatches.count(), that->fPatches.begin()); 173 this->joinBounds(*that); 174 return true; 175 } 176 177 struct Patch { 178 SkMatrix fViewMatrix; 179 std::unique_ptr<SkLatticeIter> fIter; 180 SkRect fDst; 181 GrColor fColor; 182 }; 183 184 Helper fHelper; 185 SkSTArray<1, Patch, true> fPatches; 186 int fImageWidth; 187 int fImageHeight; 188 189 typedef GrMeshDrawOp INHERITED; 190}; 191 192} // anonymous namespace 193 194namespace GrLatticeOp { 195std::unique_ptr<GrDrawOp> MakeNonAA(GrPaint&& paint, const SkMatrix& viewMatrix, int imageWidth, 196 int imageHeight, std::unique_ptr<SkLatticeIter> iter, 197 const SkRect& dst) { 198 return NonAALatticeOp::Make(std::move(paint), viewMatrix, imageWidth, imageHeight, 199 std::move(iter), dst); 200} 201}; 202 203#if GR_TEST_UTILS 204 205/** Randomly divides subset into count divs. */ 206static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop, 207 SkRandom* random) { 208 // Rules for lattice divs: Must be strictly increasing and in the range 209 // [subsetStart, subsetStop). 210 // Not terribly efficient alg for generating random divs: 211 // 1) Start with minimum legal pixels between each div. 212 // 2) Randomly assign the remaining pixels of the subset to divs. 213 // 3) Convert from pixel counts to div offsets. 214 215 // 1) Initially each divs[i] represents the number of pixels between 216 // div i-1 and i. The initial div is allowed to be at subsetStart. There 217 // must be one pixel spacing between subsequent divs. 218 divs[0] = 0; 219 for (int i = 1; i < count; ++i) { 220 divs[i] = 1; 221 } 222 // 2) Assign the remaining subset pixels to fall 223 int subsetLength = subsetStop - subsetStart; 224 for (int i = 0; i < subsetLength - count; ++i) { 225 // +1 because count divs means count+1 intervals. 226 int entry = random->nextULessThan(count + 1); 227 // We don't have an entry to to store the count after the last div 228 if (entry < count) { 229 divs[entry]++; 230 } 231 } 232 // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset. 233 int offset = subsetStart; 234 for (int i = 0; i < count; ++i) { 235 divs[i] += offset; 236 offset = divs[i]; 237 } 238} 239 240GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) { 241 SkCanvas::Lattice lattice; 242 int imgW, imgH; 243 // We loop because our random lattice code can produce an invalid lattice in the case where 244 // there is a single div separator in both x and y and both are aligned with the left and top 245 // edge of the image subset, respectively. 246 std::unique_ptr<int[]> xdivs; 247 std::unique_ptr<int[]> ydivs; 248 std::unique_ptr<SkCanvas::Lattice::RectType[]> flags; 249 std::unique_ptr<SkColor[]> colors; 250 SkIRect subset; 251 do { 252 imgW = random->nextRangeU(1, 1000); 253 imgH = random->nextRangeU(1, 1000); 254 if (random->nextBool()) { 255 subset.fLeft = random->nextULessThan(imgW); 256 subset.fRight = random->nextRangeU(subset.fLeft + 1, imgW); 257 subset.fTop = random->nextULessThan(imgH); 258 subset.fBottom = random->nextRangeU(subset.fTop + 1, imgH); 259 } else { 260 subset.setXYWH(0, 0, imgW, imgH); 261 } 262 // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with a 263 // non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds. 264 lattice.fBounds = ⊂ 265 lattice.fXCount = random->nextRangeU(1, subset.width()); 266 lattice.fYCount = random->nextRangeU(1, subset.height()); 267 xdivs.reset(new int[lattice.fXCount]); 268 ydivs.reset(new int[lattice.fYCount]); 269 init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random); 270 init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random); 271 lattice.fXDivs = xdivs.get(); 272 lattice.fYDivs = ydivs.get(); 273 bool hasFlags = random->nextBool(); 274 if (hasFlags) { 275 int n = (lattice.fXCount + 1) * (lattice.fYCount + 1); 276 flags.reset(new SkCanvas::Lattice::RectType[n]); 277 colors.reset(new SkColor[n]); 278 for (int i = 0; i < n; ++i) { 279 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent 280 : SkCanvas::Lattice::kDefault; 281 } 282 lattice.fRectTypes = flags.get(); 283 lattice.fColors = colors.get(); 284 } else { 285 lattice.fRectTypes = nullptr; 286 lattice.fColors = nullptr; 287 } 288 } while (!SkLatticeIter::Valid(imgW, imgH, lattice)); 289 290 SkRect dst; 291 dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f); 292 dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f); 293 dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f); 294 dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f); 295 std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst)); 296 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 297 return NonAALatticeOp::Make(std::move(paint), viewMatrix, imgW, imgH, std::move(iter), dst); 298} 299 300#endif 301