diff options
author | fmalita <fmalita@chromium.org> | 2014-08-21 08:53:26 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-21 08:53:26 -0700 |
commit | 00d5c2c6523321d25b32905ff4822f083a4173ee (patch) | |
tree | 7810daf8bac8a4105080a6c42889b47005a4709a /src | |
parent | 9fa60daad4d5f54c0dbe3dbcc7608a8f6d721187 (diff) |
SkTextBlob
Initial implementation.
R=bungeman@google.com, jbroman@chromium.org, mtklein@google.com, reed@google.com, robertphillips@google.com
Author: fmalita@chromium.org
Review URL: https://codereview.chromium.org/473633002
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkCanvas.cpp | 44 | ||||
-rw-r--r-- | src/core/SkRecordDraw.cpp | 1 | ||||
-rw-r--r-- | src/core/SkRecorder.cpp | 5 | ||||
-rw-r--r-- | src/core/SkRecorder.h | 4 | ||||
-rw-r--r-- | src/core/SkRecords.h | 26 | ||||
-rw-r--r-- | src/core/SkTextBlob.cpp | 221 |
6 files changed, 290 insertions, 11 deletions
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 104dfc488d..5a694db3b8 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -22,6 +22,7 @@ #include "SkSmallAllocator.h" #include "SkSurface_Base.h" #include "SkTemplates.h" +#include "SkTextBlob.h" #include "SkTextFormatParams.h" #include "SkTLazy.h" #include "SkUtils.h" @@ -2215,6 +2216,43 @@ void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPat LOOPER_END } +void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + SkASSERT(blob); + + // FIXME: dispatch to the device instead + + if (x || y) { + this->translate(x, y); + } + + SkTextBlob::RunIterator it(blob); + while (!it.done()) { + size_t textLen = it.glyphCount() * sizeof(uint16_t); + const SkPoint& offset = it.offset(); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + this->drawText(it.glyphs(), textLen, offset.x(), offset.y(), paint); + break; + case SkTextBlob::kHorizontal_Positioning: + this->drawPosTextH(it.glyphs(), textLen, it.pos(), offset.y(), paint); + break; + case SkTextBlob::kFull_Positioning: + this->drawPosText(it.glyphs(), textLen, (const SkPoint*)it.pos(), paint); + break; + default: + SkFAIL("unhandled positioning mode"); + } + + it.next(); + } + + if (x || y) { + this->translate(-x, -y); + } +} + // These will become non-virtual, so they always call the (virtual) onDraw... method void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { @@ -2232,6 +2270,12 @@ void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& const SkMatrix* matrix, const SkPaint& paint) { this->onDrawTextOnPath(text, byteLength, path, matrix, paint); } +void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + if (NULL != blob) { + this->onDrawTextBlob(blob, x, y, paint); + } +} void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint verts[], const SkPoint texs[], diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index a1c148a547..aa3e8de95a 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -89,6 +89,7 @@ DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); DRAW(DrawRect, drawRect(r.rect, r.paint)); DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint)); DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); +DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint)); DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, r.xmode.get(), r.indices, r.indexCount, r.paint)); diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index a2ef00bfe2..2e14c3e25d 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -187,6 +187,11 @@ void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkP this->copy(matrix)); } +void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + APPEND(DrawTextBlob, delay_copy(paint), blob, x, y); +} + void SkRecorder::onDrawPicture(const SkPicture* pic, const SkMatrix* matrix, const SkPaint* paint) { APPEND(DrawPicture, this->copy(paint), pic, this->copy(matrix)); } diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 1373f8a77f..8b1430d533 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -89,6 +89,10 @@ public: const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) SK_OVERRIDE; + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint); void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) SK_OVERRIDE; diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h index 6ec6e9ca2e..6baabe6b76 100644 --- a/src/core/SkRecords.h +++ b/src/core/SkRecords.h @@ -10,23 +10,22 @@ #include "SkCanvas.h" #include "SkPicture.h" +#include "SkTextBlob.h" -class SkPictureBox { +namespace SkRecords { + +template <typename T> +class RefBox : SkNoncopyable { public: - SkPictureBox(const SkPicture* obj) : fObj(SkRef(obj)) {} - ~SkPictureBox() { fObj->unref(); } + RefBox(const T* obj) : fObj(SkRef(obj)) {} + ~RefBox() { fObj->unref(); } - operator const SkPicture*() const { return fObj; } + operator const T*() const { return fObj; } private: - SkPictureBox(const SkPictureBox&); - SkPictureBox& operator=(const SkPictureBox&); - - const SkPicture* fObj; + const T* fObj; }; -namespace SkRecords { - // A list of all the types of canvas calls we can record. // Each of these is reified into a struct below. // @@ -68,6 +67,7 @@ namespace SkRecords { M(DrawRRect) \ M(DrawRect) \ M(DrawSprite) \ + M(DrawTextBlob) \ M(DrawVertices) // Defines SkRecords::Type, an enum of all record types. @@ -231,7 +231,7 @@ RECORD2(DrawOval, SkPaint, paint, SkRect, oval); RECORD1(DrawPaint, SkPaint, paint); RECORD2(DrawPath, SkPaint, paint, SkPath, path); //RECORD2(DrawPatch, SkPaint, paint, SkPatch, patch); -RECORD3(DrawPicture, Optional<SkPaint>, paint, SkPictureBox, picture, Optional<SkMatrix>, matrix); +RECORD3(DrawPicture, Optional<SkPaint>, paint, RefBox<SkPicture>, picture, Optional<SkMatrix>, matrix); RECORD4(DrawPoints, SkPaint, paint, SkCanvas::PointMode, mode, size_t, count, SkPoint*, pts); RECORD4(DrawPosText, SkPaint, paint, PODArray<char>, text, @@ -250,6 +250,10 @@ RECORD5(DrawText, SkPaint, paint, size_t, byteLength, SkScalar, x, SkScalar, y); +RECORD4(DrawTextBlob, SkPaint, paint, + RefBox<SkTextBlob>, blob, + SkScalar, x, + SkScalar, y); RECORD5(DrawTextOnPath, SkPaint, paint, PODArray<char>, text, size_t, byteLength, diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp new file mode 100644 index 0000000000..551d98836a --- /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; +} + |