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