blob: af5fd722114e7d60ca4671583970e47c61546bca [file] [log] [blame]
/*
* 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 GrTextBlob_DEFINED
#define GrTextBlob_DEFINED
#include <limits>
#include "include/core/SkPoint3.h"
#include "include/core/SkRefCnt.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkIPoint16.h"
#include "src/core/SkMaskFilterBase.h"
#include "src/core/SkOpts.h"
#include "src/core/SkRectPriv.h"
#include "src/core/SkStrikeSpec.h"
#include "src/core/SkTLazy.h"
#include "src/gpu/GrColor.h"
#include "src/gpu/GrDrawOpAtlas.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/text/GrStrikeCache.h"
class GrAtlasManager;
class GrAtlasTextOp;
class GrDeferredUploadTarget;
class GrDrawOp;
class GrGlyph;
class GrStrikeCache;
class GrSubRun;
class SkMatrixProvider;
class SkSurfaceProps;
class SkTextBlob;
class SkTextBlobRunIterator;
// A GrTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
// on the GPU. These are initially created with valid positions and colors, but invalid
// texture coordinates.
//
// A GrTextBlob contains a number of SubRuns that are created in the blob's arena. Each SubRun
// tracks its own GrGlyph* and vertex data. The memory is organized in the arena in the following
// way so that the pointers for the GrGlyph* and vertex data are known before creating the SubRun.
//
// GrGlyph*... | vertexData... | SubRun | GrGlyph*... | vertexData... | SubRun etc.
//
// In these classes, I'm trying to follow the convention about matrices and origins.
// * draw Matrix|Origin - describes the current draw command.
// * initial Matrix - describes the combined initial matrix and origin the GrTextBlob was created
// with.
//
//
class GrTextBlob final : public SkNVRefCnt<GrTextBlob>, public SkGlyphRunPainterInterface {
public:
struct Key {
Key();
uint32_t fUniqueID;
// Color may affect the gamma of the mask we generate, but in a fairly limited way.
// Each color is assigned to on of a fixed number of buckets based on its
// luminance. For each luminance bucket there is a "canonical color" that
// represents the bucket. This functionality is currently only supported for A8
SkColor fCanonicalColor;
SkPaint::Style fStyle;
SkScalar fFrameWidth;
SkScalar fMiterLimit;
SkPaint::Join fJoin;
SkPixelGeometry fPixelGeometry;
bool fHasBlur;
SkMaskFilterBase::BlurRec fBlurRec;
uint32_t fScalerContextFlags;
bool operator==(const Key& other) const;
};
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob);
// Change memory management to handle the data after GrTextBlob, but in the same allocation
// of memory. Only allow placement new.
void operator delete(void* p);
void* operator new(size_t);
void* operator new(size_t, void* p);
~GrTextBlob() override;
// Make an empty GrTextBlob, with all the invariants set to make the right decisions when
// adding SubRuns.
static sk_sp<GrTextBlob> Make(const SkGlyphRunList& glyphRunList,
const SkMatrix& drawMatrix);
static const Key& GetKey(const GrTextBlob& blob);
static uint32_t Hash(const Key& key);
void addKey(const Key& key);
bool hasPerspective() const;
const SkMatrix& initialMatrix() const { return fInitialMatrix; }
void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax);
std::tuple<SkScalar, SkScalar> scaleBounds() const {
return {fMaxMinScale, fMinMaxScale};
}
bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix);
const Key& key() const;
size_t size() const;
template<typename AddSingleMaskFormat>
void addMultiMaskFormat(
AddSingleMaskFormat addSingle,
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
SkPoint residual);
const SkTInternalLList<GrSubRun>& subRunList() const { return fSubRunList; }
private:
GrTextBlob(size_t allocSize, const SkMatrix& drawMatrix, SkColor initialLuminance);
void insertSubRun(GrSubRun* subRun);
// Methods to satisfy SkGlyphRunPainterInterface
void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
SkPoint residual) override;
void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkFont& runFont,
const SkStrikeSpec& strikeSpec) override;
void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
const SkFont& runFont,
SkScalar minScale,
SkScalar maxScale) override;
void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec) override;
// Overall size of this struct plus vertices and glyphs at the end.
const size_t fSize;
// The initial view matrix combined with the initial origin. Used to determine if a cached
// subRun can be used in this draw situation.
const SkMatrix fInitialMatrix;
const SkColor fInitialLuminance;
Key fKey;
// We can reuse distance field text, but only if the new view matrix would not result in
// a mip change. Because there can be multiple runs in a blob, we track the overall
// maximum minimum scale, and minimum maximum scale, we can support before we need to regen
SkScalar fMaxMinScale{-SK_ScalarMax};
SkScalar fMinMaxScale{SK_ScalarMax};
SkTInternalLList<GrSubRun> fSubRunList;
SkArenaAlloc fAlloc;
};
// -- GrSubRun -------------------------------------------------------------------------------------
class GrSubRun {
public:
virtual ~GrSubRun() = default;
// Produce GPU ops for this subRun.
virtual void draw(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const = 0;
// Given an already cached subRun, can this subRun handle this combination paint, matrix, and
// position.
virtual bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) = 0;
private:
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrSubRun);
};
// -- GrPathSubRun ---------------------------------------------------------------------------------
class GrPathSubRun final : public GrSubRun {
struct PathGlyph;
public:
GrPathSubRun(bool isAntiAliased,
const SkStrikeSpec& strikeSpec,
const GrTextBlob& blob,
SkSpan<PathGlyph> paths);
void draw(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) override;
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
bool isAntiAliased,
const SkStrikeSpec& strikeSpec,
const GrTextBlob& blob,
SkArenaAlloc* alloc);
private:
struct PathGlyph {
PathGlyph(const SkPath& path, SkPoint origin);
SkPath fPath;
SkPoint fOrigin;
};
const GrTextBlob& fBlob;
const bool fIsAntiAliased;
const SkStrikeSpec fStrikeSpec;
const SkSpan<const PathGlyph> fPaths;
};
// -- GrAtlasSubRun --------------------------------------------------------------------------------
class GrAtlasSubRun : public GrSubRun {
public:
static constexpr int kVerticesPerGlyph = 4;
virtual size_t vertexStride() const = 0;
virtual int glyphCount() const = 0;
virtual std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
makeAtlasTextOp(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const = 0;
virtual void fillVertexData(
void* vertexDst, int offset, int count,
GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin,
SkIRect clip) const = 0;
virtual void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache* cache) = 0;
// This call is not thread safe. It should only be called from GrDrawOp::onPrepare which
// is single threaded.
virtual std::tuple<bool, int> regenerateAtlas(
int begin, int end, GrMeshDrawOp::Target* target) const = 0;
};
// -- GrGlyphVector --------------------------------------------------------------------------------
class GrGlyphVector {
union Variant {
// Initially, filled with packed id, but changed to GrGlyph* in the onPrepare stage.
SkPackedGlyphID packedGlyphID;
GrGlyph* grGlyph;
};
public:
static GrGlyphVector Make(
const SkStrikeSpec& spec, SkSpan<SkGlyphVariant> glyphs, SkArenaAlloc* alloc);
SkSpan<const GrGlyph*> glyphs() const;
SkScalar strikeToSourceRatio() const { return fStrikeSpec.strikeToSourceRatio(); }
void packedGlyphIDToGrGlyph(GrStrikeCache* cache);
std::tuple<bool, int> regenerateAtlas(
int begin, int end,
GrMaskFormat maskFormat,
int srcPadding,
GrMeshDrawOp::Target *target,
bool bilerpPadding = false);
static size_t GlyphVectorSize(size_t count) {
return sizeof(Variant) * count;
}
private:
GrGlyphVector(const SkStrikeSpec& spec, SkSpan<Variant> glyphs);
const SkStrikeSpec fStrikeSpec;
SkSpan<Variant> fGlyphs;
sk_sp<GrTextStrike> fStrike{nullptr};
uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
};
// -- GrDirectMaskSubRun ---------------------------------------------------------------------------
class GrDirectMaskSubRun final : public GrAtlasSubRun {
public:
using VertexData = SkIPoint;
GrDirectMaskSubRun(GrMaskFormat format,
SkPoint residual,
GrTextBlob* blob,
const SkRect& bounds,
SkSpan<const VertexData> vertexData,
GrGlyphVector glyphs);
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format,
SkPoint residual,
GrTextBlob* blob,
SkArenaAlloc* alloc);
void draw(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) override;
size_t vertexStride() const override;
int glyphCount() const override;
std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
makeAtlasTextOp(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
std::tuple<bool, int>
regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const override;
void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
const SkMatrix& drawMatrix, SkPoint drawOrigin,
SkIRect clip) const override;
private:
// The rectangle that surrounds all the glyph bounding boxes in device space.
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
const GrMaskFormat fMaskFormat;
const SkPoint fResidual;
GrTextBlob* const fBlob;
// The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
const SkRect fVertexBounds;
const SkSpan<const VertexData> fVertexData;
// The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
// be single threaded.
mutable GrGlyphVector fGlyphs;
};
// -- GrTransformedMaskSubRun ----------------------------------------------------------------------
class GrTransformedMaskSubRun final : public GrAtlasSubRun {
public:
struct VertexData {
const SkPoint pos;
// The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
// device space rect.
GrIRect16 rect;
};
// SubRun for masks
GrTransformedMaskSubRun(GrMaskFormat format,
GrTextBlob* blob,
const SkRect& bounds,
SkSpan<const VertexData> vertexData,
GrGlyphVector glyphs);
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format,
SkPoint residual,
GrTextBlob* blob,
SkArenaAlloc* alloc);
void draw(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) override;
std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
makeAtlasTextOp(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
std::tuple<bool, int> regenerateAtlas(
int begin, int end, GrMeshDrawOp::Target* target) const override;
void fillVertexData(
void* vertexDst, int offset, int count,
GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin,
SkIRect clip) const override;
size_t vertexStride() const override;
int glyphCount() const override;
private:
bool hasW() const;
// The rectangle that surrounds all the glyph bounding boxes in device space.
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
const GrMaskFormat fMaskFormat;
GrTextBlob* fBlob;
// The bounds in source space. The bounds are the joined rectangles of all the glyphs.
const SkRect fVertexBounds;
const SkSpan<const VertexData> fVertexData;
// The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
// be single threaded.
mutable GrGlyphVector fGlyphs;
};
// -- GrSDFTSubRun ---------------------------------------------------------------------------------
// Hold data to draw Scaled Distance Field Text sub runs.
class GrSDFTSubRun final : public GrAtlasSubRun {
public:
struct VertexData {
const SkPoint pos;
// The rectangle of the glyphs in strike space.
GrIRect16 rect;
};
GrSDFTSubRun(GrMaskFormat format,
GrTextBlob* blob,
SkRect vertexBounds,
SkSpan<const VertexData> vertexData,
GrGlyphVector glyphs,
bool useLCDText,
bool antiAliased);
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkFont& runFont,
const SkStrikeSpec& strikeSpec,
GrTextBlob* blob,
SkArenaAlloc* alloc);
void draw(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) override;
std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
makeAtlasTextOp(const GrClip* clip,
const SkMatrixProvider& viewMatrix,
const SkGlyphRunList& glyphRunList,
GrRenderTargetContext* rtc) const override;
void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
std::tuple<bool, int> regenerateAtlas(
int begin, int end, GrMeshDrawOp::Target* target) const override;
void fillVertexData(
void* vertexDst, int offset, int count,
GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin,
SkIRect clip) const override;
size_t vertexStride() const override;
int glyphCount() const override;
private:
bool hasW() const;
// The rectangle that surrounds all the glyph bounding boxes in device space.
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
const GrMaskFormat fMaskFormat;
GrTextBlob* fBlob;
// The bounds in source space. The bounds are the joined rectangles of all the glyphs.
const SkRect fVertexBounds;
const SkSpan<const VertexData> fVertexData;
// The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
// be single threaded.
mutable GrGlyphVector fGlyphs;
const bool fUseLCDText;
const bool fAntiAliased;
};
#endif // GrTextBlob_DEFINED