blob: 551d98836a3e40ea172a07e573de30dde9966756 [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTextBlob.h"
SkTextBlob::SkTextBlob(uint16_t *glyphs, SkScalar *pos, const SkTArray<Run> *runs,
const SkRect& bounds)
: fGlyphBuffer(glyphs)
, fPosBuffer(pos)
, fRuns(runs)
, fBounds(bounds) {
}
uint32_t SkTextBlob::uniqueID() const {
static int32_t gTextBlobGenerationID; // = 0;
// loop in case our global wraps around, as we never want to return SK_InvalidGenID
while (SK_InvalidGenID == fUniqueID) {
fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1;
}
return fUniqueID;
}
SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob)
: fBlob(blob)
, fIndex(0) {
SkASSERT(NULL != blob);
}
bool SkTextBlob::RunIterator::done() const {
return NULL == fBlob->fRuns.get() || fIndex >= fBlob->fRuns->count();
}
void SkTextBlob::RunIterator::next() {
SkASSERT(!this->done());
fIndex++;
}
uint32_t SkTextBlob::RunIterator::glyphCount() const {
SkASSERT(!this->done());
return (*fBlob->fRuns)[fIndex].count;
}
const uint16_t* SkTextBlob::RunIterator::glyphs() const {
SkASSERT(!this->done());
return fBlob->fGlyphBuffer.get() + (*fBlob->fRuns)[fIndex].glyphStart;
}
const SkScalar* SkTextBlob::RunIterator::pos() const {
SkASSERT(!this->done());
return fBlob->fPosBuffer.get() + (*fBlob->fRuns)[fIndex].posStart;
}
const SkPoint& SkTextBlob::RunIterator::offset() const {
SkASSERT(!this->done());
return (*fBlob->fRuns)[fIndex].offset;
}
const SkPaint& SkTextBlob::RunIterator::font() const {
SkASSERT(!this->done());
return (*fBlob->fRuns)[fIndex].font;
}
SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const {
SkASSERT(!this->done());
return (*fBlob->fRuns)[fIndex].positioning;
}
SkTextBlobBuilder::SkTextBlobBuilder(unsigned runs)
: fRuns(NULL)
, fDeferredBounds(false) {
if (runs > 0) {
// if the number of runs is known, size our run storage accordingly.
fRuns = SkNEW(SkTArray<SkTextBlob::Run>(runs));
}
fBounds.setEmpty();
}
SkTextBlobBuilder::~SkTextBlobBuilder() {
// unused runs
SkDELETE(fRuns);
}
void SkTextBlobBuilder::updateDeferredBounds() {
SkASSERT(!fDeferredBounds || (NULL != fRuns && !fRuns->empty()));
if (!fDeferredBounds) {
return;
}
// FIXME: measure the current run & union bounds
fDeferredBounds = false;
}
void SkTextBlobBuilder::ensureRun(const SkPaint& font, SkTextBlob::GlyphPositioning pos,
const SkPoint& offset) {
SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
if (NULL == fRuns) {
fRuns = SkNEW(SkTArray<SkTextBlob::Run>());
}
// we can merge same-font/same-positioning runs in the following cases:
// * fully positioned run following another fully positioned run
// * horizontally postioned run following another horizontally positioned run with the same
// y-offset
if (!fRuns->empty()
&& fRuns->back().positioning == pos
&& fRuns->back().font == font
&& (SkTextBlob::kFull_Positioning == fRuns->back().positioning
|| (SkTextBlob::kHorizontal_Positioning == fRuns->back().positioning
&& fRuns->back().offset.y() == offset.y()))){
return;
}
this->updateDeferredBounds();
// start a new run
SkTextBlob::Run& newRun = fRuns->push_back();
newRun.count = 0;
newRun.glyphStart = fGlyphBuffer.count();
newRun.posStart = fPosBuffer.count();
newRun.offset = offset;
newRun.font = font;
newRun.positioning = pos;
}
void SkTextBlobBuilder::allocInternal(const SkPaint &font,
SkTextBlob::GlyphPositioning positioning,
int count, SkPoint offset, const SkRect* bounds) {
SkASSERT(count > 0);
this->ensureRun(font, positioning, offset);
// SkTextBlob::GlyphPositioning values are directly mapped to scalars-per-glyph.
unsigned posScalarsPerGlyph = positioning;
SkASSERT(posScalarsPerGlyph <= 2);
fGlyphBuffer.append(count);
fPosBuffer.append(count * posScalarsPerGlyph);
SkASSERT(NULL != fRuns && !fRuns->empty());
SkTextBlob::Run& run = fRuns->back();
run.count += count;
// The current run might have been merged, so the start offset may point to prev run data.
// Start from the back (which always points to the end of the current run buffers) instead.
fCurrentRunBuffer.glyphs = fGlyphBuffer.isEmpty()
? NULL : fGlyphBuffer.end() - count;
SkASSERT(NULL == fCurrentRunBuffer.glyphs || fCurrentRunBuffer.glyphs >= fGlyphBuffer.begin());
fCurrentRunBuffer.pos = fPosBuffer.isEmpty()
? NULL : fPosBuffer.end() - count * posScalarsPerGlyph;
SkASSERT(NULL == fCurrentRunBuffer.pos || fCurrentRunBuffer.pos >= fPosBuffer.begin());
if (!fDeferredBounds) {
if (NULL != bounds) {
fBounds.join(*bounds);
} else {
fDeferredBounds = true;
}
}
}
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);
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),
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);
return fCurrentRunBuffer;
}
const SkTextBlob* SkTextBlobBuilder::build() {
const SkTextBlob* blob;
if (fGlyphBuffer.count() > 0) {
// we have some glyphs, construct a real blob
SkASSERT(NULL != fRuns && !fRuns->empty());
this->updateDeferredBounds();
// ownership of all buffers is transferred to the blob
blob = SkNEW_ARGS(SkTextBlob, (fGlyphBuffer.detach(),
fPosBuffer.detach(),
fRuns,
fBounds));
fRuns = NULL;
fBounds.setEmpty();
} else {
// empty blob
SkASSERT(NULL == fRuns || fRuns->empty());
SkASSERT(fBounds.isEmpty());
blob = SkNEW_ARGS(SkTextBlob, (NULL, NULL, NULL, SkRect::MakeEmpty()));
}
return blob;
}