Index: src/core/SkTextBlob.cpp |
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..551d98836a3e40ea172a07e573de30dde9966756 |
--- /dev/null |
+++ b/src/core/SkTextBlob.cpp |
@@ -0,0 +1,221 @@ |
+/* |
+ * 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; |
+} |
+ |