summaryrefslogtreecommitdiffstats
path: root/libs/hwui/OpenGLRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/OpenGLRenderer.cpp')
-rw-r--r--libs/hwui/OpenGLRenderer.cpp707
1 files changed, 576 insertions, 131 deletions
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d9d7d23..6c9c0eb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -63,7 +63,7 @@ struct Blender {
// In this array, the index of each Blender equals the value of the first
// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
static const Blender gBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO },
+ { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
{ SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
{ SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
{ SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
@@ -81,7 +81,7 @@ static const Blender gBlends[] = {
// this array's SrcOver blending mode is actually DstOver. You can refer to
// createLayer() for more information on the purpose of this array.
static const Blender gBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO },
+ { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
{ SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
{ SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
{ SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
@@ -633,26 +633,64 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
}
}
+void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
+ float alpha = layer->alpha / 255.0f;
+
+ setupDraw();
+ if (layer->renderTarget == GL_TEXTURE_2D) {
+ setupDrawWithTexture();
+ } else {
+ setupDrawWithExternalTexture();
+ }
+ setupDrawTextureTransform();
+ setupDrawColor(alpha, alpha, alpha, alpha);
+ setupDrawColorFilter();
+ setupDrawBlending(layer->blend, layer->mode);
+ setupDrawProgram();
+ setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ if (layer->renderTarget == GL_TEXTURE_2D) {
+ setupDrawTexture(layer->texture);
+ } else {
+ setupDrawExternalTexture(layer->texture);
+ }
+ setupDrawTextureTransformUniforms(layer->texTransform);
+ setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+
+ finishDrawTexture();
+}
+
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
- const Rect& texCoords = layer->texCoords;
- resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
+ if (!layer->isTextureLayer) {
+ const Rect& texCoords = layer->texCoords;
+ resetDrawTextureTexCoords(texCoords.left, texCoords.top,
+ texCoords.right, texCoords.bottom);
- drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
- layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
- &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
+ drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
+ layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
+ &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
- resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ } else {
+ resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
+ drawTextureLayer(layer, rect);
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ }
}
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
#if RENDER_LAYERS_AS_REGIONS
-#if RENDER_LAYERS_RECT_AS_RECT
if (layer->region.isRect()) {
- composeLayerRect(layer, rect);
+ layer->setRegionAsRect();
+
+ composeLayerRect(layer, layer->regionRect);
+
layer->region.clear();
return;
}
-#endif
if (!layer->region.isEmpty()) {
size_t count;
@@ -881,12 +919,27 @@ void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
mDescription.hasAlpha8Texture = isAlpha8;
}
+void OpenGLRenderer::setupDrawWithExternalTexture() {
+ mDescription.hasExternalTexture = true;
+}
+
+void OpenGLRenderer::setupDrawAALine() {
+ mDescription.isAA = true;
+}
+
+void OpenGLRenderer::setupDrawPoint(float pointSize) {
+ mDescription.isPoint = true;
+ mDescription.pointSize = pointSize;
+}
+
void OpenGLRenderer::setupDrawColor(int color) {
setupDrawColor(color, (color >> 24) & 0xFF);
}
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorA = alpha / 255.0f;
+ // Second divide of a by 255 is an optimization, allowing us to simply multiply
+ // the rgb values by a instead of also dividing by 255
const float a = mColorA / 255.0f;
mColorR = a * ((color >> 16) & 0xFF);
mColorG = a * ((color >> 8) & 0xFF);
@@ -897,6 +950,8 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) {
void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
mColorA = alpha / 255.0f;
+ // Double-divide of a by 255 is an optimization, allowing us to simply multiply
+ // the rgb values by a instead of also dividing by 255
const float a = mColorA / 255.0f;
mColorR = a * ((color >> 16) & 0xFF);
mColorG = a * ((color >> 8) & 0xFF);
@@ -935,12 +990,26 @@ void OpenGLRenderer::setupDrawColorFilter() {
}
}
+void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
+ if (mColorSet && mode == SkXfermode::kClear_Mode) {
+ mColorA = 1.0f;
+ mColorR = mColorG = mColorB = 0.0f;
+ mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
+ }
+}
+
void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) {
+ // When the blending mode is kClear_Mode, we need to use a modulate color
+ // argb=1,0,0,0
+ accountForClear(mode);
chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
mDescription, swapSrcDst);
}
void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
+ // When the blending mode is kClear_Mode, we need to use a modulate color
+ // argb=1,0,0,0
+ accountForClear(mode);
chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
mDescription, swapSrcDst);
}
@@ -965,8 +1034,8 @@ void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float ri
}
}
-void OpenGLRenderer::setupDrawModelViewIdentity() {
- mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform);
+void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
+ mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset);
}
void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
@@ -989,6 +1058,11 @@ void OpenGLRenderer::setupDrawModelView(float left, float top, float right, floa
}
}
+void OpenGLRenderer::setupDrawPointUniforms() {
+ int slot = mCaches.currentProgram->getUniform("pointSize");
+ glUniform1f(slot, mDescription.pointSize);
+}
+
void OpenGLRenderer::setupDrawColorUniforms() {
if (mColorSet || (mShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -1036,6 +1110,23 @@ void OpenGLRenderer::setupDrawTexture(GLuint texture) {
glEnableVertexAttribArray(mTexCoordsSlot);
}
+void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
+ bindExternalTexture(texture);
+ glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
+
+ mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+ glEnableVertexAttribArray(mTexCoordsSlot);
+}
+
+void OpenGLRenderer::setupDrawTextureTransform() {
+ mDescription.hasTextureTransform = true;
+}
+
+void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
+ glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
+ GL_FALSE, &transform.data[0]);
+}
+
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
if (!vertices) {
mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
@@ -1049,6 +1140,41 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v
}
}
+void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
+ mCaches.unbindMeshBuffer();
+ glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gVertexStride, vertices);
+}
+
+/**
+ * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
+ * outer boundary that fades out to 0. The variables set in the shader define the proportion of
+ * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength
+ * attributes (one per vertex) are values from zero to one that tells the fragment
+ * shader where the fragment is in relation to the line width/length overall; these values are
+ * then used to compute the proper color, based on whether the fragment lies in the fading AA
+ * region of the line.
+ * Note that we only pass down the width values in this setup function. The length coordinates
+ * are set up for each individual segment.
+ */
+void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
+ GLvoid* lengthCoords, float boundaryWidthProportion) {
+ mCaches.unbindMeshBuffer();
+ glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gAAVertexStride, vertices);
+ int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+ glEnableVertexAttribArray(widthSlot);
+ glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
+ int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+ glEnableVertexAttribArray(lengthSlot);
+ glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
+ int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
+ glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
+ // Setting the inverse value saves computations per-fragment in the shader
+ int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
+ glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+}
+
void OpenGLRenderer::finishDrawTexture() {
glDisableVertexAttribArray(mTexCoordsSlot);
}
@@ -1072,6 +1198,50 @@ bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, u
return false;
}
+void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
+ if (displayList) {
+ displayList->output(*this, level);
+ }
+}
+
+void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) {
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+ float x = left;
+ float y = top;
+
+ bool ignoreTransform = false;
+ if (mSnapshot->transform->isPureTranslate()) {
+ x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
+ y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+ ignoreTransform = true;
+ }
+
+ setupDraw();
+ setupDrawWithTexture(true);
+ if (paint) {
+ setupDrawAlpha8Color(paint->getColor(), alpha);
+ }
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(true, mode);
+ setupDrawProgram();
+ setupDrawModelView(x, y, x + texture->width, y + texture->height, ignoreTransform);
+ setupDrawTexture(texture->id);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderUniforms();
+ setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+
+ finishDrawTexture();
+}
+
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
const float right = left + bitmap->width();
const float bottom = top + bitmap->height();
@@ -1085,7 +1255,11 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint
if (!texture) return;
const AutoTexture autoCleanup(texture);
- drawTextureRect(left, top, right, bottom, texture, paint);
+ if (bitmap->getConfig() == SkBitmap::kA8_Config) {
+ drawAlphaBitmap(texture, left, top, paint);
+ } else {
+ drawTextureRect(left, top, right, bottom, texture, paint);
+ }
}
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
@@ -1209,10 +1383,10 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
const float width = texture->width;
const float height = texture->height;
- const float u1 = srcLeft / width;
- const float v1 = srcTop / height;
- const float u2 = srcRight / width;
- const float v2 = srcBottom / height;
+ const float u1 = (srcLeft + 0.5f) / width;
+ const float v1 = (srcTop + 0.5f) / height;
+ const float u2 = (srcRight - 0.5f) / width;
+ const float v2 = (srcBottom - 0.5f) / height;
mCaches.unbindMeshBuffer();
resetDrawTextureTexCoords(u1, v1, u2, v2);
@@ -1297,117 +1471,375 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int
}
}
+/**
+ * This function uses a similar approach to that of AA lines in the drawLines() function.
+ * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
+ * shader to compute the translucency of the color, determined by whether a given pixel is
+ * within that boundary region and how far into the region it is.
+ */
+void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode) {
+ float inverseScaleX = 1.0f;
+ float inverseScaleY = 1.0f;
+ // The quad that we use needs to account for scaling.
+ if (!mSnapshot->transform->isPureTranslate()) {
+ Matrix4 *mat = mSnapshot->transform;
+ float m00 = mat->data[Matrix4::kScaleX];
+ float m01 = mat->data[Matrix4::kSkewY];
+ float m02 = mat->data[2];
+ float m10 = mat->data[Matrix4::kSkewX];
+ float m11 = mat->data[Matrix4::kScaleX];
+ float m12 = mat->data[6];
+ float scaleX = sqrt(m00 * m00 + m01 * m01);
+ float scaleY = sqrt(m10 * m10 + m11 * m11);
+ inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+ inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+ }
+
+ setupDraw();
+ setupDrawAALine();
+ setupDrawColor(color);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(true, mode);
+ setupDrawProgram();
+ setupDrawModelViewIdentity(true);
+ setupDrawColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderIdentityUniforms();
+
+ AAVertex rects[4];
+ AAVertex* aaVertices = &rects[0];
+ void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
+ void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+
+ float boundarySizeX = .5 * inverseScaleX;
+ float boundarySizeY = .5 * inverseScaleY;
+
+ // Adjust the rect by the AA boundary padding
+ left -= boundarySizeX;
+ right += boundarySizeX;
+ top -= boundarySizeY;
+ bottom += boundarySizeY;
+
+ float width = right - left;
+ float height = bottom - top;
+
+ float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
+ float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
+ int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
+ glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
+ glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion));
+
+ if (!quickReject(left, top, right, bottom)) {
+ AAVertex::set(aaVertices++, left, bottom, 1, 1);
+ AAVertex::set(aaVertices++, left, top, 1, 0);
+ AAVertex::set(aaVertices++, right, bottom, 0, 1);
+ AAVertex::set(aaVertices++, right, top, 0, 0);
+ dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+}
+
+/**
+ * We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization
+ * rules for those lines produces some unexpected results, and may vary between hardware devices.
+ * The basics of lines-as-quads is easy; we simply find the normal to the line and position the
+ * corners of the quads on either side of each line endpoint, separated by the strokeWidth
+ * of the line. Hairlines are more involved because we need to account for transform scaling
+ * to end up with a one-pixel-wide line in screen space..
+ * Anti-aliased lines add another factor to the approach. We use a specialized fragment shader
+ * in combination with values that we calculate and pass down in this method. The basic approach
+ * is that the quad we create contains both the core line area plus a bounding area in which
+ * the translucent/AA pixels are drawn. The values we calculate tell the shader what
+ * proportion of the width and the length of a given segment is represented by the boundary
+ * region. The quad ends up being exactly .5 pixel larger in all directions than the non-AA quad.
+ * The bounding region is actually 1 pixel wide on all sides (half pixel on the outside, half pixel
+ * on the inside). This ends up giving the result we want, with pixels that are completely
+ * 'inside' the line area being filled opaquely and the other pixels being filled according to
+ * how far into the boundary region they are, which is determined by shader interpolation.
+ */
void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
if (mSnapshot->isIgnored()) return;
const bool isAA = paint->isAntiAlias();
- const float strokeWidth = paint->getStrokeWidth() * 0.5f;
- // A stroke width of 0 has a special meaningin Skia:
- // it draws an unscaled 1px wide line
- const bool isHairLine = paint->getStrokeWidth() == 0.0f;
-
+ // We use half the stroke width here because we're going to position the quad
+ // corner vertices half of the width away from the line endpoints
+ float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
+ // A stroke width of 0 has a special meaning in Skia:
+ // it draws a line 1 px wide regardless of current transform
+ bool isHairLine = paint->getStrokeWidth() == 0.0f;
+ float inverseScaleX = 1.0f;
+ float inverseScaleY = 1.0f;
+ bool scaled = false;
int alpha;
SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- int verticesCount = count >> 2;
int generatedVerticesCount = 0;
- if (!isHairLine) {
- // TODO: AA needs more vertices
- verticesCount *= 6;
- } else {
- // TODO: AA will be different
- verticesCount *= 2;
+ int verticesCount = count;
+ if (count > 4) {
+ // Polyline: account for extra vertices needed for continuous tri-strip
+ verticesCount += (count - 4);
+ }
+
+ if (isHairLine || isAA) {
+ // The quad that we use for AA and hairlines needs to account for scaling. For hairlines
+ // the line on the screen should always be one pixel wide regardless of scale. For
+ // AA lines, we only want one pixel of translucent boundary around the quad.
+ if (!mSnapshot->transform->isPureTranslate()) {
+ Matrix4 *mat = mSnapshot->transform;
+ float m00 = mat->data[Matrix4::kScaleX];
+ float m01 = mat->data[Matrix4::kSkewY];
+ float m02 = mat->data[2];
+ float m10 = mat->data[Matrix4::kSkewX];
+ float m11 = mat->data[Matrix4::kScaleX];
+ float m12 = mat->data[6];
+ float scaleX = sqrt(m00*m00 + m01*m01);
+ float scaleY = sqrt(m10*m10 + m11*m11);
+ inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+ inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+ if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
+ scaled = true;
+ }
+ }
}
- TextureVertex lines[verticesCount];
- TextureVertex* vertex = &lines[0];
-
+ getAlphaAndMode(paint, &alpha, &mode);
setupDraw();
+ if (isAA) {
+ setupDrawAALine();
+ }
setupDrawColor(paint->getColor(), alpha);
setupDrawColorFilter();
setupDrawShader();
- setupDrawBlending(mode);
+ if (isAA) {
+ setupDrawBlending(true, mode);
+ } else {
+ setupDrawBlending(mode);
+ }
setupDrawProgram();
- setupDrawModelViewIdentity();
+ setupDrawModelViewIdentity(true);
setupDrawColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderIdentityUniforms();
- setupDrawMesh(vertex);
- if (!isHairLine) {
- // TODO: Handle the AA case
- for (int i = 0; i < count; i += 4) {
- // a = start point, b = end point
- vec2 a(points[i], points[i + 1]);
- vec2 b(points[i + 2], points[i + 3]);
-
- // Bias to snap to the same pixels as Skia
- a += 0.375;
- b += 0.375;
-
- // Find the normal to the line
- vec2 n = (b - a).copyNormalized() * strokeWidth;
- float x = n.x;
- n.x = -n.y;
- n.y = x;
-
- // Four corners of the rectangle defining a thick line
- vec2 p1 = a - n;
- vec2 p2 = a + n;
- vec2 p3 = b + n;
- vec2 p4 = b - n;
-
- const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
- const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
- const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
- const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
-
- if (!quickReject(left, top, right, bottom)) {
- // Draw the line as 2 triangles, could be optimized
- // by using only 4 vertices and the correct indices
- // Also we should probably used non textured vertices
- // when line AA is disabled to save on bandwidth
- TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
- TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f);
- TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
- TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
- TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
- TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f);
-
- generatedVerticesCount += 6;
-
- dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+ if (isHairLine) {
+ // Set a real stroke width to be used in quad construction
+ halfStrokeWidth = isAA? 1 : .5;
+ } else if (isAA && !scaled) {
+ // Expand boundary to enable AA calculations on the quad border
+ halfStrokeWidth += .5f;
+ }
+ Vertex lines[verticesCount];
+ Vertex* vertices = &lines[0];
+ AAVertex wLines[verticesCount];
+ AAVertex* aaVertices = &wLines[0];
+ if (!isAA) {
+ setupDrawVertices(vertices);
+ } else {
+ void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
+ void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+ // innerProportion is the ratio of the inner (non-AA) part of the line to the total
+ // AA stroke width (the base stroke width expanded by a half pixel on either side).
+ // This value is used in the fragment shader to determine how to fill fragments.
+ // We will need to calculate the actual width proportion on each segment for
+ // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
+ float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ }
+
+ AAVertex* prevAAVertex = NULL;
+ Vertex* prevVertex = NULL;
+
+ int boundaryLengthSlot = -1;
+ int inverseBoundaryLengthSlot = -1;
+ int boundaryWidthSlot = -1;
+ int inverseBoundaryWidthSlot = -1;
+ for (int i = 0; i < count; i += 4) {
+ // a = start point, b = end point
+ vec2 a(points[i], points[i + 1]);
+ vec2 b(points[i + 2], points[i + 3]);
+ float length = 0;
+ float boundaryLengthProportion = 0;
+ float boundaryWidthProportion = 0;
+
+ // Find the normal to the line
+ vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
+ if (isHairLine) {
+ if (isAA) {
+ float wideningFactor;
+ if (fabs(n.x) >= fabs(n.y)) {
+ wideningFactor = fabs(1.0f / n.x);
+ } else {
+ wideningFactor = fabs(1.0f / n.y);
+ }
+ n *= wideningFactor;
+ }
+ if (scaled) {
+ n.x *= inverseScaleX;
+ n.y *= inverseScaleY;
+ }
+ } else if (scaled) {
+ // Extend n by .5 pixel on each side, post-transform
+ vec2 extendedN = n.copyNormalized();
+ extendedN /= 2;
+ extendedN.x *= inverseScaleX;
+ extendedN.y *= inverseScaleY;
+ float extendedNLength = extendedN.length();
+ // We need to set this value on the shader prior to drawing
+ boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
+ n += extendedN;
+ }
+ float x = n.x;
+ n.x = -n.y;
+ n.y = x;
+
+ // aa lines expand the endpoint vertices to encompass the AA boundary
+ if (isAA) {
+ vec2 abVector = (b - a);
+ length = abVector.length();
+ abVector.normalize();
+ if (scaled) {
+ abVector.x *= inverseScaleX;
+ abVector.y *= inverseScaleY;
+ float abLength = abVector.length();
+ boundaryLengthProportion = abLength / (length + abLength);
+ } else {
+ boundaryLengthProportion = .5 / (length + 1);
}
+ abVector /= 2;
+ a -= abVector;
+ b += abVector;
}
- if (generatedVerticesCount > 0) {
- // GL_LINE does not give the result we want to match Skia
- glDrawArrays(GL_TRIANGLES, 0, generatedVerticesCount);
+ // Four corners of the rectangle defining a thick line
+ vec2 p1 = a - n;
+ vec2 p2 = a + n;
+ vec2 p3 = b + n;
+ vec2 p4 = b - n;
+
+
+ const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
+ const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
+ const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
+ const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
+
+ if (!quickReject(left, top, right, bottom)) {
+ if (!isAA) {
+ if (prevVertex != NULL) {
+ // Issue two repeat vertices to create degenerate triangles to bridge
+ // between the previous line and the new one. This is necessary because
+ // we are creating a single triangle_strip which will contain
+ // potentially discontinuous line segments.
+ Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]);
+ Vertex::set(vertices++, p1.x, p1.y);
+ generatedVerticesCount += 2;
+ }
+ Vertex::set(vertices++, p1.x, p1.y);
+ Vertex::set(vertices++, p2.x, p2.y);
+ Vertex::set(vertices++, p4.x, p4.y);
+ Vertex::set(vertices++, p3.x, p3.y);
+ prevVertex = vertices - 1;
+ generatedVerticesCount += 4;
+ } else {
+ if (!isHairLine && scaled) {
+ // Must set width proportions per-segment for scaled non-hairlines to use the
+ // correct AA boundary dimensions
+ if (boundaryWidthSlot < 0) {
+ boundaryWidthSlot =
+ mCaches.currentProgram->getUniform("boundaryWidth");
+ inverseBoundaryWidthSlot =
+ mCaches.currentProgram->getUniform("inverseBoundaryWidth");
+ }
+ glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
+ glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+ }
+ if (boundaryLengthSlot < 0) {
+ boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
+ inverseBoundaryLengthSlot =
+ mCaches.currentProgram->getUniform("inverseBoundaryLength");
+ }
+ glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
+ glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
+
+ if (prevAAVertex != NULL) {
+ // Issue two repeat vertices to create degenerate triangles to bridge
+ // between the previous line and the new one. This is necessary because
+ // we are creating a single triangle_strip which will contain
+ // potentially discontinuous line segments.
+ AAVertex::set(aaVertices++,prevAAVertex->position[0],
+ prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length);
+ AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
+ generatedVerticesCount += 2;
+ }
+ AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
+ AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
+ AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
+ AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
+ prevAAVertex = aaVertices - 1;
+ generatedVerticesCount += 4;
+ }
+ dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
+ a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
+ *mSnapshot->transform);
}
- } else {
- // TODO: Handle the AA case
- for (int i = 0; i < count; i += 4) {
- const float left = fmin(points[i], points[i + 1]);
- const float right = fmax(points[i], points[i + 1]);
- const float top = fmin(points[i + 2], points[i + 3]);
- const float bottom = fmax(points[i + 2], points[i + 3]);
+ }
+ if (generatedVerticesCount > 0) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
+ }
+}
+
+void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
+ if (mSnapshot->isIgnored()) return;
- if (!quickReject(left, top, right, bottom)) {
- TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
- TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f);
+ // TODO: The paint's cap style defines whether the points are square or circular
+ // TODO: Handle AA for round points
- generatedVerticesCount += 2;
+ // A stroke width of 0 has a special meaning in Skia:
+ // it draws an unscaled 1px point
+ float strokeWidth = paint->getStrokeWidth();
+ const bool isHairLine = paint->getStrokeWidth() == 0.0f;
+ if (isHairLine) {
+ // Now that we know it's hairline, we can set the effective width, to be used later
+ strokeWidth = 1.0f;
+ }
+ const float halfWidth = strokeWidth / 2;
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
- dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
- }
- }
+ int verticesCount = count >> 1;
+ int generatedVerticesCount = 0;
- if (generatedVerticesCount > 0) {
- glLineWidth(1.0f);
- glDrawArrays(GL_LINES, 0, generatedVerticesCount);
- }
+ TextureVertex pointsData[verticesCount];
+ TextureVertex* vertex = &pointsData[0];
+
+ setupDraw();
+ setupDrawPoint(strokeWidth);
+ setupDrawColor(paint->getColor(), alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(mode);
+ setupDrawProgram();
+ setupDrawModelViewIdentity(true);
+ setupDrawColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawPointUniforms();
+ setupDrawShaderIdentityUniforms();
+ setupDrawMesh(vertex);
+
+ for (int i = 0; i < count; i += 2) {
+ TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
+ generatedVerticesCount++;
+ float left = points[i] - halfWidth;
+ float right = points[i] + halfWidth;
+ float top = points[i + 1] - halfWidth;
+ float bottom = points [i + 1] + halfWidth;
+ dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
+
+ glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
}
void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -1502,7 +1934,11 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
}
int color = p->getColor();
- drawColorRect(left, top, right, bottom, color, mode);
+ if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
+ drawAARect(left, top, right, bottom, color, mode);
+ } else {
+ drawColorRect(left, top, right, bottom, color, mode);
+ }
}
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
@@ -1512,7 +1948,16 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
}
if (mSnapshot->isIgnored()) return;
+ // TODO: We should probably make a copy of the paint instead of modifying
+ // it; modifying the paint will change its generationID the first
+ // time, which might impact caches. More investigation needed to
+ // see if it matters.
+ // If we make a copy, then drawTextDecorations() should *not* make
+ // its own copy as it does right now.
paint->setAntiAlias(true);
+#if RENDER_TEXT_AS_GLYPHS
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+#endif
float length = -1.0f;
switch (paint->getTextAlign()) {
@@ -1546,27 +1991,36 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
if (mHasShadow) {
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
- const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount,
- count, mShadowRadius);
+ const ShadowTexture* shadow = mCaches.dropShadowCache.get(
+ paint, text, bytesCount, count, mShadowRadius);
const AutoTexture autoCleanup(shadow);
- const float sx = x - shadow->left + mShadowDx;
- const float sy = y - shadow->top + mShadowDy;
+ const float sx = oldX - shadow->left + mShadowDx;
+ const float sy = oldY - shadow->top + mShadowDy;
const int shadowAlpha = ((mShadowColor >> 24) & 0xFF);
+ int shadowColor = mShadowColor;
+ if (mShader) {
+ shadowColor = 0xffffffff;
+ }
glActiveTexture(gTextureUnits[0]);
setupDraw();
setupDrawWithTexture(true);
- setupDrawAlpha8Color(mShadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
+ setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
setupDrawBlending(true, mode);
setupDrawProgram();
- setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height, pureTranslate);
+ setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
setupDrawTexture(shadow->id);
setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderUniforms();
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+
finishDrawTexture();
}
@@ -1658,14 +2112,9 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
#if RENDER_LAYERS_AS_REGIONS
if (!layer->region.isEmpty()) {
-#if RENDER_LAYERS_RECT_AS_RECT
if (layer->region.isRect()) {
- const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight());
- composeLayerRect(layer, r);
+ composeLayerRect(layer, layer->regionRect);
} else if (layer->mesh) {
-#else
- if (layer->mesh) {
-#endif
const float a = alpha / 255.0f;
const Rect& rect = layer->layer;
@@ -1675,13 +2124,11 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
setupDrawColorFilter();
setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false);
setupDrawProgram();
+ setupDrawModelViewTranslate(x, y,
+ x + layer->layer.getWidth(), y + layer->layer.getHeight());
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawTexture(layer->texture);
- // TODO: The current layer, if any, will be dirtied with the bounding box
- // of the layer we are drawing. Since the layer we are drawing has
- // a mesh, we know the dirty region, we should use it instead
- setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
glDrawElements(GL_TRIANGLES, layer->meshElementCount,
@@ -1786,14 +2233,15 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
// 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 = paint->measureText(text, bytesCount);
+ underlineWidth = paintCopy.measureText(text, bytesCount);
}
float offsetX = 0;
- switch (paint->getTextAlign()) {
+ switch (paintCopy.getTextAlign()) {
case SkPaint::kCenter_Align:
offsetX = underlineWidth * 0.5f;
break;
@@ -1805,8 +2253,7 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
}
if (underlineWidth > 0.0f) {
- const float textSize = paint->getTextSize();
- // TODO: Support stroke width < 1.0f when we have AA lines
+ const float textSize = paintCopy.getTextSize();
const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
const float left = x - offsetX;
@@ -1836,10 +2283,9 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
points[currentPoint++] = top;
}
- SkPaint linesPaint(*paint);
- linesPaint.setStrokeWidth(strokeWidth);
+ paintCopy.setStrokeWidth(strokeWidth);
- drawLines(&points[0], pointsCount, &linesPaint);
+ drawLines(&points[0], pointsCount, &paintCopy);
}
}
}
@@ -2004,12 +2450,11 @@ void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mod
}
SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
- // In the future we should look at unifying the Porter-Duff modes and
- // SkXferModes so that we can use SkXfermode::IsMode(xfer, &mode).
- if (mode == NULL) {
- return SkXfermode::kSrcOver_Mode;
+ SkXfermode::Mode resultMode;
+ if (!SkXfermode::AsMode(mode, &resultMode)) {
+ resultMode = SkXfermode::kSrcOver_Mode;
}
- return mode->fMode;
+ return resultMode;
}
void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT) {