SkTextBlob: Begin implementing Extended TextBlob API
BUG=skia:5434 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2084533004 Review-Url: https://codereview.chromium.org/2084533004
This commit is contained in:
parent
fd7f867a05
commit
4f0a23a8d5
@ -213,10 +213,11 @@ private:
|
||||
// V45: Add invNormRotation to SkLightingShader.
|
||||
// V46: Add drawTextRSXform
|
||||
// V47: Add occluder rect to SkBlurMaskFilter
|
||||
// V48: Read and write extended SkTextBlobs.
|
||||
|
||||
// Only SKPs within the min/current picture version range (inclusive) can be read.
|
||||
static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39.
|
||||
static const uint32_t CURRENT_PICTURE_VERSION = 47;
|
||||
static const uint32_t CURRENT_PICTURE_VERSION = 48;
|
||||
|
||||
static_assert(MIN_PICTURE_VERSION <= 41,
|
||||
"Remove kFontFileName and related code from SkFontDescriptor.cpp.");
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "../private/SkTemplates.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkString.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkReadBuffer;
|
||||
@ -49,7 +50,7 @@ public:
|
||||
return MakeFromBuffer(buffer).release();
|
||||
}
|
||||
|
||||
enum GlyphPositioning {
|
||||
enum GlyphPositioning : uint8_t {
|
||||
kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph.
|
||||
kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph.
|
||||
kFull_Positioning = 2 // Point positioning -- two scalars per glyph.
|
||||
@ -112,11 +113,29 @@ public:
|
||||
/**
|
||||
* Glyph and position buffers associated with a run.
|
||||
*
|
||||
* A run is a sequence of glyphs sharing the same font metrics and positioning mode.
|
||||
* A run is a sequence of glyphs sharing the same font metrics
|
||||
* and positioning mode.
|
||||
*
|
||||
* If textByteCount is 0, utf8text and clusters will be NULL (no
|
||||
* character information will be associated with the glyphs).
|
||||
*
|
||||
* utf8text will point to a buffer of size textByteCount bytes.
|
||||
*
|
||||
* clusters (if not NULL) will point to an array of size count.
|
||||
* For each glyph, give the byte-offset into the text for the
|
||||
* first byte in the first character in that glyph's cluster.
|
||||
* Each value in the array should be an integer less than
|
||||
* textByteCount. Values in the array should either be
|
||||
* monotonically increasing (left-to-right text) or monotonically
|
||||
* decreasing (right-to-left text). This definiton is conviently
|
||||
* the same as used by Harfbuzz's hb_glyph_info_t::cluster field,
|
||||
* except that Harfbuzz interleaves glyphs and clusters.
|
||||
*/
|
||||
struct RunBuffer {
|
||||
SkGlyphID* glyphs;
|
||||
SkScalar* pos;
|
||||
char* utf8text;
|
||||
uint32_t* clusters;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -126,14 +145,27 @@ public:
|
||||
* @param font The font to be used for this run.
|
||||
* @param count Number of glyphs.
|
||||
* @param x,y Position within the blob.
|
||||
* @param textByteCount length of the original UTF-8 text that
|
||||
* corresponds to this sequence of glyphs. If 0,
|
||||
* text will not be included in the textblob.
|
||||
* @param lang Language code, currently unimplemented.
|
||||
* @param bounds Optional run bounding box. If known in advance (!= NULL), it will
|
||||
* be used when computing the blob bounds, to avoid re-measuring.
|
||||
*
|
||||
* @return A writable glyph buffer, valid until the next allocRun() or
|
||||
* build() call. The buffer is guaranteed to hold @count@ glyphs.
|
||||
*/
|
||||
const RunBuffer& allocRunText(const SkPaint& font,
|
||||
int count,
|
||||
SkScalar x,
|
||||
SkScalar y,
|
||||
int textByteCount,
|
||||
SkString lang,
|
||||
const SkRect* bounds = NULL);
|
||||
const RunBuffer& allocRun(const SkPaint& font, int count, SkScalar x, SkScalar y,
|
||||
const SkRect* bounds = NULL);
|
||||
const SkRect* bounds = NULL) {
|
||||
return this->allocRunText(font, count, x, y, 0, SkString(), bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new horizontally-positioned run and returns its writable glyph and position
|
||||
@ -142,14 +174,23 @@ public:
|
||||
* @param font The font to be used for this run.
|
||||
* @param count Number of glyphs.
|
||||
* @param y Vertical offset within the blob.
|
||||
* @param textByteCount length of the original UTF-8 text that
|
||||
* corresponds to this sequence of glyphs. If 0,
|
||||
* text will not be included in the textblob.
|
||||
* @param lang Language code, currently unimplemented.
|
||||
* @param bounds Optional run bounding box. If known in advance (!= NULL), it will
|
||||
* be used when computing the blob bounds, to avoid re-measuring.
|
||||
*
|
||||
* @return Writable glyph and position buffers, valid until the next allocRun()
|
||||
* or build() call. The buffers are guaranteed to hold @count@ elements.
|
||||
*/
|
||||
const RunBuffer& allocRunTextPosH(const SkPaint& font, int count, SkScalar y,
|
||||
int textByteCount, SkString lang,
|
||||
const SkRect* bounds = NULL);
|
||||
const RunBuffer& allocRunPosH(const SkPaint& font, int count, SkScalar y,
|
||||
const SkRect* bounds = NULL);
|
||||
const SkRect* bounds = NULL) {
|
||||
return this->allocRunTextPosH(font, count, y, 0, SkString(), bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new fully-positioned run and returns its writable glyph and position
|
||||
@ -157,6 +198,10 @@ public:
|
||||
*
|
||||
* @param font The font to be used for this run.
|
||||
* @param count Number of glyphs.
|
||||
* @param textByteCount length of the original UTF-8 text that
|
||||
* corresponds to this sequence of glyphs. If 0,
|
||||
* text will not be included in the textblob.
|
||||
* @param lang Language code, currently unimplemented.
|
||||
* @param bounds Optional run bounding box. If known in advance (!= NULL), it will
|
||||
* be used when computing the blob bounds, to avoid re-measuring.
|
||||
*
|
||||
@ -164,12 +209,18 @@ public:
|
||||
* or build() call. The glyph buffer and position buffer are
|
||||
* guaranteed to hold @count@ and 2 * @count@ elements, respectively.
|
||||
*/
|
||||
const RunBuffer& allocRunPos(const SkPaint& font, int count, const SkRect* bounds = NULL);
|
||||
const RunBuffer& allocRunTextPos(const SkPaint& font, int count,
|
||||
int textByteCount, SkString lang,
|
||||
const SkRect* bounds = NULL);
|
||||
const RunBuffer& allocRunPos(const SkPaint& font, int count,
|
||||
const SkRect* bounds = NULL) {
|
||||
return this->allocRunTextPos(font, count, 0, SkString(), bounds);
|
||||
}
|
||||
|
||||
private:
|
||||
void reserve(size_t size);
|
||||
void allocInternal(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
|
||||
int count, SkPoint offset, const SkRect* bounds);
|
||||
int count, int textBytes, SkPoint offset, const SkRect* bounds);
|
||||
bool mergeRun(const SkPaint& font, SkTextBlob::GlyphPositioning positioning,
|
||||
int count, SkPoint offset);
|
||||
void updateDeferredBounds();
|
||||
|
@ -105,17 +105,41 @@ static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_shou
|
||||
//
|
||||
// Each run record describes a text blob run, and can be used to determine the (implicit)
|
||||
// location of the following record.
|
||||
//
|
||||
// Extended Textblob runs have more data after the Pos[] array:
|
||||
//
|
||||
// -------------------------------------------------------------------------
|
||||
// ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
|
||||
// -------------------------------------------------------------------------
|
||||
//
|
||||
// To determine the length of the extended run data, the TextSize must be read.
|
||||
//
|
||||
// Extended Textblob runs may be mixed with non-extended runs.
|
||||
|
||||
SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
|
||||
|
||||
namespace {
|
||||
struct RunRecordStorageEquivalent {
|
||||
RunFont fFont;
|
||||
SkPoint fOffset;
|
||||
uint32_t fCount;
|
||||
uint32_t fFlags;
|
||||
SkDEBUGCODE(unsigned fMagic;)
|
||||
};
|
||||
}
|
||||
|
||||
class SkTextBlob::RunRecord {
|
||||
public:
|
||||
RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
|
||||
RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
|
||||
: fFont(font)
|
||||
, fCount(count)
|
||||
, fOffset(offset)
|
||||
, fPositioning(pos) {
|
||||
, fPositioning(pos)
|
||||
, fExtended(textSize > 0) {
|
||||
SkDEBUGCODE(fMagic = kRunRecordMagic);
|
||||
if (textSize > 0) {
|
||||
*this->textSizePtr() = textSize;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t glyphCount() const {
|
||||
@ -135,7 +159,8 @@ public:
|
||||
}
|
||||
|
||||
uint16_t* glyphBuffer() const {
|
||||
// Glyph are stored immediately following the record.
|
||||
static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
|
||||
// Glyphs are stored immediately following the record.
|
||||
return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
|
||||
}
|
||||
|
||||
@ -145,11 +170,31 @@ public:
|
||||
SkAlign4(fCount * sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) {
|
||||
uint32_t textSize() const { return fExtended ? *this->textSizePtr() : 0; }
|
||||
|
||||
uint32_t* clusterBuffer() const {
|
||||
// clusters follow the textSize.
|
||||
return fExtended ? 1 + this->textSizePtr() : nullptr;
|
||||
}
|
||||
|
||||
char* textBuffer() const {
|
||||
if (!fExtended) { return nullptr; }
|
||||
return reinterpret_cast<char*>(this->clusterBuffer() + fCount);
|
||||
}
|
||||
|
||||
static size_t StorageSize(int glyphCount, int textSize,
|
||||
SkTextBlob::GlyphPositioning positioning) {
|
||||
static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
|
||||
// RunRecord object + (aligned) glyph buffer + position buffer
|
||||
return SkAlignPtr(sizeof(SkTextBlob::RunRecord)
|
||||
+ SkAlign4(glyphCount* sizeof(uint16_t))
|
||||
+ glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning));
|
||||
size_t size = sizeof(SkTextBlob::RunRecord)
|
||||
+ SkAlign4(glyphCount* sizeof(uint16_t))
|
||||
+ PosCount(glyphCount, positioning) * sizeof(SkScalar);
|
||||
if (textSize > 0) { // Extended run.
|
||||
size += sizeof(uint32_t)
|
||||
+ sizeof(uint32_t) * glyphCount
|
||||
+ textSize;
|
||||
}
|
||||
return SkAlignPtr(size);
|
||||
}
|
||||
|
||||
static const RunRecord* First(const SkTextBlob* blob) {
|
||||
@ -158,20 +203,41 @@ public:
|
||||
}
|
||||
|
||||
static const RunRecord* Next(const RunRecord* run) {
|
||||
return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t*>(run)
|
||||
+ StorageSize(run->glyphCount(), run->positioning()));
|
||||
return reinterpret_cast<const RunRecord*>(
|
||||
reinterpret_cast<const uint8_t*>(run)
|
||||
+ StorageSize(run->glyphCount(), run->textSize(), run->positioning()));
|
||||
}
|
||||
|
||||
void validate(const uint8_t* storageTop) const {
|
||||
SkASSERT(kRunRecordMagic == fMagic);
|
||||
SkASSERT((uint8_t*)Next(this) <= storageTop);
|
||||
|
||||
SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
|
||||
SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) <= (SkScalar*)Next(this));
|
||||
if (fExtended) {
|
||||
SkASSERT(textSize() > 0);
|
||||
SkASSERT(textSizePtr() < (uint32_t*)Next(this));
|
||||
SkASSERT(clusterBuffer() < (uint32_t*)Next(this));
|
||||
SkASSERT(textBuffer() + textSize() <= (char*)Next(this));
|
||||
}
|
||||
static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
|
||||
"runrecord_should_stay_packed");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SkTextBlobBuilder;
|
||||
|
||||
static size_t PosCount(int glyphCount,
|
||||
SkTextBlob::GlyphPositioning positioning) {
|
||||
return glyphCount * ScalarsPerGlyph(positioning);
|
||||
}
|
||||
|
||||
uint32_t* textSizePtr() const {
|
||||
// textSize follows the position buffer.
|
||||
SkASSERT(fExtended);
|
||||
return (uint32_t*)(&this->posBuffer()[PosCount(fCount, fPositioning)]);
|
||||
}
|
||||
|
||||
void grow(uint32_t count) {
|
||||
SkScalar* initialPosBuffer = posBuffer();
|
||||
uint32_t initialCount = fCount;
|
||||
@ -189,6 +255,7 @@ private:
|
||||
uint32_t fCount;
|
||||
SkPoint fOffset;
|
||||
GlyphPositioning fPositioning;
|
||||
bool fExtended;
|
||||
|
||||
SkDEBUGCODE(unsigned fMagic;)
|
||||
};
|
||||
@ -218,6 +285,17 @@ SkTextBlob::~SkTextBlob() {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
union PositioningAndExtended {
|
||||
int32_t intValue;
|
||||
struct {
|
||||
SkTextBlob::GlyphPositioning positioning;
|
||||
bool extended;
|
||||
uint16_t padding;
|
||||
};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
|
||||
int runCount = fRunCount;
|
||||
|
||||
@ -230,7 +308,17 @@ void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
|
||||
SkASSERT(it.glyphCount() > 0);
|
||||
|
||||
buffer.write32(it.glyphCount());
|
||||
buffer.write32(it.positioning());
|
||||
PositioningAndExtended pe;
|
||||
pe.intValue = 0;
|
||||
pe.positioning = it.positioning();
|
||||
SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
|
||||
|
||||
uint32_t textSize = it.textSize();
|
||||
pe.extended = textSize > 0;
|
||||
buffer.write32(pe.intValue);
|
||||
if (pe.extended) {
|
||||
buffer.write32(textSize);
|
||||
}
|
||||
buffer.writePoint(it.offset());
|
||||
// This should go away when switching to SkFont
|
||||
it.applyFontToPaint(&runPaint);
|
||||
@ -239,6 +327,10 @@ void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
|
||||
buffer.writeByteArray(it.pos(),
|
||||
it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
|
||||
if (pe.extended) {
|
||||
buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
|
||||
buffer.writeByteArray(it.text(), it.textSize());
|
||||
}
|
||||
|
||||
it.next();
|
||||
SkDEBUGCODE(runCount--);
|
||||
@ -258,10 +350,14 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
|
||||
SkTextBlobBuilder blobBuilder;
|
||||
for (int i = 0; i < runCount; ++i) {
|
||||
int glyphCount = reader.read32();
|
||||
GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32());
|
||||
|
||||
PositioningAndExtended pe;
|
||||
pe.intValue = reader.read32();
|
||||
GlyphPositioning pos = pe.positioning;
|
||||
if (glyphCount <= 0 || pos > kFull_Positioning) {
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0;
|
||||
|
||||
SkPoint offset;
|
||||
reader.readPoint(&offset);
|
||||
@ -271,13 +367,15 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
|
||||
const SkTextBlobBuilder::RunBuffer* buf = nullptr;
|
||||
switch (pos) {
|
||||
case kDefault_Positioning:
|
||||
buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds);
|
||||
buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
|
||||
textSize, SkString(), &bounds);
|
||||
break;
|
||||
case kHorizontal_Positioning:
|
||||
buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds);
|
||||
buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
|
||||
textSize, SkString(), &bounds);
|
||||
break;
|
||||
case kFull_Positioning:
|
||||
buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds);
|
||||
buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@ -288,6 +386,13 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
|
||||
glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pe.extended) {
|
||||
if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
|
||||
!reader.readByteArray(buf->utf8text, textSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blobBuilder.make();
|
||||
@ -350,6 +455,20 @@ void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
|
||||
fCurrentRun->font().applyToPaint(paint);
|
||||
}
|
||||
|
||||
uint32_t* SkTextBlobRunIterator::clusters() const {
|
||||
SkASSERT(!this->done());
|
||||
return fCurrentRun->clusterBuffer();
|
||||
}
|
||||
uint32_t SkTextBlobRunIterator::textSize() const {
|
||||
SkASSERT(!this->done());
|
||||
return fCurrentRun->textSize();
|
||||
}
|
||||
char* SkTextBlobRunIterator::text() const {
|
||||
SkASSERT(!this->done());
|
||||
return fCurrentRun->textBuffer();
|
||||
}
|
||||
|
||||
|
||||
bool SkTextBlobRunIterator::isLCD() const {
|
||||
return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
|
||||
}
|
||||
@ -513,6 +632,10 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni
|
||||
fLastRun);
|
||||
SkASSERT(run->glyphCount() > 0);
|
||||
|
||||
if (run->textSize() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (run->positioning() != positioning
|
||||
|| run->font() != font
|
||||
|| (run->glyphCount() + count < run->glyphCount())) {
|
||||
@ -529,8 +652,8 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
|
||||
SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
|
||||
size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) -
|
||||
SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning);
|
||||
this->reserve(sizeDelta);
|
||||
|
||||
// reserve may have realloced
|
||||
@ -553,24 +676,25 @@ bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni
|
||||
|
||||
void SkTextBlobBuilder::allocInternal(const SkPaint &font,
|
||||
SkTextBlob::GlyphPositioning positioning,
|
||||
int count, SkPoint offset, const SkRect* bounds) {
|
||||
int count, int textSize, SkPoint offset, const SkRect* bounds) {
|
||||
SkASSERT(count > 0);
|
||||
SkASSERT(textSize >= 0);
|
||||
SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
|
||||
|
||||
if (!this->mergeRun(font, positioning, count, offset)) {
|
||||
if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
|
||||
this->updateDeferredBounds();
|
||||
|
||||
size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
|
||||
size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning);
|
||||
this->reserve(runSize);
|
||||
|
||||
SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
|
||||
SkASSERT(fStorageUsed + runSize <= fStorageSize);
|
||||
|
||||
SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
|
||||
SkTextBlob::RunRecord(count, offset, font, positioning);
|
||||
|
||||
SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
|
||||
fCurrentRunBuffer.glyphs = run->glyphBuffer();
|
||||
fCurrentRunBuffer.pos = run->posBuffer();
|
||||
fCurrentRunBuffer.utf8text = run->textBuffer();
|
||||
fCurrentRunBuffer.clusters = run->clusterBuffer();
|
||||
|
||||
fLastRun = fStorageUsed;
|
||||
fStorageUsed += runSize;
|
||||
@ -579,7 +703,8 @@ void SkTextBlobBuilder::allocInternal(const SkPaint &font,
|
||||
SkASSERT(fStorageUsed <= fStorageSize);
|
||||
run->validate(fStorage.get() + fStorageUsed);
|
||||
}
|
||||
|
||||
SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
|
||||
SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
|
||||
if (!fDeferredBounds) {
|
||||
if (bounds) {
|
||||
fBounds.join(*bounds);
|
||||
@ -589,26 +714,31 @@ void SkTextBlobBuilder::allocInternal(const SkPaint &font,
|
||||
}
|
||||
}
|
||||
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count,
|
||||
SkScalar x, SkScalar y,
|
||||
const SkRect* bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds);
|
||||
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
|
||||
SkScalar x, SkScalar y,
|
||||
int textByteCount,
|
||||
SkString lang,
|
||||
const SkRect* bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
|
||||
return fCurrentRunBuffer;
|
||||
}
|
||||
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count,
|
||||
SkScalar y,
|
||||
const SkRect* bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y),
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
|
||||
SkScalar y,
|
||||
int textByteCount,
|
||||
SkString lang,
|
||||
const SkRect* bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
|
||||
bounds);
|
||||
|
||||
return fCurrentRunBuffer;
|
||||
}
|
||||
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count,
|
||||
const SkRect *bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds);
|
||||
const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
|
||||
int textByteCount,
|
||||
SkString lang,
|
||||
const SkRect *bounds) {
|
||||
this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
|
||||
|
||||
return fCurrentRunBuffer;
|
||||
}
|
||||
@ -631,7 +761,8 @@ sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
|
||||
size_t validateSize = sizeof(SkTextBlob);
|
||||
const SkTextBlob::RunRecord* run = SkTextBlob::RunRecord::First(blob);
|
||||
for (int i = 0; i < fRunCount; ++i) {
|
||||
validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning);
|
||||
validateSize += SkTextBlob::RunRecord::StorageSize(
|
||||
run->fCount, run->textSize(), run->fPositioning);
|
||||
run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
|
||||
run = SkTextBlob::RunRecord::Next(run);
|
||||
}
|
||||
|
@ -28,6 +28,10 @@ public:
|
||||
const SkPoint& offset() const;
|
||||
void applyFontToPaint(SkPaint*) const;
|
||||
SkTextBlob::GlyphPositioning positioning() const;
|
||||
uint32_t* clusters() const;
|
||||
uint32_t textSize() const;
|
||||
char* text() const;
|
||||
|
||||
bool isLCD() const;
|
||||
|
||||
private:
|
||||
|
@ -349,3 +349,40 @@ DEF_TEST(TextBlob_builder, reporter) {
|
||||
DEF_TEST(TextBlob_paint, reporter) {
|
||||
TextBlobTester::TestPaintProps(reporter);
|
||||
}
|
||||
|
||||
DEF_TEST(TextBlob_extended, reporter) {
|
||||
SkTextBlobBuilder textBlobBuilder;
|
||||
SkPaint paint;
|
||||
const char text1[] = "Foo";
|
||||
const char text2[] = "Bar";
|
||||
|
||||
int glyphCount = paint.textToGlyphs(text1, strlen(text1), nullptr);
|
||||
SkAutoTMalloc<uint16_t> glyphs(glyphCount);
|
||||
(void)paint.textToGlyphs(text1, strlen(text1), glyphs.get());
|
||||
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
|
||||
auto run = textBlobBuilder.allocRunText(
|
||||
paint, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr);
|
||||
memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount);
|
||||
memcpy(run.utf8text, text2, strlen(text2));
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2)));
|
||||
}
|
||||
sk_sp<const SkTextBlob> blob(textBlobBuilder.build());
|
||||
REPORTER_ASSERT(reporter, blob);
|
||||
|
||||
for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
|
||||
REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount);
|
||||
for (uint32_t i = 0; i < it.glyphCount(); ++i) {
|
||||
REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, SkTextBlob::kDefault_Positioning == it.positioning());
|
||||
REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset());
|
||||
REPORTER_ASSERT(reporter, it.textSize() > 0);
|
||||
REPORTER_ASSERT(reporter, it.clusters());
|
||||
for (uint32_t i = 0; i < it.glyphCount(); ++i) {
|
||||
REPORTER_ASSERT(reporter, i == it.clusters()[i]);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize()));
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,9 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
|
||||
hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, NULL);
|
||||
hb_glyph_position_t* pos =
|
||||
hb_buffer_get_glyph_positions(buffer, NULL);
|
||||
auto runBuffer = builder->allocRunPos(paint, len);
|
||||
auto runBuffer = builder->allocRunTextPos(
|
||||
paint, SkToInt(len), SkToInt(textBytes), SkString());
|
||||
memcpy(runBuffer.utf8text, utf8text, textBytes);
|
||||
|
||||
double x = point.x();
|
||||
double y = point.y();
|
||||
@ -115,12 +117,14 @@ SkScalar SkShaper::shape(SkTextBlobBuilder* builder,
|
||||
|
||||
for (unsigned i = 0; i < len; i++) {
|
||||
runBuffer.glyphs[i] = info[i].codepoint;
|
||||
runBuffer.clusters[i] = info[i].cluster;
|
||||
reinterpret_cast<SkPoint*>(runBuffer.pos)[i] =
|
||||
SkPoint::Make(SkDoubleToScalar(x + pos[i].x_offset * textSizeX),
|
||||
SkDoubleToScalar(y - pos[i].y_offset * textSizeY));
|
||||
x += pos[i].x_advance * textSizeX;
|
||||
y += pos[i].y_advance * textSizeY;
|
||||
}
|
||||
|
||||
hb_buffer_clear_contents(buffer);
|
||||
return (SkScalar)x;
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ public:
|
||||
}
|
||||
SkTextBlobBuilder textBlobBuilder;
|
||||
shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0});
|
||||
sk_sp<const SkTextBlob> blob(textBlobBuilder.build());
|
||||
sk_sp<const SkTextBlob> blob = textBlobBuilder.make();
|
||||
pageCanvas->drawTextBlob(
|
||||
blob.get(), SkDoubleToScalar(current_x),
|
||||
SkDoubleToScalar(current_y), glyph_paint);
|
||||
|
Loading…
Reference in New Issue
Block a user