/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skgpu_ganesh_AtlasTextOp_DEFINED #define skgpu_ganesh_AtlasTextOp_DEFINED #include "src/gpu/AtlasTypes.h" #include "src/gpu/ganesh/GrColorSpaceXform.h" #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h" #include "src/gpu/ganesh/ops/GrMeshDrawOp.h" #include "src/text/gpu/TextBlob.h" class GrProgramInfo; class GrRecordingContext; namespace skgpu::ganesh { class AtlasTextOp final : public GrMeshDrawOp { public: DEFINE_OP_CLASS_ID ~AtlasTextOp() override { for (const Geometry* g = fHead; g != nullptr;) { const Geometry* next = g->fNext; g->~Geometry(); g = next; } } void* operator new(size_t s); void operator delete(void* b) noexcept; static void ClearCache(); struct Geometry { Geometry(const sktext::gpu::AtlasSubRun& subRun, const SkMatrix& drawMatrix, SkPoint drawOrigin, SkIRect clipRect, sk_sp&& supportData, const SkPMColor4f& color) : fSubRun{subRun} , fSupportDataKeepAlive{std::move(supportData)} , fDrawMatrix{drawMatrix} , fDrawOrigin{drawOrigin} , fClipRect{clipRect} , fColor{color} { SkASSERT(fSupportDataKeepAlive != nullptr); } static Geometry* Make(const sktext::gpu::AtlasSubRun& subRun, const SkMatrix& drawMatrix, SkPoint drawOrigin, SkIRect clipRect, sk_sp&& supportData, const SkPMColor4f& color, SkArenaAlloc* alloc); void fillVertexData(void* dst, int offset, int count) const; const sktext::gpu::AtlasSubRun& fSubRun; // Keep the TextBlob or Slug alive until the op is deleted. sk_sp fSupportDataKeepAlive; const SkMatrix fDrawMatrix; const SkPoint fDrawOrigin; // fClipRect is only used in the DirectMaskSubRun case to do geometric clipping. // TransformedMaskSubRun, and SDFTSubRun don't use this field, and expect an empty rect. const SkIRect fClipRect; // Color is updated after processor analysis if it was determined the shader resolves to // a constant color that we then evaluate on the CPU. // TODO: This can be made const once processor analysis is separated from op creation. SkPMColor4f fColor; Geometry* fNext{nullptr}; }; const char* name() const override { return "AtlasTextOp"; } void visitProxies(const GrVisitProxyFunc&) const override; FixedFunctionFlags fixedFunctionFlags() const override; GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override; enum class MaskType : uint32_t { kGrayscaleCoverage, kLCDCoverage, kColorBitmap, #if !defined(SK_DISABLE_SDF_TEXT) kAliasedDistanceField, kGrayscaleDistanceField, kLCDDistanceField, kLCDBGRDistanceField, kLast = kLCDBGRDistanceField #else kLast = kColorBitmap #endif }; inline static constexpr int kMaskTypeCount = static_cast(MaskType::kLast) + 1; #if GR_TEST_UTILS static GrOp::Owner CreateOpTestingOnly(skgpu::v1::SurfaceDrawContext*, const SkPaint&, const SkFont&, const SkMatrixProvider&, const char* text, int x, int y); #endif private: friend class GrOp; // for ctor struct FlushInfo { sk_sp fVertexBuffer; sk_sp fIndexBuffer; GrGeometryProcessor* fGeometryProcessor; const GrSurfaceProxy** fPrimProcProxies; int fGlyphsToFlush = 0; int fVertexOffset = 0; int fNumDraws = 0; }; AtlasTextOp(MaskType maskType, bool needsTransform, int glyphCount, SkRect deviceRect, Geometry* geo, const GrColorInfo& dstColorInfo, GrPaint&& paint); AtlasTextOp(MaskType maskType, bool needsTransform, int glyphCount, SkRect deviceRect, SkColor luminanceColor, bool useGammaCorrectDistanceTable, uint32_t DFGPFlags, Geometry* geo, GrPaint&& paint); GrProgramInfo* programInfo() override { // TODO [PI]: implement return nullptr; } void addGeometry(Geometry* geometry) { *fTail = geometry; // The geometry may have many entries. Find the end. do { fTail = &(*fTail)->fNext; } while (*fTail != nullptr); } void onCreateProgramInfo(const GrCaps*, SkArenaAlloc*, const GrSurfaceProxyView& writeView, bool usesMSAASurface, GrAppliedClip&&, const GrDstProxyView&, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) override { // We cannot surface the AtlasTextOp's programInfo at record time. As currently // implemented, the GP is modified at flush time based on the number of pages in the // atlas. } void onPrePrepareDraws(GrRecordingContext*, const GrSurfaceProxyView& writeView, GrAppliedClip*, const GrDstProxyView&, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) override { // TODO [PI]: implement } void onPrepareDraws(GrMeshDrawTarget*) override; void onExecute(GrOpFlushState*, const SkRect& chainBounds) override; #if GR_TEST_UTILS SkString onDumpInfo() const override; #endif skgpu::MaskFormat maskFormat() const { switch (this->maskType()) { case MaskType::kLCDCoverage: return skgpu::MaskFormat::kA565; case MaskType::kColorBitmap: return skgpu::MaskFormat::kARGB; case MaskType::kGrayscaleCoverage: #if !defined(SK_DISABLE_SDF_TEXT) case MaskType::kAliasedDistanceField: case MaskType::kGrayscaleDistanceField: case MaskType::kLCDDistanceField: case MaskType::kLCDBGRDistanceField: #endif return skgpu::MaskFormat::kA8; } // SkUNREACHABLE; return skgpu::MaskFormat::kA8; } #if !defined(SK_DISABLE_SDF_TEXT) bool usesDistanceFields() const { return MaskType::kAliasedDistanceField == this->maskType() || MaskType::kGrayscaleDistanceField == this->maskType() || MaskType::kLCDDistanceField == this->maskType() || MaskType::kLCDBGRDistanceField == this->maskType(); } bool isLCD() const { return MaskType::kLCDCoverage == this->maskType() || MaskType::kLCDDistanceField == this->maskType() || MaskType::kLCDBGRDistanceField == this->maskType(); } #else bool isLCD() const { return MaskType::kLCDCoverage == this->maskType(); } #endif inline void createDrawForGeneratedGlyphs( GrMeshDrawTarget* target, FlushInfo* flushInfo) const; MaskType maskType() const { return static_cast(fMaskType); } CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override; #if !defined(SK_DISABLE_SDF_TEXT) GrGeometryProcessor* setupDfProcessor(SkArenaAlloc*, const GrShaderCaps&, const SkMatrix& localMatrix, const GrSurfaceProxyView* views, unsigned int numActiveViews) const; #endif GrProcessorSet fProcessors; int fNumGlyphs; // Sum of glyphs in each geometry's subrun // All combinable atlas ops have equal bit field values uint32_t fDFGPFlags : 10; // Distance field properties uint32_t fMaskType : 3; // MaskType uint32_t fUsesLocalCoords : 1; // Filled in post processor analysis uint32_t fNeedsGlyphTransform : 1; uint32_t fHasPerspective : 1; // True if perspective affects draw uint32_t fUseGammaCorrectDistanceTable : 1; static_assert(kMaskTypeCount <= 8, "MaskType does not fit in 3 bits"); #if !defined(SK_DISABLE_SDF_TEXT) static_assert(kInvalid_DistanceFieldEffectFlag <= (1 << 9), "DFGP Flags do not fit in 10 bits"); #endif // Only needed for color emoji sk_sp fColorSpaceXform; // Only used for distance fields; per-channel luminance for LCD, or gamma-corrected luminance // for single-channel distance fields. const SkColor fLuminanceColor{0}; Geometry* fHead{nullptr}; Geometry** fTail{&fHead}; using INHERITED = GrMeshDrawOp; }; } // namespace skgpu::ganesh #endif // skgpu_ganesh_AtlasTextOp_DEFINED