Home | History | Annotate | Download | only in ops
      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