Chromium Code Reviews| Index: src/core/SkTextBlob.cpp |
| diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7a90d5501141966192a25dbc7af1e6921bdd7a02 |
| --- /dev/null |
| +++ b/src/core/SkTextBlob.cpp |
| @@ -0,0 +1,211 @@ |
| +/* |
| + * 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 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::alloc_internal(const SkPaint &font, |
| + SkTextBlob::GlyphPositioning positioning, |
| + size_t count, SkPoint offset, const SkRect* bounds) { |
| + this->ensureRun(font, positioning, offset); |
| + |
| + fGlyphBuffer.append(count); |
| + fPosBuffer.append(count * positioning); |
|
jbroman
2014/08/20 15:10:32
nit: It's not immediately obvious why this works (
f(malita)
2014/08/20 15:47:47
Done.
|
| + |
| + SkASSERT(NULL != fRuns && !fRuns->empty()); |
| + fRuns->back().count += count; |
| + |
| + if (!fDeferredBounds) { |
| + if (NULL != bounds) { |
| + fBounds.join(*bounds); |
| + } else { |
| + fDeferredBounds = true; |
| + } |
| + } |
| +} |
| + |
| +SkTextBlobBuilder::RunBuffer SkTextBlobBuilder::currentRunBuffer() { |
| + SkASSERT(NULL != fRuns && !fRuns->empty()); |
| + |
| + RunBuffer buffer; |
| + buffer.glyphs = fGlyphBuffer.isEmpty() |
| + ? NULL |
| + : fGlyphBuffer.begin() + fRuns->back().glyphStart; |
| + buffer.pos = fPosBuffer.isEmpty() |
| + ? NULL |
| + : fPosBuffer.begin() + fRuns->back().posStart; |
| + |
| + return buffer; |
| +} |
| + |
| +uint16_t* SkTextBlobBuilder::allocRun(const SkPaint& font, size_t count, SkPoint offset, |
| + const SkRect* bounds) { |
| + alloc_internal(font, SkTextBlob::kDefault_Positioning, count, offset, bounds); |
| + |
| + return fGlyphBuffer.begin() + fRuns->back().glyphStart; |
| +} |
| + |
| +SkTextBlobBuilder::RunBuffer SkTextBlobBuilder::allocRun(const SkPaint& font, size_t count, |
| + SkScalar yOffset, const SkRect* bounds) { |
|
robertphillips
2014/08/20 14:52:18
this->allocInternal ?
f(malita)
2014/08/20 15:47:47
Done.
|
| + alloc_internal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, yOffset), |
| + bounds); |
| + |
|
robertphillips
2014/08/20 14:52:18
this-> ?
f(malita)
2014/08/20 15:47:47
Done.
|
| + return currentRunBuffer(); |
| +} |
| + |
| +SkTextBlobBuilder::RunBuffer SkTextBlobBuilder::allocRun(const SkPaint& font, size_t count, |
| + const SkRect *bounds) { |
| + alloc_internal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds); |
| + |
|
robertphillips
2014/08/20 14:52:18
this-> ?
f(malita)
2014/08/20 15:47:47
Done.
|
| + return currentRunBuffer(); |
| +} |
| + |
| +const SkTextBlob* SkTextBlobBuilder::build() { |
| + const SkTextBlob* leBlob; |
|
jbroman
2014/08/20 15:10:32
nit: what does "le blob" mean? Why not "blob"?
f(malita)
2014/08/20 15:47:47
Done.
|
| + |
| + 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 |
| + leBlob = SkNEW_ARGS(SkTextBlob, (fGlyphBuffer.detach(), |
| + fPosBuffer.detach(), |
| + fRuns, |
| + fBounds)); |
| + fRuns = NULL; |
| + fBounds.setEmpty(); |
| + } else { |
| + // empty blob |
| + SkASSERT(NULL == fRuns || fRuns->empty()); |
| + SkASSERT(fBounds.isEmpty()); |
| + |
| + leBlob = SkNEW_ARGS(SkTextBlob, (NULL, NULL, NULL, SkRect::MakeEmpty())); |
| + } |
| + |
| + return leBlob; |
| +} |
| + |