blob: 37abde9c83835776c7ba5c3f98a1b7e601350bc6 [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef sktext_gpu_SubRunContainer_DEFINED
#define sktext_gpu_SubRunContainer_DEFINED
#include "include/core/SkMatrix.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSpan.h"
#include "src/text/gpu/SubRunAllocator.h"
#include <cstddef>
#include <functional>
#include <iterator>
#include <memory>
#include <tuple>
#include <utility>
class SkCanvas;
class SkPaint;
class SkReadBuffer;
class SkStrikeClient;
class SkWriteBuffer;
struct SkIRect;
struct SkPoint;
struct SkStrikeDeviceInfo;
namespace sktext {
class GlyphRunList;
class StrikeForGPUCacheInterface;
}
namespace skgpu {
enum class MaskFormat : int;
}
#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
#include "src/gpu/ganesh/GrColor.h"
#include "src/gpu/ganesh/ops/GrOp.h"
class GrClip;
namespace skgpu::ganesh {
class SurfaceDrawContext;
}
#endif
namespace sktext::gpu {
class GlyphVector;
class Glyph;
class StrikeCache;
class VertexFiller;
using RegenerateAtlasDelegate = std::function<std::tuple<bool, int>(GlyphVector*,
int begin,
int end,
skgpu::MaskFormat,
int padding)>;
struct RendererData {
bool isSDF = false;
bool isLCD = false;
};
// -- AtlasSubRun --------------------------------------------------------------------------------
// AtlasSubRun is the API that AtlasTextOp uses to generate vertex data for drawing.
// There are three different ways AtlasSubRun is specialized.
// * DirectMaskSubRun* - this is by far the most common type of SubRun. The mask pixels are
// in 1:1 correspondence with the pixels on the device. The destination rectangles in this
// SubRun are in device space. This SubRun handles color glyphs.
// * TransformedMaskSubRun* - handles glyph where the image in the atlas needs to be
// transformed to the screen. It is usually used for large color glyph which can't be
// drawn with paths or scaled distance fields, but will be used to draw bitmap glyphs to
// the screen, if the matrix does not map 1:1 to the screen. The destination rectangles
// are in source space.
// * SDFTSubRun* - scaled distance field text handles largish single color glyphs that still
// can fit in the atlas; the sizes between direct SubRun, and path SubRun. The destination
// rectangles are in source space.
class AtlasSubRun {
public:
virtual ~AtlasSubRun() = default;
virtual SkSpan<const Glyph*> glyphs() const = 0;
virtual int glyphCount() const = 0;
virtual skgpu::MaskFormat maskFormat() const = 0;
virtual int glyphSrcPadding() const = 0;
virtual unsigned short instanceFlags() const = 0;
#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
virtual size_t vertexStride(const SkMatrix& drawMatrix) const = 0;
virtual std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
const GrClip*,
const SkMatrix& viewMatrix,
SkPoint drawOrigin,
const SkPaint&,
sk_sp<SkRefCnt>&& subRunStorage,
skgpu::ganesh::SurfaceDrawContext*) const = 0;
virtual void fillVertexData(
void* vertexDst, int offset, int count,
GrColor color,
const SkMatrix& drawMatrix,
SkPoint drawOrigin,
SkIRect clip) const = 0;
#endif
// This call is not thread safe. It should only be called from a known single-threaded env.
virtual std::tuple<bool, int> regenerateAtlas(
int begin, int end, RegenerateAtlasDelegate) const = 0;
virtual const VertexFiller& vertexFiller() const = 0;
virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0;
};
using AtlasDrawDelegate = std::function<void(const sktext::gpu::AtlasSubRun* subRun,
SkPoint drawOrigin,
const SkPaint& paint,
sk_sp<SkRefCnt> subRunStorage,
sktext::gpu::RendererData)>;
// -- SubRun -------------------------------------------------------------------------------------
// SubRun defines the most basic functionality of a SubRun; the ability to draw, and the
// ability to be in a list.
class SubRun;
using SubRunOwner = std::unique_ptr<SubRun, SubRunAllocator::Destroyer>;
class SubRun {
public:
virtual ~SubRun();
virtual void draw(SkCanvas*, SkPoint drawOrigin, const SkPaint&, sk_sp<SkRefCnt> subRunStorage,
const AtlasDrawDelegate&) const = 0;
void flatten(SkWriteBuffer& buffer) const;
static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer,
sktext::gpu::SubRunAllocator* alloc,
const SkStrikeClient* client);
// Size hint for unflattening this run. If this is accurate, it will help with the allocation
// of the slug. If it's off then there may be more allocations needed to unflatten.
virtual int unflattenSize() 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& positionMatrix) const = 0;
// Return the underlying atlas SubRun if it exists. Otherwise, return nullptr.
// * Don't use this API. It is only to support testing.
virtual const AtlasSubRun* testingOnly_atlasSubRun() const = 0;
protected:
enum SubRunStreamTag : int;
virtual SubRunStreamTag subRunStreamTag() const = 0;
virtual void doFlatten(SkWriteBuffer& buffer) const = 0;
private:
friend class SubRunList;
SubRunOwner fNext;
};
// -- SubRunList -----------------------------------------------------------------------------------
class SubRunList {
public:
class Iterator {
public:
using value_type = SubRun;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::input_iterator_tag;
Iterator(SubRun* subRun) : fPtr{subRun} { }
Iterator& operator++() { fPtr = fPtr->fNext.get(); return *this; }
Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
bool operator==(const Iterator& rhs) const { return fPtr == rhs.fPtr; }
bool operator!=(const Iterator& rhs) const { return fPtr != rhs.fPtr; }
reference operator*() { return *fPtr; }
private:
SubRun* fPtr;
};
void append(SubRunOwner subRun) {
SubRunOwner* newTail = &subRun->fNext;
*fTail = std::move(subRun);
fTail = newTail;
}
bool isEmpty() const { return fHead == nullptr; }
Iterator begin() { return Iterator{ fHead.get()}; }
Iterator end() { return Iterator{nullptr}; }
Iterator begin() const { return Iterator{ fHead.get()}; }
Iterator end() const { return Iterator{nullptr}; }
SubRun& front() const {return *fHead; }
private:
SubRunOwner fHead{nullptr};
SubRunOwner* fTail{&fHead};
};
// -- SubRunContainer ------------------------------------------------------------------------------
class SubRunContainer;
using SubRunContainerOwner = std::unique_ptr<SubRunContainer, SubRunAllocator::Destroyer>;
class SubRunContainer {
public:
explicit SubRunContainer(const SkMatrix& initialPositionMatrix);
SubRunContainer() = delete;
SubRunContainer(const SubRunContainer&) = delete;
SubRunContainer& operator=(const SubRunContainer&) = delete;
// Delete the move operations because the SubRuns contain pointers to fInitialPositionMatrix.
SubRunContainer(SubRunContainer&&) = delete;
SubRunContainer& operator=(SubRunContainer&&) = delete;
void flattenAllocSizeHint(SkWriteBuffer& buffer) const;
static int AllocSizeHintFromBuffer(SkReadBuffer& buffer);
void flattenRuns(SkWriteBuffer& buffer) const;
static SubRunContainerOwner MakeFromBufferInAlloc(SkReadBuffer& buffer,
const SkStrikeClient* client,
SubRunAllocator* alloc);
enum SubRunCreationBehavior {kAddSubRuns, kStrikeCalculationsOnly};
// The returned SubRunContainerOwner will never be null. If subRunCreation ==
// kStrikeCalculationsOnly, then the returned container will be empty.
[[nodiscard]] static SubRunContainerOwner MakeInAlloc(const GlyphRunList& glyphRunList,
const SkMatrix& positionMatrix,
const SkPaint& runPaint,
SkStrikeDeviceInfo strikeDeviceInfo,
StrikeForGPUCacheInterface* strikeCache,
sktext::gpu::SubRunAllocator* alloc,
SubRunCreationBehavior creationBehavior,
const char* tag);
static size_t EstimateAllocSize(const GlyphRunList& glyphRunList);
void draw(SkCanvas*, SkPoint drawOrigin, const SkPaint&, const SkRefCnt* subRunStorage,
const AtlasDrawDelegate&) const;
const SkMatrix& initialPosition() const { return fInitialPositionMatrix; }
bool isEmpty() const { return fSubRuns.isEmpty(); }
bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const;
private:
friend class TextBlobTools;
const SkMatrix fInitialPositionMatrix;
SubRunList fSubRuns;
};
// Returns the empty span if there is a problem reading the positions.
SkSpan<SkPoint> MakePointsFromBuffer(SkReadBuffer&, SubRunAllocator*);
} // namespace sktext::gpu
#endif // sktext_gpu_SubRunContainer_DEFINED