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 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 
     30 static const int kDistanceAdjustLumShift = 5;
     31 
     32 SkString 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 
     49 void 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 
     71 void GrAtlasTextOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
     72     optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
     73 
     74     fColor = fGeoData[0].fColor;
     75     fUsesLocalCoords = optimizations.readsLocalCoords();
     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     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 
    148 void 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 
    160 bool 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
    223 sk_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 
    276 void GrBlobRegenHelper::flush() { fOp->flush(fTarget, fFlushInfo); }
    277