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#include "glsl/GrGLSLColorSpaceXformHelper.h" 21#include "glsl/GrGLSLGeometryProcessor.h" 22#include "glsl/GrGLSLVarying.h" 23 24namespace { 25 26class LatticeGP : public GrGeometryProcessor { 27public: 28 struct Vertex { 29 SkPoint fPosition; 30 SkPoint fTextureCoords; 31 SkRect fTextureDomain; 32 GrColor fColor; 33 }; 34 35 static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxy, 36 sk_sp<GrColorSpaceXform> csxf, 37 GrSamplerState::Filter filter) { 38 return sk_sp<GrGeometryProcessor>(new LatticeGP(std::move(proxy), std::move(csxf), filter)); 39 } 40 41 const char* name() const override { return "LatticeGP"; } 42 43 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { 44 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get())); 45 } 46 47 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override { 48 class GLSLProcessor : public GrGLSLGeometryProcessor { 49 public: 50 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, 51 FPCoordTransformIter&& transformIter) override { 52 const auto& latticeGP = proc.cast<LatticeGP>(); 53 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 54 if (fColorSpaceXformHelper.isValid()) { 55 fColorSpaceXformHelper.setData(pdman, latticeGP.fColorSpaceXform.get()); 56 } 57 } 58 59 private: 60 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 61 using Interpolation = GrGLSLVaryingHandler::Interpolation; 62 const auto& latticeGP = args.fGP.cast<LatticeGP>(); 63 fColorSpaceXformHelper.emitCode(args.fUniformHandler, 64 latticeGP.fColorSpaceXform.get()); 65 66 args.fVaryingHandler->emitAttributes(latticeGP); 67 this->writeOutputPosition(args.fVertBuilder, gpArgs, latticeGP.fPositions.fName); 68 this->emitTransforms(args.fVertBuilder, 69 args.fVaryingHandler, 70 args.fUniformHandler, 71 latticeGP.fTextureCoords.asShaderVar(), 72 args.fFPCoordTransformHandler); 73 args.fFragBuilder->codeAppend("float2 textureCoords;"); 74 args.fVaryingHandler->addPassThroughAttribute(&latticeGP.fTextureCoords, 75 "textureCoords"); 76 args.fFragBuilder->codeAppend("float4 textureDomain;"); 77 args.fVaryingHandler->addPassThroughAttribute( 78 &latticeGP.fTextureDomain, "textureDomain", Interpolation::kCanBeFlat); 79 args.fVaryingHandler->addPassThroughAttribute(&latticeGP.fColors, args.fOutputColor, 80 Interpolation::kCanBeFlat); 81 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); 82 args.fFragBuilder->appendTextureLookupAndModulate( 83 args.fOutputColor, 84 args.fTexSamplers[0], 85 "clamp(textureCoords, textureDomain.xy, textureDomain.zw)", 86 kFloat2_GrSLType, 87 &fColorSpaceXformHelper); 88 args.fFragBuilder->codeAppend(";"); 89 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage); 90 } 91 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper; 92 }; 93 return new GLSLProcessor; 94 } 95 96private: 97 LatticeGP(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> csxf, 98 GrSamplerState::Filter filter) 99 : INHERITED(kLatticeGP_ClassID), fColorSpaceXform(std::move(csxf)) { 100 fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType); 101 fSampler.reset(std::move(proxy), filter); 102 this->addTextureSampler(&fSampler); 103 fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType); 104 fTextureDomain = this->addVertexAttrib("textureDomain", kFloat4_GrVertexAttribType); 105 fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType); 106 } 107 108 Attribute fPositions; 109 Attribute fTextureCoords; 110 Attribute fTextureDomain; 111 Attribute fColors; 112 sk_sp<GrColorSpaceXform> fColorSpaceXform; 113 TextureSampler fSampler; 114 115 typedef GrGeometryProcessor INHERITED; 116}; 117 118class NonAALatticeOp final : public GrMeshDrawOp { 119private: 120 using Helper = GrSimpleMeshDrawOpHelper; 121 122public: 123 DEFINE_OP_CLASS_ID 124 125 static const int kVertsPerRect = 4; 126 static const int kIndicesPerRect = 6; 127 128 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 129 sk_sp<GrTextureProxy> proxy, 130 sk_sp<GrColorSpaceXform> colorSpaceXForm, 131 GrSamplerState::Filter filter, 132 std::unique_ptr<SkLatticeIter> iter, const SkRect& dst) { 133 return Helper::FactoryHelper<NonAALatticeOp>(std::move(paint), viewMatrix, std::move(proxy), 134 std::move(colorSpaceXForm), filter, 135 std::move(iter), dst); 136 } 137 138 NonAALatticeOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 139 sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> colorSpaceXform, 140 GrSamplerState::Filter filter, std::unique_ptr<SkLatticeIter> iter, 141 const SkRect& dst) 142 : INHERITED(ClassID()) 143 , fHelper(helperArgs, GrAAType::kNone) 144 , fProxy(std::move(proxy)) 145 , fColorSpaceXform(std::move(colorSpaceXform)) 146 , fFilter(filter) { 147 Patch& patch = fPatches.push_back(); 148 patch.fViewMatrix = viewMatrix; 149 patch.fColor = color; 150 patch.fIter = std::move(iter); 151 patch.fDst = dst; 152 153 // setup bounds 154 this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 155 } 156 157 const char* name() const override { return "NonAALatticeOp"; } 158 159 void visitProxies(const VisitProxyFunc& func) const override { 160 fHelper.visitProxies(func); 161 } 162 163 SkString dumpInfo() const override { 164 SkString str; 165 166 for (int i = 0; i < fPatches.count(); ++i) { 167 str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i, 168 fPatches[i].fColor, fPatches[i].fDst.fLeft, fPatches[i].fDst.fTop, 169 fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom); 170 } 171 172 str += fHelper.dumpInfo(); 173 str += INHERITED::dumpInfo(); 174 return str; 175 } 176 177 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 178 179 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 180 GrPixelConfigIsClamped dstIsClamped) override { 181 auto opaque = GrColorIsOpaque(fPatches[0].fColor) && GrPixelConfigIsOpaque(fProxy->config()) 182 ? GrProcessorAnalysisColor::Opaque::kYes 183 : GrProcessorAnalysisColor::Opaque::kNo; 184 auto analysisColor = GrProcessorAnalysisColor(opaque); 185 auto result = fHelper.xpRequiresDstTexture( 186 caps, clip, dstIsClamped, GrProcessorAnalysisCoverage::kNone, &analysisColor); 187 analysisColor.isConstant(&fPatches[0].fColor); 188 return result; 189 } 190 191private: 192 void onPrepareDraws(Target* target) override { 193 auto gp = LatticeGP::Make(fProxy, fColorSpaceXform, fFilter); 194 if (!gp) { 195 SkDebugf("Couldn't create GrGeometryProcessor\n"); 196 return; 197 } 198 199 size_t vertexStride = gp->getVertexStride(); 200 int patchCnt = fPatches.count(); 201 int numRects = 0; 202 for (int i = 0; i < patchCnt; i++) { 203 numRects += fPatches[i].fIter->numRectsToDraw(); 204 } 205 206 if (!numRects) { 207 return; 208 } 209 210 sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer(); 211 PatternHelper helper(GrPrimitiveType::kTriangles); 212 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect, 213 kIndicesPerRect, numRects); 214 if (!vertices || !indexBuffer) { 215 SkDebugf("Could not allocate vertices\n"); 216 return; 217 } 218 219 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 220 for (int i = 0; i < patchCnt; i++) { 221 const Patch& patch = fPatches[i]; 222 223 // Apply the view matrix here if it is scale-translate. Otherwise, we need to 224 // wait until we've created the dst rects. 225 bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate(); 226 if (isScaleTranslate) { 227 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix); 228 } 229 230 SkIRect srcR; 231 SkRect dstR; 232 intptr_t patchVerts = verts; 233 Sk4f scales(1.f / fProxy->width(), 1.f / fProxy->height(), 234 1.f / fProxy->width(), 1.f / fProxy->height()); 235 static const Sk4f kDomainOffsets(0.5f, 0.5f, -0.5f, -0.5f); 236 static const Sk4f kFlipOffsets(0.f, 1, 0.f, 1.f); 237 static const Sk4f kFlipMuls(1.f, -1.f, 1.f, -1.f); 238 while (patch.fIter->next(&srcR, &dstR)) { 239 auto vertices = reinterpret_cast<LatticeGP::Vertex*>(verts); 240 SkPointPriv::SetRectTriStrip(&vertices->fPosition, dstR.fLeft, dstR.fTop, 241 dstR.fRight, dstR.fBottom, vertexStride); 242 Sk4f coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop), 243 SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom)); 244 Sk4f domain = coords + kDomainOffsets; 245 coords *= scales; 246 domain *= scales; 247 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) { 248 coords = kFlipMuls * coords + kFlipOffsets; 249 domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets); 250 } 251 SkPointPriv::SetRectTriStrip(&vertices->fTextureCoords, coords[0], coords[1], 252 coords[2], coords[3], vertexStride); 253 for (int j = 0; j < kVertsPerRect; ++j) { 254 vertices[j].fTextureDomain = {domain[0], domain[1], domain[2], domain[3]}; 255 } 256 257 for (int j = 0; j < kVertsPerRect; ++j) { 258 vertices[j].fColor = patch.fColor; 259 } 260 verts += kVertsPerRect * vertexStride; 261 } 262 263 // If we didn't handle it above, apply the matrix here. 264 if (!isScaleTranslate) { 265 SkPoint* positions = reinterpret_cast<SkPoint*>(patchVerts); 266 SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, positions, vertexStride, 267 kVertsPerRect * patch.fIter->numRectsToDraw()); 268 } 269 } 270 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 271 } 272 273 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 274 NonAALatticeOp* that = t->cast<NonAALatticeOp>(); 275 if (fProxy != that->fProxy) { 276 return false; 277 } 278 if (fFilter != that->fFilter) { 279 return false; 280 } 281 if (GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) { 282 return false; 283 } 284 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 285 return false; 286 } 287 288 fPatches.move_back_n(that->fPatches.count(), that->fPatches.begin()); 289 this->joinBounds(*that); 290 return true; 291 } 292 293 struct Patch { 294 SkMatrix fViewMatrix; 295 std::unique_ptr<SkLatticeIter> fIter; 296 SkRect fDst; 297 GrColor fColor; 298 }; 299 300 Helper fHelper; 301 SkSTArray<1, Patch, true> fPatches; 302 sk_sp<GrTextureProxy> fProxy; 303 sk_sp<GrColorSpaceXform> fColorSpaceXform; 304 GrSamplerState::Filter fFilter; 305 306 typedef GrMeshDrawOp INHERITED; 307}; 308 309} // anonymous namespace 310 311namespace GrLatticeOp { 312std::unique_ptr<GrDrawOp> MakeNonAA(GrPaint&& paint, const SkMatrix& viewMatrix, 313 sk_sp<GrTextureProxy> proxy, 314 sk_sp<GrColorSpaceXform> colorSpaceXform, 315 GrSamplerState::Filter filter, 316 std::unique_ptr<SkLatticeIter> iter, const SkRect& dst) { 317 return NonAALatticeOp::Make(std::move(paint), viewMatrix, std::move(proxy), 318 std::move(colorSpaceXform), filter, std::move(iter), dst); 319} 320}; 321 322#if GR_TEST_UTILS 323#include "GrContextPriv.h" 324#include "GrProxyProvider.h" 325 326/** Randomly divides subset into count divs. */ 327static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop, 328 SkRandom* random) { 329 // Rules for lattice divs: Must be strictly increasing and in the range 330 // [subsetStart, subsetStop). 331 // Not terribly efficient alg for generating random divs: 332 // 1) Start with minimum legal pixels between each div. 333 // 2) Randomly assign the remaining pixels of the subset to divs. 334 // 3) Convert from pixel counts to div offsets. 335 336 // 1) Initially each divs[i] represents the number of pixels between 337 // div i-1 and i. The initial div is allowed to be at subsetStart. There 338 // must be one pixel spacing between subsequent divs. 339 divs[0] = 0; 340 for (int i = 1; i < count; ++i) { 341 divs[i] = 1; 342 } 343 // 2) Assign the remaining subset pixels to fall 344 int subsetLength = subsetStop - subsetStart; 345 for (int i = 0; i < subsetLength - count; ++i) { 346 // +1 because count divs means count+1 intervals. 347 int entry = random->nextULessThan(count + 1); 348 // We don't have an entry to to store the count after the last div 349 if (entry < count) { 350 divs[entry]++; 351 } 352 } 353 // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset. 354 int offset = subsetStart; 355 for (int i = 0; i < count; ++i) { 356 divs[i] += offset; 357 offset = divs[i]; 358 } 359} 360 361GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) { 362 SkCanvas::Lattice lattice; 363 // We loop because our random lattice code can produce an invalid lattice in the case where 364 // there is a single div separator in both x and y and both are aligned with the left and top 365 // edge of the image subset, respectively. 366 std::unique_ptr<int[]> xdivs; 367 std::unique_ptr<int[]> ydivs; 368 std::unique_ptr<SkCanvas::Lattice::RectType[]> flags; 369 std::unique_ptr<SkColor[]> colors; 370 SkIRect subset; 371 GrSurfaceDesc desc; 372 desc.fConfig = kRGBA_8888_GrPixelConfig; 373 desc.fWidth = random->nextRangeU(1, 1000); 374 desc.fHeight = random->nextRangeU(1, 1000); 375 desc.fOrigin = 376 random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; 377 auto proxy = context->contextPriv().proxyProvider()->createProxy( 378 desc, SkBackingFit::kExact, SkBudgeted::kYes); 379 380 do { 381 if (random->nextBool()) { 382 subset.fLeft = random->nextULessThan(desc.fWidth); 383 subset.fRight = random->nextRangeU(subset.fLeft + 1, desc.fWidth); 384 subset.fTop = random->nextULessThan(desc.fHeight); 385 subset.fBottom = random->nextRangeU(subset.fTop + 1, desc.fHeight); 386 } else { 387 subset.setXYWH(0, 0, desc.fWidth, desc.fHeight); 388 } 389 // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with 390 // a non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds. 391 lattice.fBounds = ⊂ 392 lattice.fXCount = random->nextRangeU(1, subset.width()); 393 lattice.fYCount = random->nextRangeU(1, subset.height()); 394 xdivs.reset(new int[lattice.fXCount]); 395 ydivs.reset(new int[lattice.fYCount]); 396 init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random); 397 init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random); 398 lattice.fXDivs = xdivs.get(); 399 lattice.fYDivs = ydivs.get(); 400 bool hasFlags = random->nextBool(); 401 if (hasFlags) { 402 int n = (lattice.fXCount + 1) * (lattice.fYCount + 1); 403 flags.reset(new SkCanvas::Lattice::RectType[n]); 404 colors.reset(new SkColor[n]); 405 for (int i = 0; i < n; ++i) { 406 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent 407 : SkCanvas::Lattice::kDefault; 408 } 409 lattice.fRectTypes = flags.get(); 410 lattice.fColors = colors.get(); 411 } else { 412 lattice.fRectTypes = nullptr; 413 lattice.fColors = nullptr; 414 } 415 } while (!SkLatticeIter::Valid(desc.fWidth, desc.fHeight, lattice)); 416 SkRect dst; 417 dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f); 418 dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f); 419 dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f); 420 dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f); 421 std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst)); 422 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 423 auto csxf = GrTest::TestColorXform(random); 424 GrSamplerState::Filter filter = 425 random->nextBool() ? GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kBilerp; 426 return NonAALatticeOp::Make(std::move(paint), viewMatrix, std::move(proxy), std::move(csxf), 427 filter, std::move(iter), dst); 428} 429 430#endif 431