Correctly render text when coordinates are not integers.
Bug #3225632
Change-Id: If09759e6e95eb2885362ab3ba088cf5aae64c7bf
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 7462d5b..fe7f883 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -62,6 +62,11 @@
ALMOST_EQUAL(data[10], 1.0f));
}
+bool Matrix4::isPureTranslate() {
+ return mSimpleMatrix &&
+ ALMOST_EQUAL(data[kScaleX], 1.0f) && ALMOST_EQUAL(data[kScaleY], 1.0f);
+}
+
void Matrix4::load(const float* v) {
memcpy(data, v, sizeof(data));
mSimpleMatrix = false;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index d678bd0..23fc6c3 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -103,6 +103,8 @@
multiply(u);
}
+ bool isPureTranslate();
+
bool changesBounds();
void copyTo(float* v) const;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f3e5b16..b24c04f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -909,9 +909,19 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f,
- mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
- GL_TRIANGLE_STRIP, gMeshCount);
+ if (mSnapshot->transform->isPureTranslate()) {
+ const float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
+ const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
+
+ drawTextureMesh(x, y, x + (dstRight - dstLeft), y + (dstBottom - dstTop),
+ texture->id, alpha / 255.0f, mode, texture->blend,
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ GL_TRIANGLE_STRIP, gMeshCount, false, true);
+ } else {
+ drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ GL_TRIANGLE_STRIP, gMeshCount);
+ }
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
@@ -937,6 +947,7 @@
right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
if (mesh && mesh->verticesCount > 0) {
+ const bool pureTranslate = mSnapshot->transform->isPureTranslate();
#if RENDER_LAYERS_AS_REGIONS
// Mark the current layer dirty where we are going to draw the patch
if ((mSnapshot->flags & Snapshot::kFlagFboTarget) &&
@@ -944,16 +955,33 @@
const size_t count = mesh->quads.size();
for (size_t i = 0; i < count; i++) {
Rect bounds = mesh->quads.itemAt(i);
- dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
- *mSnapshot->transform);
+ if (pureTranslate) {
+ const float x = (int) floorf(bounds.left + 0.5f);
+ const float y = (int) floorf(bounds.top + 0.5f);
+ dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getBottom(),
+ *mSnapshot->transform);
+ } else {
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
+ *mSnapshot->transform);
+ }
}
}
#endif
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
- true, !mesh->hasEmptyQuads);
+ if (pureTranslate) {
+ const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
+ const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+
+ drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
+ GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer,
+ true, !mesh->hasEmptyQuads);
+ } else {
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
+ GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
+ true, !mesh->hasEmptyQuads);
+ }
}
}
@@ -1087,6 +1115,13 @@
break;
}
+ // TODO: Handle paint->getTextScaleX()
+ const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+ if (pureTranslate) {
+ x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
+ y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+ }
+
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
@@ -1110,7 +1145,7 @@
count, mShadowRadius);
const AutoTexture autoCleanup(shadow);
- setupShadow(shadow, x, y, mode, a);
+ setupShadow(shadow, x, y, mode, a, pureTranslate);
// Draw the mesh
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -1120,14 +1155,17 @@
GLuint textureUnit = 0;
glActiveTexture(gTextureUnits[textureUnit]);
- // Assume that the modelView matrix does not force scales, rotates, etc.
- const bool linearFilter = mSnapshot->transform->changesBounds();
+ // Pick the appropriate texture filtering
+ bool linearFilter = mSnapshot->transform->changesBounds();
+ if (pureTranslate && !linearFilter) {
+ linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
+ }
// Dimensions are set to (0,0), the layer (if any) won't be dirtied
setupTextureAlpha8(fontRenderer.getTexture(linearFilter), 0, 0, textureUnit,
- x, y, r, g, b, a, mode, false, true, NULL, NULL);
+ x, y, r, g, b, a, mode, false, true, NULL, NULL, 0, pureTranslate);
- const Rect& clip = mSnapshot->getLocalClip();
+ const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
#if RENDER_LAYERS_AS_REGIONS
@@ -1137,11 +1175,13 @@
#endif
mCaches.unbindMeshBuffer();
- if (fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y,
+ if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
hasLayer ? &bounds : NULL)) {
#if RENDER_LAYERS_AS_REGIONS
if (hasLayer) {
- mSnapshot->transform->mapRect(bounds);
+ if (!pureTranslate) {
+ mSnapshot->transform->mapRect(bounds);
+ }
bounds.intersect(*mSnapshot->clipRect);
bounds.snapToPixelBoundaries();
@@ -1241,7 +1281,7 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setupShadow(const ShadowTexture* texture, float x, float y,
- SkXfermode::Mode mode, float alpha) {
+ SkXfermode::Mode mode, float alpha, bool ignoreTransforms) {
const float sx = x - texture->left + mShadowDx;
const float sy = y - texture->top + mShadowDy;
@@ -1252,7 +1292,9 @@
const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f;
GLuint textureUnit = 0;
- setupTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, true, false);
+ setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit,
+ sx, sy, r, g, b, a, mode, true, false,
+ (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, 0, ignoreTransforms);
}
void OpenGLRenderer::setupTextureAlpha8(const Texture* texture, GLuint& textureUnit,
@@ -1273,7 +1315,7 @@
void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
SkXfermode::Mode mode, bool transforms, bool applyFilters,
- GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+ GLvoid* vertices, GLvoid* texCoords, GLuint vbo, bool ignoreTransform) {
// Describe the required shaders
ProgramDescription description;
description.hasTexture = true;
@@ -1321,9 +1363,14 @@
mModelView.loadIdentity();
}
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+ mat4 t;
+ if (!ignoreTransform) {
+ t.load(*mSnapshot->transform);
+ }
+
+ mCaches.currentProgram->set(mOrthoMatrix, mModelView, t);
if (width > 0 && height > 0) {
- dirtyLayer(x, y, x + width, y + height, *mSnapshot->transform);
+ dirtyLayer(x, y, x + width, y + height, t);
}
if (setColor) {
@@ -1334,6 +1381,9 @@
if (applyFilters) {
// Setup attributes and uniforms required by the shaders
if (mShader) {
+ if (ignoreTransform) {
+ mModelView.loadInverse(*mSnapshot->transform);
+ }
mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
}
if (mColorFilter) {
@@ -1473,7 +1523,11 @@
// Setup attributes and uniforms required by the shaders
if (mShader) {
- if (ignoreMatrix) mModelView.loadIdentity();
+ if (ignoreMatrix) {
+ mModelView.loadIdentity();
+ } else if (ignoreTransform) {
+ mModelView.loadInverse(*mSnapshot->transform);
+ }
mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
}
if (mColorFilter) {
@@ -1489,9 +1543,18 @@
setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
- texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLE_STRIP, gMeshCount);
+ if (mSnapshot->transform->isPureTranslate()) {
+ const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
+ const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+
+ drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
+ alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL,
+ (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true);
+ } else {
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+ texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
+ GL_TRIANGLE_STRIP, gMeshCount);
+ }
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index a942dde..e866d1b4 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -294,9 +294,10 @@
* @param y The y coordinate of the shadow
* @param mode The blending mode
* @param alpha The alpha value
+ * @param ignoreTransforms True if the coordinates are already in screen space
*/
void setupShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode,
- float alpha);
+ float alpha, bool ignoreTransforms = false);
/**
* Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
@@ -349,7 +350,7 @@
void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
SkXfermode::Mode mode, bool transforms, bool applyFilters,
- GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
+ GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0, bool ignoreTransform = false);
/**
* Draws text underline and strike-through if needed.