1 /* 2 * Copyright 2017 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 "SkAtlasTextTarget.h" 9 10 #include "GrClip.h" 11 #include "GrContextPriv.h" 12 #include "GrDrawingManager.h" 13 #include "GrMemoryPool.h" 14 #include "SkAtlasTextContext.h" 15 #include "SkAtlasTextFont.h" 16 #include "SkAtlasTextRenderer.h" 17 #include "SkGlyphRunPainter.h" 18 #include "SkGr.h" 19 #include "SkInternalAtlasTextContext.h" 20 #include "ops/GrAtlasTextOp.h" 21 #include "text/GrTextContext.h" 22 23 static constexpr int kMaxBatchLookBack = 10; 24 25 SkAtlasTextTarget::SkAtlasTextTarget(sk_sp<SkAtlasTextContext> context, int width, int height, 26 void* handle) 27 : fHandle(handle) 28 , fContext(std::move(context)) 29 , fWidth(width) 30 , fHeight(height) 31 , fMatrixStack(sizeof(SkMatrix), 4) 32 , fSaveCnt(0) { 33 fMatrixStack.push_back(); 34 this->accessCTM()->reset(); 35 } 36 37 SkAtlasTextTarget::~SkAtlasTextTarget() { fContext->renderer()->targetDeleted(fHandle); } 38 39 int SkAtlasTextTarget::save() { 40 const auto& currCTM = this->ctm(); 41 *static_cast<SkMatrix*>(fMatrixStack.push_back()) = currCTM; 42 return fSaveCnt++; 43 } 44 45 void SkAtlasTextTarget::restore() { 46 if (fSaveCnt) { 47 fMatrixStack.pop_back(); 48 fSaveCnt--; 49 } 50 } 51 52 void SkAtlasTextTarget::restoreToCount(int count) { 53 while (fSaveCnt > count) { 54 this->restore(); 55 } 56 } 57 58 void SkAtlasTextTarget::translate(SkScalar dx, SkScalar dy) { 59 this->accessCTM()->preTranslate(dx, dy); 60 } 61 62 void SkAtlasTextTarget::scale(SkScalar sx, SkScalar sy) { this->accessCTM()->preScale(sx, sy); } 63 64 void SkAtlasTextTarget::rotate(SkScalar degrees) { this->accessCTM()->preRotate(degrees); } 65 66 void SkAtlasTextTarget::rotate(SkScalar degrees, SkScalar px, SkScalar py) { 67 this->accessCTM()->preRotate(degrees, px, py); 68 } 69 70 void SkAtlasTextTarget::skew(SkScalar sx, SkScalar sy) { this->accessCTM()->preSkew(sx, sy); } 71 72 void SkAtlasTextTarget::concat(const SkMatrix& matrix) { this->accessCTM()->preConcat(matrix); } 73 74 ////////////////////////////////////////////////////////////////////////////// 75 76 static const GrColorSpaceInfo kColorSpaceInfo(nullptr, kRGBA_8888_GrPixelConfig); 77 static const SkSurfaceProps kProps( 78 SkSurfaceProps::kUseDistanceFieldFonts_Flag, kUnknown_SkPixelGeometry); 79 80 ////////////////////////////////////////////////////////////////////////////// 81 82 class SkInternalAtlasTextTarget : public GrTextTarget, public SkAtlasTextTarget { 83 public: 84 SkInternalAtlasTextTarget(sk_sp<SkAtlasTextContext> context, 85 int width, int height, 86 void* handle) 87 : GrTextTarget(width, height, kColorSpaceInfo) 88 , SkAtlasTextTarget(std::move(context), width, height, handle) 89 , fGlyphPainter(kProps, kColorSpaceInfo) { 90 fOpMemoryPool = fContext->internal().grContext()->priv().refOpMemoryPool(); 91 } 92 93 ~SkInternalAtlasTextTarget() override { 94 this->deleteOps(); 95 } 96 97 /** GrTextTarget overrides */ 98 99 void addDrawOp(const GrClip&, std::unique_ptr<GrAtlasTextOp> op) override; 100 101 void drawShape(const GrClip&, const SkPaint&, const SkMatrix& viewMatrix, 102 const GrShape&) override { 103 SkDebugf("Path glyph??"); 104 } 105 106 void makeGrPaint(GrMaskFormat, const SkPaint& skPaint, const SkMatrix&, 107 GrPaint* grPaint) override { 108 grPaint->setColor4f(skPaint.getColor4f().premul()); 109 } 110 111 GrContext* getContext() override { 112 return this->context()->internal().grContext(); 113 } 114 115 SkGlyphRunListPainter* glyphPainter() override { 116 return &fGlyphPainter; 117 } 118 119 /** SkAtlasTextTarget overrides */ 120 121 void drawText(const SkGlyphID[], const SkPoint[], int glyphCnt, uint32_t color, 122 const SkAtlasTextFont&) override; 123 void flush() override; 124 125 private: 126 void deleteOps(); 127 128 uint32_t fColor; 129 using SkAtlasTextTarget::fWidth; 130 using SkAtlasTextTarget::fHeight; 131 SkTArray<std::unique_ptr<GrAtlasTextOp>, true> fOps; 132 sk_sp<GrOpMemoryPool> fOpMemoryPool; 133 SkGlyphRunListPainter fGlyphPainter; 134 }; 135 136 ////////////////////////////////////////////////////////////////////////////// 137 138 std::unique_ptr<SkAtlasTextTarget> SkAtlasTextTarget::Make(sk_sp<SkAtlasTextContext> context, 139 int width, int height, void* handle) { 140 return std::unique_ptr<SkAtlasTextTarget>( 141 new SkInternalAtlasTextTarget(std::move(context), width, height, handle)); 142 } 143 144 ////////////////////////////////////////////////////////////////////////////// 145 146 void SkInternalAtlasTextTarget::drawText(const SkGlyphID glyphs[], const SkPoint positions[], 147 int glyphCnt, uint32_t color, 148 const SkAtlasTextFont& font) { 149 SkPaint paint; 150 paint.setAntiAlias(true); 151 152 // The atlas text context does munging of the paint color. We store the client's color here 153 // and then overwrite the generated op's color when addDrawOp() is called. 154 fColor = color; 155 156 SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag, kUnknown_SkPixelGeometry); 157 auto grContext = this->context()->internal().grContext(); 158 auto atlasTextContext = grContext->priv().drawingManager()->getTextContext(); 159 SkGlyphRunBuilder builder; 160 builder.drawGlyphsWithPositions(paint, font.makeFont(), 161 SkSpan<const SkGlyphID>{glyphs, SkTo<size_t>(glyphCnt)}, 162 positions); 163 auto glyphRunList = builder.useGlyphRunList(); 164 if (!glyphRunList.empty()) { 165 atlasTextContext->drawGlyphRunList(grContext, this, GrNoClip(), this->ctm(), props, 166 glyphRunList); 167 } 168 } 169 170 void SkInternalAtlasTextTarget::addDrawOp(const GrClip& clip, std::unique_ptr<GrAtlasTextOp> op) { 171 SkASSERT(clip.quickContains(SkRect::MakeIWH(fWidth, fHeight))); 172 // The SkAtlasTextRenderer currently only handles grayscale SDF glyphs. 173 if (op->maskType() != GrAtlasTextOp::kGrayscaleDistanceField_MaskType) { 174 return; 175 } 176 const GrCaps& caps = *this->context()->internal().grContext()->priv().caps(); 177 op->finalizeForTextTarget(fColor, caps); 178 int n = SkTMin(kMaxBatchLookBack, fOps.count()); 179 for (int i = 0; i < n; ++i) { 180 GrAtlasTextOp* other = fOps.fromBack(i).get(); 181 if (other->combineIfPossible(op.get(), caps) == GrOp::CombineResult::kMerged) { 182 fOpMemoryPool->release(std::move(op)); 183 return; 184 } 185 if (GrRectsOverlap(op->bounds(), other->bounds())) { 186 break; 187 } 188 } 189 fOps.emplace_back(std::move(op)); 190 } 191 192 void SkInternalAtlasTextTarget::deleteOps() { 193 for (int i = 0; i < fOps.count(); ++i) { 194 if (fOps[i]) { 195 fOpMemoryPool->release(std::move(fOps[i])); 196 } 197 } 198 fOps.reset(); 199 } 200 201 void SkInternalAtlasTextTarget::flush() { 202 for (int i = 0; i < fOps.count(); ++i) { 203 fOps[i]->executeForTextTarget(this); 204 } 205 this->context()->internal().flush(); 206 this->deleteOps(); 207 } 208 209 void GrAtlasTextOp::finalizeForTextTarget(uint32_t color, const GrCaps& caps) { 210 // TODO4F: Odd handling of client colors among AtlasTextTarget and AtlasTextRenderer 211 SkPMColor4f color4f = SkPMColor4f::FromBytes_RGBA(color); 212 for (int i = 0; i < fGeoCount; ++i) { 213 fGeoData[i].fColor = color4f; 214 } 215 // Atlas text doesn't use MSAA, so no need to handle a GrFSAAType. 216 // Also, no need to support normalized F16 with manual clamp? 217 this->finalize(caps, nullptr /* applied clip */, GrFSAAType::kNone, GrClampType::kAuto); 218 } 219 220 void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target) { 221 FlushInfo flushInfo; 222 SkExclusiveStrikePtr autoGlyphCache; 223 auto& context = target->context()->internal(); 224 auto glyphCache = context.grContext()->priv().getGrStrikeCache(); 225 auto atlasManager = context.grContext()->priv().getAtlasManager(); 226 auto resourceProvider = context.grContext()->priv().resourceProvider(); 227 228 unsigned int numProxies; 229 if (!atlasManager->getProxies(kA8_GrMaskFormat, &numProxies)) { 230 return; 231 } 232 233 for (int i = 0; i < fGeoCount; ++i) { 234 // TODO4F: Preserve float colors 235 GrTextBlob::VertexRegenerator regenerator( 236 resourceProvider, fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun, 237 fGeoData[i].fViewMatrix, fGeoData[i].fX, fGeoData[i].fY, 238 fGeoData[i].fColor.toBytes_RGBA(), &context, glyphCache, atlasManager, 239 &autoGlyphCache); 240 bool done = false; 241 while (!done) { 242 GrTextBlob::VertexRegenerator::Result result; 243 if (!regenerator.regenerate(&result)) { 244 break; 245 } 246 done = result.fFinished; 247 248 context.recordDraw(result.fFirstVertex, result.fGlyphsRegenerated, 249 fGeoData[i].fViewMatrix, target->handle()); 250 if (!result.fFinished) { 251 // Make space in the atlas so we can continue generating vertices. 252 context.flush(); 253 } 254 } 255 } 256 } 257