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; |
+} |
+ |