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 "GrAtlasTextOp.h" 9 10 #include "GrContext.h" 11 #include "GrOpFlushState.h" 12 #include "GrResourceProvider.h" 13 14 #include "SkGlyphCache.h" 15 #include "SkMathPriv.h" 16 17 #include "effects/GrBitmapTextGeoProc.h" 18 #include "effects/GrDistanceFieldGeoProc.h" 19 #include "text/GrAtlasGlyphCache.h" 20 21 /////////////////////////////////////////////////////////////////////////////////////////////////// 22 23 static const int kDistanceAdjustLumShift = 5; 24 25 SkString GrAtlasTextOp::dumpInfo() const { 26 SkString str; 27 28 for (int i = 0; i < fGeoCount; ++i) { 29 str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n", 30 i, 31 fGeoData[i].fColor, 32 fGeoData[i].fX, 33 fGeoData[i].fY, 34 fGeoData[i].fBlob->runCount()); 35 } 36 37 str += fProcessors.dumpProcessors(); 38 str += INHERITED::dumpInfo(); 39 return str; 40 } 41 42 GrDrawOp::FixedFunctionFlags GrAtlasTextOp::fixedFunctionFlags() const { 43 return FixedFunctionFlags::kNone; 44 } 45 46 GrDrawOp::RequiresDstTexture GrAtlasTextOp::finalize(const GrCaps& caps, 47 const GrAppliedClip* clip) { 48 GrProcessorAnalysisCoverage coverage; 49 GrProcessorAnalysisColor color; 50 if (kColorBitmapMask_MaskType == fMaskType) { 51 color.setToUnknown(); 52 } else { 53 color.setToConstant(fColor); 54 } 55 switch (fMaskType) { 56 case kGrayscaleCoverageMask_MaskType: 57 case kAliasedDistanceField_MaskType: 58 case kGrayscaleDistanceField_MaskType: 59 coverage = GrProcessorAnalysisCoverage::kSingleChannel; 60 break; 61 case kLCDCoverageMask_MaskType: 62 case kLCDDistanceField_MaskType: 63 case kLCDBGRDistanceField_MaskType: 64 coverage = GrProcessorAnalysisCoverage::kLCD; 65 break; 66 case kColorBitmapMask_MaskType: 67 coverage = GrProcessorAnalysisCoverage::kNone; 68 break; 69 } 70 auto analysis = fProcessors.finalize(color, coverage, clip, false, caps, &fColor); 71 fUsesLocalCoords = analysis.usesLocalCoords(); 72 fCanCombineOnTouchOrOverlap = 73 !analysis.requiresDstTexture() && 74 !(fProcessors.xferProcessor() && fProcessors.xferProcessor()->xferBarrierType(caps)); 75 return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo; 76 } 77 78 void GrAtlasTextOp::onPrepareDraws(Target* target) const { 79 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. 80 // TODO actually only invert if we don't have RGBA 81 SkMatrix localMatrix; 82 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { 83 SkDebugf("Cannot invert viewmatrix\n"); 84 return; 85 } 86 87 sk_sp<GrTextureProxy> proxy = fFontCache->getProxy(this->maskFormat()); 88 if (!proxy) { 89 SkDebugf("Could not allocate backing texture for atlas\n"); 90 return; 91 } 92 93 GrMaskFormat maskFormat = this->maskFormat(); 94 95 FlushInfo flushInfo; 96 flushInfo.fPipeline = target->makePipeline(fSRGBFlags, &fProcessors); 97 if (this->usesDistanceFields()) { 98 flushInfo.fGeometryProcessor = 99 this->setupDfProcessor(this->viewMatrix(), 100 fLuminanceColor, this->color(), std::move(proxy)); 101 } else { 102 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode); 103 flushInfo.fGeometryProcessor = 104 GrBitmapTextGeoProc::Make(this->color(), std::move(proxy), params, maskFormat, 105 localMatrix, this->usesLocalCoords()); 106 } 107 108 flushInfo.fGlyphsToFlush = 0; 109 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride(); 110 SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat)); 111 112 int glyphCount = this->numGlyphs(); 113 const GrBuffer* vertexBuffer; 114 115 void* vertices = target->makeVertexSpace( 116 vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset); 117 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); 118 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer()); 119 if (!vertices || !flushInfo.fVertexBuffer) { 120 SkDebugf("Could not allocate vertices\n"); 121 return; 122 } 123 124 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); 125 126 GrBlobRegenHelper helper(this, target, &flushInfo); 127 SkAutoGlyphCache glyphCache; 128 for (int i = 0; i < fGeoCount; i++) { 129 const Geometry& args = fGeoData[i]; 130 Blob* blob = args.fBlob; 131 size_t byteCount; 132 void* blobVertices; 133 int subRunGlyphCount; 134 blob->regenInOp(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache, 135 vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor, 136 &blobVertices, &byteCount, &subRunGlyphCount); 137 138 // now copy all vertices 139 memcpy(currVertex, blobVertices, byteCount); 140 141 currVertex += byteCount; 142 } 143 144 this->flush(target, &flushInfo); 145 } 146 147 void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const { 148 GrMesh mesh(GrPrimitiveType::kTriangles); 149 int maxGlyphsPerDraw = 150 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6); 151 mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph, 152 flushInfo->fGlyphsToFlush, maxGlyphsPerDraw); 153 mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset); 154 target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline, mesh); 155 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush; 156 flushInfo->fGlyphsToFlush = 0; 157 } 158 159 bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) { 160 GrAtlasTextOp* that = t->cast<GrAtlasTextOp>(); 161 if (fProcessors != that->fProcessors) { 162 return false; 163 } 164 165 if (!fCanCombineOnTouchOrOverlap && GrRectsTouchOrOverlap(this->bounds(), that->bounds())) { 166 return false; 167 } 168 169 if (fMaskType != that->fMaskType) { 170 return false; 171 } 172 173 if (!this->usesDistanceFields()) { 174 if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) { 175 return false; 176 } 177 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 178 return false; 179 } 180 } else { 181 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 182 return false; 183 } 184 185 if (fLuminanceColor != that->fLuminanceColor) { 186 return false; 187 } 188 } 189 190 fNumGlyphs += that->numGlyphs(); 191 192 // Reallocate space for geo data if necessary and then import that's geo data. 193 int newGeoCount = that->fGeoCount + fGeoCount; 194 // We assume (and here enforce) that the allocation size is the smallest power of two that 195 // is greater than or equal to the number of geometries (and at least 196 // kMinGeometryAllocated). 197 int newAllocSize = GrNextPow2(newGeoCount); 198 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount)); 199 200 if (newGeoCount > currAllocSize) { 201 fGeoData.realloc(newAllocSize); 202 } 203 204 // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that 205 // it doesn't try to unref them. 206 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry)); 207 #ifdef SK_DEBUG 208 for (int i = 0; i < that->fGeoCount; ++i) { 209 that->fGeoData.get()[i].fBlob = (Blob*)0x1; 210 } 211 #endif 212 that->fGeoCount = 0; 213 fGeoCount = newGeoCount; 214 215 this->joinBounds(*that); 216 return true; 217 } 218 219 // TODO just use class params 220 // TODO trying to figure out why lcd is so whack 221 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(const SkMatrix& viewMatrix, 222 SkColor luminanceColor, 223 GrColor color, 224 sk_sp<GrTextureProxy> proxy) const { 225 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode); 226 bool isLCD = this->isLCD(); 227 // set up any flags 228 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; 229 flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0; 230 flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0; 231 flags |= (kAliasedDistanceField_MaskType == fMaskType) ? kAliased_DistanceFieldEffectFlag : 0; 232 233 // see if we need to create a new effect 234 if (isLCD) { 235 flags |= kUseLCD_DistanceFieldEffectFlag; 236 flags |= (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0; 237 238 float redCorrection = fDistanceAdjustTable->getAdjustment( 239 SkColorGetR(luminanceColor) >> kDistanceAdjustLumShift, 240 fUseGammaCorrectDistanceTable); 241 float greenCorrection = fDistanceAdjustTable->getAdjustment( 242 SkColorGetG(luminanceColor) >> kDistanceAdjustLumShift, 243 fUseGammaCorrectDistanceTable); 244 float blueCorrection = fDistanceAdjustTable->getAdjustment( 245 SkColorGetB(luminanceColor) >> kDistanceAdjustLumShift, 246 fUseGammaCorrectDistanceTable); 247 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust = 248 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make( 249 redCorrection, greenCorrection, blueCorrection); 250 251 return GrDistanceFieldLCDTextGeoProc::Make(color, viewMatrix, std::move(proxy), params, 252 widthAdjust, flags, this->usesLocalCoords()); 253 } else { 254 #ifdef SK_GAMMA_APPLY_TO_A8 255 float correction = 0; 256 if (kAliasedDistanceField_MaskType != fMaskType) { 257 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, luminanceColor); 258 correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift, 259 fUseGammaCorrectDistanceTable); 260 } 261 return GrDistanceFieldA8TextGeoProc::Make(color, viewMatrix, std::move(proxy), params, 262 correction, flags, this->usesLocalCoords()); 263 #else 264 return GrDistanceFieldA8TextGeoProc::Make(color, 265 viewMatrix, std::move(proxy), 266 params, flags, this->usesLocalCoords()); 267 #endif 268 } 269 } 270 271 void GrBlobRegenHelper::flush() { fOp->flush(fTarget, fFlushInfo); } 272