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 #include "GrContext.h"
     10 #include "GrOpFlushState.h"
     11 #include "GrResourceProvider.h"
     12 #include "SkGlyphCache.h"
     13 #include "SkMathPriv.h"
     14 #include "SkMatrixPriv.h"
     15 #include "SkPoint3.h"
     16 #include "effects/GrBitmapTextGeoProc.h"
     17 #include "effects/GrDistanceFieldGeoProc.h"
     18 #include "text/GrAtlasGlyphCache.h"
     19 
     20 ///////////////////////////////////////////////////////////////////////////////////////////////////
     21 
     22 static const int kDistanceAdjustLumShift = 5;
     23 
     24 void GrAtlasTextOp::init() {
     25     const Geometry& geo = fGeoData[0];
     26     SkRect bounds;
     27     geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX, geo.fY);
     28     // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
     29     // we treat this as a set of non-AA rects rendered with a texture.
     30     this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
     31     if (this->usesDistanceFields()) {
     32         bool isLCD = this->isLCD();
     33 
     34         const SkMatrix& viewMatrix = geo.fViewMatrix;
     35 
     36         fDFGPFlags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
     37         fDFGPFlags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
     38         fDFGPFlags |= viewMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
     39         fDFGPFlags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
     40         fDFGPFlags |= (kAliasedDistanceField_MaskType == fMaskType)
     41                               ? kAliased_DistanceFieldEffectFlag
     42                               : 0;
     43 
     44         if (isLCD) {
     45             fDFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
     46             fDFGPFlags |=
     47                     (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
     48         }
     49     }
     50 }
     51 
     52 SkString GrAtlasTextOp::dumpInfo() const {
     53     SkString str;
     54 
     55     for (int i = 0; i < fGeoCount; ++i) {
     56         str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
     57                     i,
     58                     fGeoData[i].fColor,
     59                     fGeoData[i].fX,
     60                     fGeoData[i].fY,
     61                     fGeoData[i].fBlob->runCount());
     62     }
     63 
     64     str += fProcessors.dumpProcessors();
     65     str += INHERITED::dumpInfo();
     66     return str;
     67 }
     68 
     69 GrDrawOp::FixedFunctionFlags GrAtlasTextOp::fixedFunctionFlags() const {
     70     return FixedFunctionFlags::kNone;
     71 }
     72 
     73 GrDrawOp::RequiresDstTexture GrAtlasTextOp::finalize(const GrCaps& caps,
     74                                                      const GrAppliedClip* clip,
     75                                                      GrPixelConfigIsClamped dstIsClamped) {
     76     GrProcessorAnalysisCoverage coverage;
     77     GrProcessorAnalysisColor color;
     78     if (kColorBitmapMask_MaskType == fMaskType) {
     79         color.setToUnknown();
     80     } else {
     81         color.setToConstant(this->color());
     82     }
     83     switch (fMaskType) {
     84         case kGrayscaleCoverageMask_MaskType:
     85         case kAliasedDistanceField_MaskType:
     86         case kGrayscaleDistanceField_MaskType:
     87             coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     88             break;
     89         case kLCDCoverageMask_MaskType:
     90         case kLCDDistanceField_MaskType:
     91         case kLCDBGRDistanceField_MaskType:
     92             coverage = GrProcessorAnalysisCoverage::kLCD;
     93             break;
     94         case kColorBitmapMask_MaskType:
     95             coverage = GrProcessorAnalysisCoverage::kNone;
     96             break;
     97     }
     98     auto analysis = fProcessors.finalize(color, coverage, clip, false, caps, dstIsClamped,
     99                                          &fGeoData[0].fColor);
    100     fUsesLocalCoords = analysis.usesLocalCoords();
    101     fCanCombineOnTouchOrOverlap =
    102             !analysis.requiresDstTexture() &&
    103             !(fProcessors.xferProcessor() && fProcessors.xferProcessor()->xferBarrierType(caps));
    104     return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
    105 }
    106 
    107 static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
    108                        size_t vertexStride, int glyphCount) {
    109     for (int i = 0; i < glyphCount; ++i) {
    110         const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
    111         const SkPoint* blobPositionRB =
    112                 reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);
    113 
    114         // positions for bitmap glyphs are pixel boundary aligned
    115         SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX),
    116                                                  SkScalarRoundToInt(blobPositionLT->fY),
    117                                                  SkScalarRoundToInt(blobPositionRB->fX),
    118                                                  SkScalarRoundToInt(blobPositionRB->fY));
    119         if (clipRect.contains(positionRect)) {
    120             memcpy(currVertex, blobVertices, 4 * vertexStride);
    121             currVertex += 4 * vertexStride;
    122         } else {
    123             // Pull out some more data that we'll need.
    124             // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
    125             // and it avoids a lot of conditionals.
    126             auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
    127             size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
    128             auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
    129             auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
    130                                                                    coordOffset);
    131             // Pull out the texel coordinates and texture index bits
    132             uint16_t coordsRectL = blobCoordsLT[0] >> 1;
    133             uint16_t coordsRectT = blobCoordsLT[1] >> 1;
    134             uint16_t coordsRectR = blobCoordsRB[0] >> 1;
    135             uint16_t coordsRectB = blobCoordsRB[1] >> 1;
    136             uint16_t pageIndexX = blobCoordsLT[0] & 0x1;
    137             uint16_t pageIndexY = blobCoordsLT[1] & 0x1;
    138 
    139             int positionRectWidth = positionRect.width();
    140             int positionRectHeight = positionRect.height();
    141             SkASSERT(positionRectWidth == (coordsRectR - coordsRectL));
    142             SkASSERT(positionRectHeight == (coordsRectB - coordsRectT));
    143 
    144             // Clip position and texCoords to the clipRect
    145             unsigned int delta;
    146             delta = SkTMin(SkTMax(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth);
    147             coordsRectL += delta;
    148             positionRect.fLeft += delta;
    149 
    150             delta = SkTMin(SkTMax(clipRect.fTop - positionRect.fTop, 0), positionRectHeight);
    151             coordsRectT += delta;
    152             positionRect.fTop += delta;
    153 
    154             delta = SkTMin(SkTMax(positionRect.fRight - clipRect.fRight, 0), positionRectWidth);
    155             coordsRectR -= delta;
    156             positionRect.fRight -= delta;
    157 
    158             delta = SkTMin(SkTMax(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight);
    159             coordsRectB -= delta;
    160             positionRect.fBottom -= delta;
    161 
    162             // Repack texel coordinates and index
    163             coordsRectL = coordsRectL << 1 | pageIndexX;
    164             coordsRectT = coordsRectT << 1 | pageIndexY;
    165             coordsRectR = coordsRectR << 1 | pageIndexX;
    166             coordsRectB = coordsRectB << 1 | pageIndexY;
    167 
    168             // Set new positions and coords
    169             SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
    170             currPosition->fX = positionRect.fLeft;
    171             currPosition->fY = positionRect.fTop;
    172             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
    173             uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
    174             currCoords[0] = coordsRectL;
    175             currCoords[1] = coordsRectT;
    176             currVertex += vertexStride;
    177 
    178             currPosition = reinterpret_cast<SkPoint*>(currVertex);
    179             currPosition->fX = positionRect.fLeft;
    180             currPosition->fY = positionRect.fBottom;
    181             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
    182             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
    183             currCoords[0] = coordsRectL;
    184             currCoords[1] = coordsRectB;
    185             currVertex += vertexStride;
    186 
    187             currPosition = reinterpret_cast<SkPoint*>(currVertex);
    188             currPosition->fX = positionRect.fRight;
    189             currPosition->fY = positionRect.fTop;
    190             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
    191             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
    192             currCoords[0] = coordsRectR;
    193             currCoords[1] = coordsRectT;
    194             currVertex += vertexStride;
    195 
    196             currPosition = reinterpret_cast<SkPoint*>(currVertex);
    197             currPosition->fX = positionRect.fRight;
    198             currPosition->fY = positionRect.fBottom;
    199             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
    200             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
    201             currCoords[0] = coordsRectR;
    202             currCoords[1] = coordsRectB;
    203             currVertex += vertexStride;
    204         }
    205 
    206         blobVertices += 4 * vertexStride;
    207     }
    208 }
    209 
    210 void GrAtlasTextOp::onPrepareDraws(Target* target) {
    211     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
    212     // TODO actually only invert if we don't have RGBA
    213     SkMatrix localMatrix;
    214     if (this->usesLocalCoords() && !fGeoData[0].fViewMatrix.invert(&localMatrix)) {
    215         SkDebugf("Cannot invert viewmatrix\n");
    216         return;
    217     }
    218 
    219     GrMaskFormat maskFormat = this->maskFormat();
    220 
    221     uint32_t atlasPageCount = fFontCache->getAtlasPageCount(maskFormat);
    222     const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(maskFormat);
    223     if (!atlasPageCount || !proxies[0]) {
    224         SkDebugf("Could not allocate backing texture for atlas\n");
    225         return;
    226     }
    227 
    228     FlushInfo flushInfo;
    229     flushInfo.fPipeline =
    230             target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip());
    231     SkDEBUGCODE(bool dfPerspective = false);
    232     if (this->usesDistanceFields()) {
    233         flushInfo.fGeometryProcessor = this->setupDfProcessor();
    234         SkDEBUGCODE(dfPerspective = fGeoData[0].fViewMatrix.hasPerspective());
    235     } else {
    236         flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
    237             this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat,
    238             localMatrix, this->usesLocalCoords());
    239     }
    240 
    241     flushInfo.fGlyphsToFlush = 0;
    242     size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
    243     SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat, dfPerspective));
    244 
    245     int glyphCount = this->numGlyphs();
    246     const GrBuffer* vertexBuffer;
    247 
    248     void* vertices = target->makeVertexSpace(
    249             vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset);
    250     flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
    251     flushInfo.fIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
    252     if (!vertices || !flushInfo.fVertexBuffer) {
    253         SkDebugf("Could not allocate vertices\n");
    254         return;
    255     }
    256 
    257     char* currVertex = reinterpret_cast<char*>(vertices);
    258 
    259     SkAutoGlyphCache glyphCache;
    260     // each of these is a SubRun
    261     for (int i = 0; i < fGeoCount; i++) {
    262         const Geometry& args = fGeoData[i];
    263         Blob* blob = args.fBlob;
    264         GrAtlasTextBlob::VertexRegenerator regenerator(
    265                 blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY, args.fColor,
    266                 target->deferredUploadTarget(), fFontCache, &glyphCache);
    267         GrAtlasTextBlob::VertexRegenerator::Result result;
    268         do {
    269             result = regenerator.regenerate();
    270             // Copy regenerated vertices from the blob to our vertex buffer.
    271             size_t vertexBytes = result.fGlyphsRegenerated * kVerticesPerGlyph * vertexStride;
    272             if (args.fClipRect.isEmpty()) {
    273                 memcpy(currVertex, result.fFirstVertex, vertexBytes);
    274             } else {
    275                 SkASSERT(!dfPerspective);
    276                 clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
    277                            result.fGlyphsRegenerated);
    278             }
    279             if (this->usesDistanceFields() && !args.fViewMatrix.isIdentity()) {
    280                 // We always do the distance field view matrix transformation after copying rather
    281                 // than during blob vertex generation time in the blob as handling successive
    282                 // arbitrary transformations would be complicated and accumulate error.
    283                 if (args.fViewMatrix.hasPerspective()) {
    284                     auto* pos = reinterpret_cast<SkPoint3*>(currVertex);
    285                     SkMatrixPriv::MapHomogeneousPointsWithStride(
    286                             args.fViewMatrix, pos, vertexStride, pos, vertexStride,
    287                             result.fGlyphsRegenerated * kVerticesPerGlyph);
    288                 } else {
    289                     auto* pos = reinterpret_cast<SkPoint*>(currVertex);
    290                     SkMatrixPriv::MapPointsWithStride(
    291                             args.fViewMatrix, pos, vertexStride,
    292                             result.fGlyphsRegenerated * kVerticesPerGlyph);
    293                 }
    294             }
    295             flushInfo.fGlyphsToFlush += result.fGlyphsRegenerated;
    296             if (!result.fFinished) {
    297                 this->flush(target, &flushInfo);
    298             }
    299             currVertex += vertexBytes;
    300         } while (!result.fFinished);
    301     }
    302     this->flush(target, &flushInfo);
    303 }
    304 
    305 void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
    306     GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
    307     GrMaskFormat maskFormat = this->maskFormat();
    308     if (gp->numTextureSamplers() != (int)fFontCache->getAtlasPageCount(maskFormat)) {
    309         // During preparation the number of atlas pages has increased.
    310         // Update the proxies used in the GP to match.
    311         if (this->usesDistanceFields()) {
    312             if (this->isLCD()) {
    313                 reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewProxies(
    314                     fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp());
    315             } else {
    316                 reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewProxies(
    317                     fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp());
    318             }
    319         } else {
    320             reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewProxies(
    321                 fFontCache->getProxies(maskFormat), GrSamplerState::ClampNearest());
    322         }
    323     }
    324 
    325     GrMesh mesh(GrPrimitiveType::kTriangles);
    326     int maxGlyphsPerDraw =
    327             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
    328     mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
    329                              flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
    330     mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
    331     target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline, mesh);
    332     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
    333     flushInfo->fGlyphsToFlush = 0;
    334 }
    335 
    336 bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
    337     GrAtlasTextOp* that = t->cast<GrAtlasTextOp>();
    338     if (fProcessors != that->fProcessors) {
    339         return false;
    340     }
    341 
    342     if (!fCanCombineOnTouchOrOverlap && GrRectsTouchOrOverlap(this->bounds(), that->bounds())) {
    343         return false;
    344     }
    345 
    346     if (fMaskType != that->fMaskType) {
    347         return false;
    348     }
    349 
    350     const SkMatrix& thisFirstMatrix = fGeoData[0].fViewMatrix;
    351     const SkMatrix& thatFirstMatrix = that->fGeoData[0].fViewMatrix;
    352 
    353     if (this->usesLocalCoords() && !thisFirstMatrix.cheapEqualTo(thatFirstMatrix)) {
    354         return false;
    355     }
    356 
    357     if (this->usesDistanceFields()) {
    358         if (fDFGPFlags != that->fDFGPFlags) {
    359             return false;
    360         }
    361 
    362         if (fLuminanceColor != that->fLuminanceColor) {
    363             return false;
    364         }
    365     } else {
    366         if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
    367             return false;
    368         }
    369     }
    370 
    371     // Keep the batch vertex buffer size below 32K so we don't have to create a special one
    372     // We use the largest possible vertex size for this
    373     static const int kVertexSize = sizeof(SkPoint) + sizeof(SkColor) + 2 * sizeof(uint16_t);
    374     static const int kMaxGlyphs = 32768 / (kVerticesPerGlyph * kVertexSize);
    375     if (this->fNumGlyphs + that->fNumGlyphs > kMaxGlyphs) {
    376         return false;
    377     }
    378 
    379     fNumGlyphs += that->numGlyphs();
    380 
    381     // Reallocate space for geo data if necessary and then import that geo's data.
    382     int newGeoCount = that->fGeoCount + fGeoCount;
    383 
    384     // We reallocate at a rate of 1.5x to try to get better total memory usage
    385     if (newGeoCount > fGeoDataAllocSize) {
    386         int newAllocSize = fGeoDataAllocSize + fGeoDataAllocSize / 2;
    387         while (newAllocSize < newGeoCount) {
    388             newAllocSize += newAllocSize / 2;
    389         }
    390         fGeoData.realloc(newAllocSize);
    391         fGeoDataAllocSize = newAllocSize;
    392     }
    393 
    394     // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
    395     // it doesn't try to unref them.
    396     memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
    397 #ifdef SK_DEBUG
    398     for (int i = 0; i < that->fGeoCount; ++i) {
    399         that->fGeoData.get()[i].fBlob = (Blob*)0x1;
    400     }
    401 #endif
    402     that->fGeoCount = 0;
    403     fGeoCount = newGeoCount;
    404 
    405     this->joinBounds(*that);
    406     return true;
    407 }
    408 
    409 // TODO trying to figure out why lcd is so whack
    410 // (see comments in GrAtlasTextContext::ComputeCanonicalColor)
    411 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
    412     const sk_sp<GrTextureProxy>* p = fFontCache->getProxies(this->maskFormat());
    413     bool isLCD = this->isLCD();
    414 
    415     SkMatrix localMatrix = SkMatrix::I();
    416     if (this->usesLocalCoords()) {
    417         // If this fails we'll just use I().
    418         bool result = fGeoData[0].fViewMatrix.invert(&localMatrix);
    419         (void)result;
    420     }
    421 
    422     // see if we need to create a new effect
    423     if (isLCD) {
    424         float redCorrection = fDistanceAdjustTable->getAdjustment(
    425                 SkColorGetR(fLuminanceColor) >> kDistanceAdjustLumShift,
    426                 fUseGammaCorrectDistanceTable);
    427         float greenCorrection = fDistanceAdjustTable->getAdjustment(
    428                 SkColorGetG(fLuminanceColor) >> kDistanceAdjustLumShift,
    429                 fUseGammaCorrectDistanceTable);
    430         float blueCorrection = fDistanceAdjustTable->getAdjustment(
    431                 SkColorGetB(fLuminanceColor) >> kDistanceAdjustLumShift,
    432                 fUseGammaCorrectDistanceTable);
    433         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
    434                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
    435                         redCorrection, greenCorrection, blueCorrection);
    436         return GrDistanceFieldLCDTextGeoProc::Make(p, GrSamplerState::ClampBilerp(), widthAdjust,
    437                                                    fDFGPFlags, localMatrix);
    438     } else {
    439 #ifdef SK_GAMMA_APPLY_TO_A8
    440         float correction = 0;
    441         if (kAliasedDistanceField_MaskType != fMaskType) {
    442             U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT,
    443                                                                 fLuminanceColor);
    444             correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
    445                                                              fUseGammaCorrectDistanceTable);
    446         }
    447         return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
    448                                                   correction, fDFGPFlags, localMatrix);
    449 #else
    450         return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
    451                                                   fDFGPFlags, localMatrix);
    452 #endif
    453     }
    454 }
    455 
    456