Use individual glyph positions to determine text bounds.
bug:8766924
Previously text bounds were calculated to be from 0 to totalAdvance in
the X, and from the font's top to bottom. These are incorrect,
especially in light of the font fallback mechanism.
Now, we calculate the bounds of the text as we layout each glyph.
Since these are much tighter bounds in the common case, this
significantly reduces the amount of clipping required (which in turn
enables more aggressive text merging).
Change-Id: I172e5466bf5975bf837af894a9964c41db538746
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9323a3a..e8ff1f1 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -85,8 +85,8 @@
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
+ DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -193,10 +193,10 @@
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, false);
+ return DrawBatch::replay(renderer, dirty, 0);
}
DrawOp* op = mOps[0];
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 990372e..80a1c24 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -220,6 +220,9 @@
DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+ DrawBoundedOp(const Rect& localBounds, SkPaint* paint)
+ : DrawOp(paint), mLocalBounds(localBounds) {}
+
// Calculates bounds as smallest rect encompassing all points
// NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
// subclass' constructor)
@@ -1272,23 +1275,10 @@
class DrawTextOp : public DrawBoundedOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length)
- : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
- mX(x), mY(y), mPositions(positions), mLength(length) {
- // duplicates bounds calculation from OpenGLRenderer::drawText, but doesn't alter mX
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
- mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds)
+ : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
+ mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
+ mLocalBounds.translate(x,y);
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
@@ -1314,7 +1304,7 @@
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mLength);
+ mPositions, getPaint(renderer), mTotalAdvance, mLocalBounds);
}
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
@@ -1327,7 +1317,8 @@
DrawTextOp& op = *((DrawTextOp*)ops[i]);
status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds,
+ drawOpMode);
}
return status;
}
@@ -1345,7 +1336,7 @@
float mX;
float mY;
const float* mPositions;
- float mLength;
+ float mTotalAdvance;
mat4 mPrecacheTransform;
};
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index bfd4086..6d85a16 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -420,17 +420,16 @@
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint,
- float length, DrawOpMode drawOpMode) {
+ float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
-
text = refText(text, bytesCount);
positions = refBuffer<float>(positions, count * 2);
paint = refPaint(paint);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, paint, totalAdvance, bounds);
addDrawOp(op);
return DrawGlInfo::kStatusDone;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index db08921..85d6107 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -121,7 +121,8 @@
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 038df07..890a310 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2846,8 +2846,8 @@
return fontTransform;
}
-status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length,
+status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == kDrawOpMode_Immediate &&
@@ -2855,24 +2855,8 @@
return DrawGlInfo::kStatusDone;
}
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
-
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
if (drawOpMode == kDrawOpMode_Immediate) {
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
- return DrawGlInfo::kStatusDone;
- }
+ if (quickReject(bounds)) return DrawGlInfo::kStatusDone;
} else {
// merged draw operations don't need scissor, but clip should still be valid
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
@@ -2923,7 +2907,7 @@
// TODO: Implement better clipping for scaled/rotated text
const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+ Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
@@ -2934,20 +2918,20 @@
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
}
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
- transform.mapRect(bounds);
+ transform.mapRect(layerBounds);
}
- dirtyLayerUnchecked(bounds, getRegion());
+ dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
+ drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
return DrawGlInfo::kStatusDrew;
}
@@ -3231,17 +3215,12 @@
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
float x, float y, SkPaint* paint) {
// Handle underline and strike-through
uint32_t flags = paint->getFlags();
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
SkPaint paintCopy(*paint);
- float underlineWidth = length;
- // If length is > 0.0f, we already measured the text for the text alignment
- if (length <= 0.0f) {
- underlineWidth = paintCopy.measureText(text, bytesCount);
- }
if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 597e458..23958ae 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -241,6 +241,9 @@
ANDROID_API const Rect& getClipBounds();
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
+ bool quickReject(const Rect& bounds) {
+ return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
bool quickRejectNoScissor(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual bool clipPath(SkPath* path, SkRegion::Op op);
@@ -280,7 +283,7 @@
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
@@ -824,12 +827,12 @@
*
* @param text The text to decor
* @param bytesCount The number of bytes in the text
- * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
* @param x The x coordinate where the text will be drawn
* @param y The y coordinate where the text will be drawn
* @param paint The paint to draw the text with
*/
- void drawTextDecorations(const char* text, int bytesCount, float length,
+ void drawTextDecorations(const char* text, int bytesCount, float totalAdvance,
float x, float y, SkPaint* paint);
/**