blob: f660aa7320a6293e35e94aafb89502210a341bcc [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkGlyphBuffer_DEFINED
#define SkGlyphBuffer_DEFINED
#include "src/core/SkEnumerate.h"
#include "src/core/SkGlyph.h"
#include "src/core/SkZip.h"
#include <climits>
class SkStrikeForGPU;
struct SkGlyphPositionRoundingSpec;
class SkPath;
class SkDrawable;
// SkSourceGlyphBuffer is the source of glyphs between the different stages of glyph drawing.
// It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs
// are reject by a stage they become the source for the next stage.
class SkSourceGlyphBuffer {
public:
SkSourceGlyphBuffer() = default;
void setSource(SkZip<const SkGlyphID, const SkPoint> source) {
this->~SkSourceGlyphBuffer();
new (this) SkSourceGlyphBuffer{source};
}
void reset();
void reject(size_t index) {
SkASSERT(index < fSource.size());
if (!this->sourceIsRejectBuffers()) {
// Need to expand the buffers for first use. All other reject sets will be fewer than
// this one.
auto [glyphID, pos] = fSource[index];
fRejectedGlyphIDs.push_back(glyphID);
fRejectedPositions.push_back(pos);
fRejectSize++;
} else {
SkASSERT(fRejectSize < fRejects.size());
fRejects[fRejectSize++] = fSource[index];
}
}
void reject(size_t index, int rejectedMaxDimension) {
auto [prevMin, prevMax] = fMaxDimensionHintForRejects;
fMaxDimensionHintForRejects =
{std::min(prevMin, rejectedMaxDimension),
std::max(prevMax, rejectedMaxDimension)};
this->reject(index);
}
SkZip<const SkGlyphID, const SkPoint> flipRejectsToSource() {
fRejects = SkMakeZip(fRejectedGlyphIDs, fRejectedPositions).first(fRejectSize);
fSource = fRejects;
fRejectSize = 0;
fMaxDimensionHintForSource = fMaxDimensionHintForRejects;
fMaxDimensionHintForRejects = {INT_MAX, 0};
return fSource;
}
SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; }
std::tuple<int, int> maxDimensionHint() const {return fMaxDimensionHintForSource;}
private:
SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) {
fSource = source;
}
bool sourceIsRejectBuffers() const {
return fSource.get<0>().data() == fRejectedGlyphIDs.data();
}
SkZip<const SkGlyphID, const SkPoint> fSource;
size_t fRejectSize{0};
// Calculate the smallest and largest max glyph dimension. fMaxDimensionHintForSource captures
// fMaxDimensionHintForRejects when flipping rejects to the source.
std::tuple<int, int> fMaxDimensionHintForSource{INT_MAX, 0};
std::tuple<int, int> fMaxDimensionHintForRejects{INT_MAX, 0};
SkZip<SkGlyphID, SkPoint> fRejects;
SkSTArray<4, SkGlyphID> fRejectedGlyphIDs;
SkSTArray<4, SkPoint> fRejectedPositions;
};
// A memory format that allows an SkPackedGlyphID, SkGlyph*, and SkPath* to occupy the same
// memory. This allows SkPackedGlyphIDs as input, and SkGlyph*/SkPath* as output using the same
// memory.
class SkGlyphVariant {
public:
SkGlyphVariant() : fV{nullptr} { }
SkGlyphVariant& operator= (SkPackedGlyphID packedID) {
fV.packedID = packedID;
SkDEBUGCODE(fTag = kPackedID);
return *this;
}
SkGlyphVariant& operator= (const SkGlyph* glyph) {
fV.glyph = glyph;
SkDEBUGCODE(fTag = kGlyph);
return *this;
}
SkGlyphVariant& operator= (const SkPath* path) {
fV.path = path;
SkDEBUGCODE(fTag = kPath);
return *this;
}
SkGlyphVariant& operator= (SkDrawable* drawable) {
fV.drawable = drawable;
SkDEBUGCODE(fTag = kDrawable);
return *this;
}
const SkGlyph* glyph() const {
SkASSERT(fTag == kGlyph);
return fV.glyph;
}
const SkPath* path() const {
SkASSERT(fTag == kPath);
return fV.path;
}
SkDrawable* drawable() const {
SkASSERT(fTag == kDrawable);
return fV.drawable;
}
SkPackedGlyphID packedID() const {
SkASSERT(fTag == kPackedID);
return fV.packedID;
}
operator SkPackedGlyphID() const { return this->packedID(); }
operator const SkGlyph*() const { return this->glyph(); }
operator const SkPath*() const { return this->path(); }
operator const SkDrawable*()const { return this->drawable(); }
private:
union {
const SkGlyph* glyph;
const SkPath* path;
SkDrawable* drawable;
SkPackedGlyphID packedID;
} fV;
#ifdef SK_DEBUG
enum {
kEmpty,
kPackedID,
kGlyph,
kPath,
kDrawable,
} fTag{kEmpty};
#endif
};
// A buffer for converting SkPackedGlyph to SkGlyph* or SkPath*. Initially the buffer contains
// SkPackedGlyphIDs, but those are used to lookup SkGlyph*/SkPath* which are then copied over the
// SkPackedGlyphIDs.
class SkDrawableGlyphBuffer {
public:
void ensureSize(size_t size);
// Load the buffer with SkPackedGlyphIDs and positions at (0, 0) ready to finish positioning
// during drawing.
void startSource(const SkZip<const SkGlyphID, const SkPoint>& source);
// Load the buffer with SkPackedGlyphIDs and positions using the device transform.
void startBitmapDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec);
// Load the buffer with SkPackedGlyphIDs, calculating positions so they can be constant.
//
// The positions are calculated integer positions in devices space, and the mapping of the
// the source origin through the initial matrix is returned. It is given that these positions
// are only reused when the blob is translated by an integral amount. Thus the shifted
// positions are given by the following equation where (ix, iy) is the integer positions of
// the glyph, initialMappedOrigin is (0,0) in source mapped to the device using the initial
// matrix, and newMappedOrigin is (0,0) in source mapped to the device using the current
// drawing matrix.
//
// (ix', iy') = (ix, iy) + round(newMappedOrigin - initialMappedOrigin)
//
// In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of
// floating point don't guarantee that, so force it to integer.
void startGPUDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
const SkMatrix& drawMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec);
SkString dumpInput() const;
// The input of SkPackedGlyphIDs
SkZip<SkGlyphVariant, SkPoint> input() {
SkASSERT(fPhase == kInput);
SkDEBUGCODE(fPhase = kProcess);
return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer.get(), fPositions};
}
// Store the glyph in the next slot, using the position information located at index from.
void accept(SkGlyph* glyph, size_t from) {
SkASSERT(fPhase == kProcess);
SkASSERT(fAcceptedSize <= from);
fPositions[fAcceptedSize] = fPositions[from];
fMultiBuffer[fAcceptedSize] = glyph;
fAcceptedSize++;
}
// Store the path in the next slot, using the position information located at index from.
void accept(const SkPath* path, size_t from) {
SkASSERT(fPhase == kProcess);
SkASSERT(fAcceptedSize <= from);
fPositions[fAcceptedSize] = fPositions[from];
fMultiBuffer[fAcceptedSize] = path;
fAcceptedSize++;
}
// Store drawable in the next slot, using the position information located at index from.
void accept(SkDrawable* drawable, size_t from) {
SkASSERT(fPhase == kProcess);
SkASSERT(fAcceptedSize <= from);
fPositions[fAcceptedSize] = fPositions[from];
fMultiBuffer[fAcceptedSize] = drawable;
fAcceptedSize++;
}
// The result after a series of `accept` of accepted SkGlyph* or SkPath*.
SkZip<SkGlyphVariant, SkPoint> accepted() {
SkASSERT(fPhase == kProcess);
SkDEBUGCODE(fPhase = kDraw);
return SkZip<SkGlyphVariant, SkPoint>{fAcceptedSize, fMultiBuffer.get(), fPositions};
}
bool empty() const {
SkASSERT(fPhase == kProcess || fPhase == kDraw);
return fAcceptedSize == 0;
}
void reset();
template <typename Fn>
void forEachInput(Fn&& fn) {
for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) {
fn(i, packedID.packedID(), pos);
}
}
private:
size_t fMaxSize{0};
size_t fInputSize{0};
size_t fAcceptedSize{0};
SkAutoTArray<SkGlyphVariant> fMultiBuffer;
SkAutoTMalloc<SkPoint> fPositions;
#ifdef SK_DEBUG
enum {
kReset,
kInput,
kProcess,
kDraw
} fPhase{kReset};
#endif
};
#endif // SkGlyphBuffer_DEFINED