summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Android.mk2
-rw-r--r--libs/hwui/Caches.cpp49
-rw-r--r--libs/hwui/Caches.h6
-rw-r--r--libs/hwui/Debug.h6
-rw-r--r--libs/hwui/DisplayListLogBuffer.cpp124
-rw-r--r--libs/hwui/DisplayListLogBuffer.h52
-rw-r--r--libs/hwui/DisplayListRenderer.cpp382
-rw-r--r--libs/hwui/DisplayListRenderer.h24
-rw-r--r--libs/hwui/FontRenderer.cpp82
-rw-r--r--libs/hwui/FontRenderer.h63
-rw-r--r--libs/hwui/Layer.h46
-rw-r--r--libs/hwui/LayerCache.cpp2
-rw-r--r--libs/hwui/LayerRenderer.cpp52
-rw-r--r--libs/hwui/LayerRenderer.h3
-rw-r--r--libs/hwui/Matrix.cpp4
-rw-r--r--libs/hwui/Matrix.h1
-rw-r--r--libs/hwui/OpenGLRenderer.cpp707
-rw-r--r--libs/hwui/OpenGLRenderer.h30
-rw-r--r--libs/hwui/Patch.cpp32
-rw-r--r--libs/hwui/Patch.h6
-rw-r--r--libs/hwui/PathCache.h4
-rw-r--r--libs/hwui/Program.cpp9
-rw-r--r--libs/hwui/Program.h2
-rw-r--r--libs/hwui/ProgramCache.cpp130
-rw-r--r--libs/hwui/ProgramCache.h29
-rw-r--r--libs/hwui/Properties.h5
-rw-r--r--libs/hwui/ShapeCache.h16
-rw-r--r--libs/hwui/TextDropShadowCache.h1
-rw-r--r--libs/hwui/Vertex.h47
-rw-r--r--libs/hwui/utils/GenerationCache.h245
30 files changed, 1655 insertions, 506 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index f4a0161..a98e4cd 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
+ DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
FboCache.cpp \
GradientCache.cpp \
@@ -42,7 +43,6 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
- LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4f5edd5..b0d8cf9 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "OpenGLRenderer"
#include <utils/Log.h>
+#include <utils/String8.h>
#include "Caches.h"
#include "Properties.h"
@@ -69,30 +70,43 @@ Caches::~Caches() {
///////////////////////////////////////////////////////////////////////////////
void Caches::dumpMemoryUsage() {
- LOGD("Current memory usage / total memory usage (bytes):");
- LOGD(" TextureCache %8d / %8d", textureCache.getSize(), textureCache.getMaxSize());
- LOGD(" LayerCache %8d / %8d", layerCache.getSize(), layerCache.getMaxSize());
- LOGD(" GradientCache %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize());
- LOGD(" PathCache %8d / %8d", pathCache.getSize(), pathCache.getMaxSize());
- LOGD(" CircleShapeCache %8d / %8d",
+ String8 stringLog;
+ dumpMemoryUsage(stringLog);
+ LOGD("%s", stringLog.string());
+ delete stringLog;
+}
+
+void Caches::dumpMemoryUsage(String8 &log) {
+ log.appendFormat("Current memory usage / total memory usage (bytes):\n");
+ log.appendFormat(" TextureCache %8d / %8d\n",
+ textureCache.getSize(), textureCache.getMaxSize());
+ log.appendFormat(" LayerCache %8d / %8d\n",
+ layerCache.getSize(), layerCache.getMaxSize());
+ log.appendFormat(" GradientCache %8d / %8d\n",
+ gradientCache.getSize(), gradientCache.getMaxSize());
+ log.appendFormat(" PathCache %8d / %8d\n",
+ pathCache.getSize(), pathCache.getMaxSize());
+ log.appendFormat(" CircleShapeCache %8d / %8d\n",
circleShapeCache.getSize(), circleShapeCache.getMaxSize());
- LOGD(" OvalShapeCache %8d / %8d",
+ log.appendFormat(" OvalShapeCache %8d / %8d\n",
ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
- LOGD(" RoundRectShapeCache %8d / %8d",
+ log.appendFormat(" RoundRectShapeCache %8d / %8d\n",
roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
- LOGD(" RectShapeCache %8d / %8d",
+ log.appendFormat(" RectShapeCache %8d / %8d\n",
rectShapeCache.getSize(), rectShapeCache.getMaxSize());
- LOGD(" ArcShapeCache %8d / %8d",
+ log.appendFormat(" ArcShapeCache %8d / %8d\n",
arcShapeCache.getSize(), arcShapeCache.getMaxSize());
- LOGD(" TextDropShadowCache %8d / %8d", dropShadowCache.getSize(),
+ log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
const uint32_t size = fontRenderer.getFontRendererSize(i);
- LOGD(" FontRenderer %d %8d / %8d", i, size, size);
+ log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size);
}
- LOGD("Other:");
- LOGD(" FboCache %8d / %8d", fboCache.getSize(), fboCache.getMaxSize());
- LOGD(" PatchCache %8d / %8d", patchCache.getSize(), patchCache.getMaxSize());
+ log.appendFormat("Other:\n");
+ log.appendFormat(" FboCache %8d / %8d\n",
+ fboCache.getSize(), fboCache.getMaxSize());
+ log.appendFormat(" PatchCache %8d / %8d\n",
+ patchCache.getSize(), patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
@@ -109,9 +123,8 @@ void Caches::dumpMemoryUsage() {
total += fontRenderer.getFontRendererSize(i);
}
- LOGD("Total memory usage:");
- LOGD(" %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f);
- LOGD("\n");
+ log.appendFormat("Total memory usage:\n");
+ log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0a9335f..596781e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -60,7 +60,12 @@ static const TextureVertex gMeshVertices[] = {
FV(1.0f, 1.0f, 1.0f, 1.0f)
};
static const GLsizei gMeshStride = sizeof(TextureVertex);
+static const GLsizei gVertexStride = sizeof(Vertex);
+static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
+static const GLsizei gAAVertexStride = sizeof(AAVertex);
static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
+static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
///////////////////////////////////////////////////////////////////////////////
@@ -140,6 +145,7 @@ public:
* Displays the memory usage of each cache and the total sum.
*/
void dumpMemoryUsage();
+ void dumpMemoryUsage(String8& log);
bool blend;
GLenum lastSrcMode;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 14471bc..2cdc8c3 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -26,7 +26,7 @@
// Turn on to enable memory usage summary on each frame
#define DEBUG_MEMORY_USAGE 0
-// Turn on to enable layers debugging when renderered as regions
+// Turn on to enable layers debugging when rendered as regions
#define DEBUG_LAYERS_AS_REGIONS 0
// Turn on to display debug info about vertex/fragment shaders
@@ -35,8 +35,10 @@
// Turn on to display info about layers
#define DEBUG_LAYERS 0
-// Turn on to display debug infor about 9patch objects
+// Turn on to display debug info about 9patch objects
#define DEBUG_PATCHES 0
+// Turn on to "explode" 9patch objects
+#define DEBUG_EXPLODE_PATCHES 0
// Turn on to display vertex and tex coords data about 9patch objects
// This flag requires DEBUG_PATCHES to be turned on
#define DEBUG_PATCHES_VERTICES 0
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
new file mode 100644
index 0000000..f204644
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayListLogBuffer.h"
+
+// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
+// that mStart always points at the next command, not just the next item
+#define COMMAND_SIZE 2
+#define NUM_COMMANDS 50
+#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1)
+
+/**
+ * DisplayListLogBuffer is a utility class which logs the most recent display
+ * list operations in a circular buffer. The log is process-wide, because we
+ * only care about the most recent operations, not the operations on a per-window
+ * basis for a given activity. The purpose of the log is to provide more debugging
+ * information in a bug report, by telling us not just where a process hung (which
+ * generally is just reported as a stack trace at the Java level) or crashed, but
+ * also what happened immediately before that hang or crash. This may help track down
+ * problems in the native rendering code or driver interaction related to the display
+ * list operations that led up to the hang or crash.
+ *
+ * The log is implemented as a circular buffer for both space and performance
+ * reasons - we only care about the last several operations to give us context
+ * leading up to the problem, and we don't want to constantly copy data around or do
+ * additional mallocs to keep the most recent operations logged. Only numbers are
+ * logged to make the operation fast. If and when the log is output, we process this
+ * data into meaningful strings.
+ *
+ * There is an assumption about the format of the command (currently 2 ints: the
+ * opcode and the nesting level). If the type of information logged changes (for example,
+ * we may want to save a timestamp), then the size of the buffer and the way the
+ * information is recorded in writeCommand() should change to suit.
+ */
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
+#endif
+
+namespace uirenderer {
+
+
+DisplayListLogBuffer::DisplayListLogBuffer() {
+ mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int));
+ mStart = mBufferFirst;
+ mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
+ mEnd = mStart;
+}
+
+DisplayListLogBuffer::~DisplayListLogBuffer() {
+ free(mBufferFirst);
+}
+
+/**
+ * Called from DisplayListRenderer to output the current buffer into the
+ * specified FILE. This only happens in a dumpsys/bugreport operation.
+ */
+void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[])
+{
+ int *tmpBufferPtr = mStart;
+ while (true) {
+ if (tmpBufferPtr == mEnd) {
+ break;
+ }
+ int level = *tmpBufferPtr++;
+ if (tmpBufferPtr > mBufferLast) {
+ tmpBufferPtr = mBufferFirst;
+ }
+ int op = *tmpBufferPtr++;
+ if (tmpBufferPtr > mBufferLast) {
+ tmpBufferPtr = mBufferFirst;
+ }
+ uint32_t count = (level + 1) * 2;
+ char indent[count + 1];
+ for (uint32_t i = 0; i < count; i++) {
+ indent[i] = ' ';
+ }
+ indent[count] = '\0';
+ fprintf(file, "%s%s\n", indent, opNames[op]);
+ }
+}
+
+void DisplayListLogBuffer::writeCommand(int level, int op) {
+ writeInt(level);
+ writeInt(op);
+}
+
+/**
+ * Store the given value in the buffer and increment/wrap the mEnd
+ * and mStart values as appropriate.
+ */
+void DisplayListLogBuffer::writeInt(int value) {
+ *((int*)mEnd) = value;
+ if (mEnd == mBufferLast) {
+ mEnd = mBufferFirst;
+ } else {
+ mEnd++;
+ }
+ if (mEnd == mStart) {
+ mStart++;
+ if (mStart > mBufferLast) {
+ mStart = mBufferFirst;
+ }
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
new file mode 100644
index 0000000..bf16f29
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+
+#include <utils/Singleton.h>
+#include <stdio.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
+ DisplayListLogBuffer();
+ ~DisplayListLogBuffer();
+
+ friend class Singleton<DisplayListLogBuffer>;
+
+public:
+ void writeCommand(int level, int op);
+ void writeInt(int value);
+ void outputCommands(FILE *file, const char* opNames[]);
+
+ bool isEmpty() {
+ return (mStart == mEnd);
+ }
+
+private:
+ int *mBufferFirst; // where the memory starts
+ int* mStart; // where the current command stream starts
+ int* mEnd; // where the current commands end
+ int* mBufferLast; // where the buffer memory ends
+
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 868290b..afab26a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -16,11 +16,16 @@
#define LOG_TAG "OpenGLRenderer"
+
+#include "DisplayListLogBuffer.h"
#include "DisplayListRenderer.h"
+#include <utils/String8.h>
+#include "Caches.h"
namespace android {
namespace uirenderer {
+
///////////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////////
@@ -53,6 +58,7 @@ const char* DisplayList::OP_NAMES[] = {
"DrawArc",
"DrawPath",
"DrawLines",
+ "DrawPoints",
"DrawText",
"ResetShader",
"SetupShader",
@@ -63,6 +69,20 @@ const char* DisplayList::OP_NAMES[] = {
"DrawGLFunction"
};
+void DisplayList::outputLogBuffer(int fd) {
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ if (logBuffer.isEmpty()) {
+ return;
+ }
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ FILE *file = fdopen(fd, "a");
+ fprintf(file, "\nCaches:\n%s", cachesLog.string());
+ fprintf(file, "\nRecent DisplayList operations\n");
+ logBuffer.outputCommands(file, OP_NAMES);
+ fflush(file);
+}
+
DisplayList::DisplayList(const DisplayListRenderer& recorder) {
initFromDisplayListRenderer(recorder);
}
@@ -92,13 +112,11 @@ void DisplayList::clearResources() {
mPaints.clear();
for (size_t i = 0; i < mPaths.size(); i++) {
- delete mPaths.itemAt(i);
+ SkPath* path = mPaths.itemAt(i);
+ caches.pathCache.remove(path);
+ delete path;
}
mPaths.clear();
- for (size_t i = 0; i < mOriginalPaths.size(); i++) {
- caches.resourceCache.decrementRefcount(mOriginalPaths.itemAt(i));
- }
- mOriginalPaths.clear();
for (size_t i = 0; i < mMatrices.size(); i++) {
delete mMatrices.itemAt(i);
@@ -150,13 +168,6 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
mPaths.add(paths.itemAt(i));
}
- const Vector<SkPath*> &originalPaths = recorder.getOriginalPaths();
- for (size_t i = 0; i < originalPaths.size(); i++) {
- SkPath* path = originalPaths.itemAt(i);
- mOriginalPaths.add(path);
- caches.resourceCache.incrementRefcount(path);
- }
-
const Vector<SkMatrix*> &matrices = recorder.getMatrices();
for (size_t i = 0; i < matrices.size(); i++) {
mMatrices.add(matrices.itemAt(i));
@@ -166,6 +177,331 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
void DisplayList::init() {
}
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
+ TextContainer text;
+
+ uint32_t count = (level + 1) * 2;
+ char indent[count + 1];
+ for (uint32_t i = 0; i < count; i++) {
+ indent[i] = ' ';
+ }
+ indent[count] = '\0';
+ LOGD("%sStart display list (%p)", (char*) indent + 2, this);
+
+ int saveCount = renderer.getSaveCount() - 1;
+
+ mReader.rewind();
+
+ while (!mReader.eof()) {
+ int op = mReader.readInt();
+
+ switch (op) {
+ case DrawGLFunction: {
+ Functor *functor = (Functor *) getInt();
+ LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
+ }
+ break;
+ case Save: {
+ int rendererNum = getInt();
+ LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum);
+ }
+ break;
+ case Restore: {
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case RestoreToCount: {
+ int restoreCount = saveCount + getInt();
+ LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount);
+ }
+ break;
+ case SaveLayer: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ SkPaint* paint = getPaint();
+ int flags = getInt();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
+ OP_NAMES[op], f1, f2, f3, f4, paint, flags);
+ }
+ break;
+ case SaveLayerAlpha: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ int alpha = getInt();
+ int flags = getInt();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent,
+ OP_NAMES[op], f1, f2, f3, f4, alpha, flags);
+ }
+ break;
+ case Translate: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], f1, f2);
+ }
+ break;
+ case Rotate: {
+ float rotation = getFloat();
+ LOGD("%s%s %.2f", (char*) indent, OP_NAMES[op], rotation);
+ }
+ break;
+ case Scale: {
+ float sx = getFloat();
+ float sy = getFloat();
+ LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
+ }
+ break;
+ case Skew: {
+ float sx = getFloat();
+ float sy = getFloat();
+ LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
+ }
+ break;
+ case SetMatrix: {
+ SkMatrix* matrix = getMatrix();
+ LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix);
+ }
+ break;
+ case ConcatMatrix: {
+ SkMatrix* matrix = getMatrix();
+ LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix);
+ }
+ break;
+ case ClipRect: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ int regionOp = getInt();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op],
+ f1, f2, f3, f4, regionOp);
+ }
+ break;
+ case DrawDisplayList: {
+ DisplayList* displayList = getDisplayList();
+ uint32_t width = getUInt();
+ uint32_t height = getUInt();
+ LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op],
+ displayList, width, height, level + 1);
+ renderer.outputDisplayList(displayList, level + 1);
+ }
+ break;
+ case DrawLayer: {
+ Layer* layer = (Layer*) getInt();
+ float x = getFloat();
+ float y = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
+ layer, x, y, paint);
+ }
+ break;
+ case DrawBitmap: {
+ SkBitmap* bitmap = getBitmap();
+ float x = getFloat();
+ float y = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
+ bitmap, x, y, paint);
+ }
+ break;
+ case DrawBitmapMatrix: {
+ SkBitmap* bitmap = getBitmap();
+ SkMatrix* matrix = getMatrix();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op],
+ bitmap, matrix, paint);
+ }
+ break;
+ case DrawBitmapRect: {
+ SkBitmap* bitmap = getBitmap();
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ float f5 = getFloat();
+ float f6 = getFloat();
+ float f7 = getFloat();
+ float f8 = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
+ (char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
+ }
+ break;
+ case DrawBitmapMesh: {
+ int verticesCount = 0;
+ uint32_t colorsCount = 0;
+ SkBitmap* bitmap = getBitmap();
+ uint32_t meshWidth = getInt();
+ uint32_t meshHeight = getInt();
+ float* vertices = getFloats(verticesCount);
+ bool hasColors = getInt();
+ int* colors = hasColors ? getInts(colorsCount) : NULL;
+ SkPaint* paint = getPaint();
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case DrawPatch: {
+ int32_t* xDivs = NULL;
+ int32_t* yDivs = NULL;
+ uint32_t* colors = NULL;
+ uint32_t xDivsCount = 0;
+ uint32_t yDivsCount = 0;
+ int8_t numColors = 0;
+ SkBitmap* bitmap = getBitmap();
+ xDivs = getInts(xDivsCount);
+ yDivs = getInts(yDivsCount);
+ colors = getUInts(numColors);
+ DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ getFloat();
+ getFloat();
+ getFloat();
+ getFloat();
+ getPaint();
+ }
+ break;
+ case DrawColor: {
+ int color = getInt();
+ int xferMode = getInt();
+ LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode);
+ }
+ break;
+ case DrawRect: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
+ f1, f2, f3, f4, paint);
+ }
+ break;
+ case DrawRoundRect: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ float f5 = getFloat();
+ float f6 = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
+ (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint);
+ }
+ break;
+ case DrawCircle: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %.2f, %.2f, %.2f, %p",
+ (char*) indent, OP_NAMES[op], f1, f2, f3, paint);
+ }
+ break;
+ case DrawOval: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p",
+ (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint);
+ }
+ break;
+ case DrawArc: {
+ float f1 = getFloat();
+ float f2 = getFloat();
+ float f3 = getFloat();
+ float f4 = getFloat();
+ float f5 = getFloat();
+ float f6 = getFloat();
+ int i1 = getInt();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
+ (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
+ }
+ break;
+ case DrawPath: {
+ SkPath* path = getPath();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint);
+ }
+ break;
+ case DrawLines: {
+ int count = 0;
+ float* points = getFloats(count);
+ SkPaint* paint = getPaint();
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case DrawPoints: {
+ int count = 0;
+ float* points = getFloats(count);
+ SkPaint* paint = getPaint();
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case DrawText: {
+ getText(&text);
+ int count = getInt();
+ float x = getFloat();
+ float y = getFloat();
+ SkPaint* paint = getPaint();
+ LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
+ text.text(), text.length(), count, x, y, paint);
+ }
+ break;
+ case ResetShader: {
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case SetupShader: {
+ SkiaShader* shader = getShader();
+ LOGD("%s%s %p", (char*) indent, OP_NAMES[op], shader);
+ }
+ break;
+ case ResetColorFilter: {
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case SetupColorFilter: {
+ SkiaColorFilter *colorFilter = getColorFilter();
+ LOGD("%s%s %p", (char*) indent, OP_NAMES[op], colorFilter);
+ }
+ break;
+ case ResetShadow: {
+ LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case SetupShadow: {
+ float radius = getFloat();
+ float dx = getFloat();
+ float dy = getFloat();
+ int color = getInt();
+ LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op],
+ radius, dx, dy, color);
+ }
+ break;
+ default:
+ LOGD("Display List error: op not handled: %s%s",
+ (char*) indent, OP_NAMES[op]);
+ break;
+ }
+ }
+
+ LOGD("%sDone", (char*) indent + 2);
+}
+
+/**
+ * Changes to replay(), specifically those involving opcode or parameter changes, should be mimicked
+ * in the output() function, since that function processes the same list of opcodes for the
+ * purposes of logging display list info for a given view.
+ */
bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) {
bool needsInvalidate = false;
TextContainer text;
@@ -181,9 +517,11 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
#endif
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
while (!mReader.eof()) {
int op = mReader.readInt();
+ logBuffer.writeCommand(level, op);
switch (op) {
case DrawGLFunction: {
@@ -452,6 +790,13 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
renderer.drawLines(points, count, getPaint());
}
break;
+ case DrawPoints: {
+ int count = 0;
+ float* points = getFloats(count);
+ DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ renderer.drawPoints(points, count, getPaint());
+ }
+ break;
case DrawText: {
getText(&text);
int count = getInt();
@@ -533,12 +878,6 @@ void DisplayListRenderer::reset() {
}
mBitmapResources.clear();
- for (size_t i = 0; i < mOriginalPaths.size(); i++) {
- SkPath* resource = mOriginalPaths.itemAt(i);
- caches.resourceCache.decrementRefcount(resource);
- }
- mOriginalPaths.clear();
-
for (size_t i = 0; i < mShaders.size(); i++) {
caches.resourceCache.decrementRefcount(mShaders.itemAt(i));
}
@@ -804,8 +1143,15 @@ void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
addPaint(paint);
}
+void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) {
+ addOp(DisplayList::DrawPoints);
+ addFloats(points, count);
+ addPaint(paint);
+}
+
void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, SkPaint* paint) {
+ if (count <= 0) return;
addOp(DisplayList::DrawText);
addText(text, bytesCount);
addInt(count);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 6fc315c..dcf2cf2 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -26,6 +26,7 @@
#include <SkTDArray.h>
#include <SkTSearch.h>
+#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
#include "utils/Functor.h"
@@ -89,6 +90,7 @@ public:
DrawArc,
DrawPath,
DrawLines,
+ DrawPoints,
DrawText,
ResetShader,
SetupShader,
@@ -105,6 +107,10 @@ public:
bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
+ void output(OpenGLRenderer& renderer, uint32_t level = 0);
+
+ static void outputLogBuffer(int fd);
+
private:
void init();
@@ -193,7 +199,6 @@ private:
Vector<SkPaint*> mPaints;
Vector<SkPath*> mPaths;
- Vector<SkPath*> mOriginalPaths;
Vector<SkMatrix*> mMatrices;
Vector<SkiaShader*> mShaders;
@@ -265,6 +270,7 @@ public:
float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
void drawPath(SkPath* path, SkPaint* paint);
void drawLines(float* points, int count, SkPaint* paint);
+ void drawPoints(float* points, int count, SkPaint* paint);
void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
void resetShader();
@@ -298,10 +304,6 @@ public:
return mPaths;
}
- const Vector<SkPath*>& getOriginalPaths() const {
- return mOriginalPaths;
- }
-
const Vector<SkMatrix*>& getMatrices() const {
return mMatrices;
}
@@ -383,16 +385,9 @@ private:
SkPath* pathCopy = mPathMap.valueFor(path);
if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
- if (pathCopy == NULL) {
- pathCopy = path;
- mOriginalPaths.add(path);
- Caches& caches = Caches::getInstance();
- caches.resourceCache.incrementRefcount(path);
- } else {
- pathCopy = new SkPath(*path);
- mPaths.add(pathCopy);
- }
+ pathCopy = new SkPath(*path);
mPathMap.add(path, pathCopy);
+ mPaths.add(pathCopy);
}
addInt((int) pathCopy);
@@ -469,7 +464,6 @@ private:
Vector<SkPaint*> mPaints;
DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap;
- Vector<SkPath*> mOriginalPaths;
Vector<SkPath*> mPaths;
DefaultKeyedVector<SkPath*, SkPath*> mPathMap;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index aa9b40e..9bf3de8 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -35,6 +35,10 @@ namespace uirenderer {
#define DEFAULT_TEXT_CACHE_WIDTH 1024
#define DEFAULT_TEXT_CACHE_HEIGHT 256
+// We should query these values from the GL context
+#define MAX_TEXT_CACHE_WIDTH 2048
+#define MAX_TEXT_CACHE_HEIGHT 2048
+
///////////////////////////////////////////////////////////////////////////////
// Font
///////////////////////////////////////////////////////////////////////////////
@@ -55,8 +59,7 @@ Font::~Font() {
}
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
- delete glyph;
+ delete mCachedGlyphs.valueAt(i);
}
}
@@ -131,48 +134,49 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
}
-Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
+Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
if (index >= 0) {
cachedGlyph = mCachedGlyphs.valueAt(index);
} else {
- cachedGlyph = cacheGlyph(paint, utfChar);
+ cachedGlyph = cacheGlyph(paint, textUnit);
}
// Is the glyph still in texture cache?
if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
updateGlyphCache(paint, skiaGlyph, cachedGlyph);
}
return cachedGlyph;
}
-void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
bitmapW, bitmapH, NULL);
} else {
- renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL);
}
}
-void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds) {
if (bounds == NULL) {
LOGE("No return rectangle provided to measure text");
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
}
#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
if (numGlyphs == 0 || text == NULL || len == 0) {
@@ -192,14 +196,14 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t
text += start;
while (glyphsLeft > 0) {
- int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+ glyph_t glyph = GET_GLYPH(text);
// Reached the end of the string
- if (utfChar < 0) {
+ if (IS_END_OF_STRING(glyph)) {
break;
}
- CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
prevRsbDelta = cachedGlyph->mRsbDelta;
@@ -265,11 +269,11 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
mState->mUploadTexture = true;
}
-Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
- const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
newGlyph->mGlyphIndex = skiaGlyph.fID;
newGlyph->mIsValid = false;
@@ -386,9 +390,17 @@ void FontRenderer::flushAllAndInvalidate() {
bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
// If the glyph is too tall, don't cache it
if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
- LOGE("Font size to large to fit in cache. width, height = %i, %i",
- (int) glyph.fWidth, (int) glyph.fHeight);
- return false;
+ if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
+ // Default cache not large enough for large glyphs - resize cache to
+ // max size and try again
+ flushAllAndInvalidate();
+ initTextTexture(true);
+ }
+ if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ LOGE("Font size to large to fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return false;
+ }
}
// Now copy the bitmap into the cache texture
@@ -446,16 +458,25 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
return true;
}
-void FontRenderer::initTextTexture() {
+void FontRenderer::initTextTexture(bool largeFonts) {
+ mCacheLines.clear();
+ if (largeFonts) {
+ mCacheWidth = MAX_TEXT_CACHE_WIDTH;
+ mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
+ }
+
mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
mUploadTexture = false;
+ if (mTextureId != 0) {
+ glDeleteTextures(1, &mTextureId);
+ }
glGenTextures(1, &mTextureId);
glBindTexture(GL_TEXTURE_2D, mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimentions
+ // Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
GL_ALPHA, GL_UNSIGNED_BYTE, 0);
@@ -480,6 +501,15 @@ void FontRenderer::initTextTexture() {
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
nextLine += mCacheLines.top()->mMaxHeight;
+ if (largeFonts) {
+ int nextSize = 76;
+ // Make several new lines with increasing font sizes
+ while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ nextSize += 50;
+ }
+ }
mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
}
@@ -643,7 +673,7 @@ void FontRenderer::precacheLatin(SkPaint* paint) {
uint32_t remainingCapacity = getRemainingCacheCapacity();
uint32_t precacheIdx = 0;
while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
- mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
+ mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
remainingCapacity = getRemainingCacheCapacity();
precacheIdx ++;
}
@@ -685,7 +715,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
}
Rect bounds;
- mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
+ mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
@@ -696,7 +726,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
int penX = radius - bounds.left;
int penY = radius - bounds.bottom;
- mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
+ mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
dataBuffer, paddedWidth, paddedHeight);
blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
@@ -726,7 +756,7 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text
mDrawn = false;
mBounds = bounds;
mClip = clip;
- mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
+ mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
mBounds = NULL;
if (mCurrentQuadIndex != 0) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index f685d5f..24ed6fa 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -33,8 +33,32 @@
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+#else
+ typedef SkUnichar glyph_t;
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Declarations
+///////////////////////////////////////////////////////////////////////////////
+
class FontRenderer;
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
/**
* Represents a font, defined by a Skia font id and a font size. A font is used
* to generate glyphs and cache them in the FontState.
@@ -51,9 +75,9 @@ public:
* Renders the specified string of text.
* If bitmap is specified, it will be used as the render target
*/
- void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y,
- uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
/**
* Creates a new font associated with the specified font state.
*/
@@ -69,13 +93,12 @@ protected:
MEASURE,
};
- void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect *bounds);
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds);
- void measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds);
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds);
struct CachedGlyphInfo {
// Has the cache been invalidated?
@@ -107,18 +130,26 @@ protected:
Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
uint32_t scaleX);
- DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
void invalidateTextureCache();
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_t glyph);
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds);
void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
- CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar);
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
FontRenderer* mState;
uint32_t mFontId;
@@ -128,6 +159,10 @@ protected:
uint32_t mScaleX;
};
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
class FontRenderer {
public:
FontRenderer();
@@ -229,7 +264,7 @@ protected:
}
};
- void initTextTexture();
+ void initTextTexture(bool largeFonts = false);
bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
void flushAllAndInvalidate();
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index bb28437..0310bc3 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -45,6 +45,9 @@ struct Layer {
mesh = NULL;
meshIndices = NULL;
meshElementCount = 0;
+ isCacheable = true;
+ isTextureLayer = false;
+ renderTarget = GL_TEXTURE_2D;
}
~Layer() {
@@ -53,6 +56,23 @@ struct Layer {
}
/**
+ * Sets this layer's region to a rectangle. Computes the appropriate
+ * texture coordinates.
+ */
+ void setRegionAsRect() {
+ const android::Rect& bounds = region.getBounds();
+ regionRect.set(bounds.leftTop().x, bounds.leftTop().y,
+ bounds.rightBottom().x, bounds.rightBottom().y);
+
+ const float texX = 1.0f / float(width);
+ const float texY = 1.0f / float(height);
+ const float height = layer.getHeight();
+ texCoords.set(
+ regionRect.left * texX, (height - regionRect.top) * texY,
+ regionRect.right * texX, (height - regionRect.bottom) * texY);
+ }
+
+ /**
* Bounds of the layer.
*/
Rect layer;
@@ -103,6 +123,11 @@ struct Layer {
* have been drawn.
*/
Region region;
+ /**
+ * If the region is a rectangle, coordinates of the
+ * region are stored here.
+ */
+ Rect regionRect;
/**
* Color filter used to draw this layer. Optional.
@@ -115,6 +140,27 @@ struct Layer {
TextureVertex* mesh;
uint16_t* meshIndices;
GLsizei meshElementCount;
+
+ /**
+ * If set to true (by default), the layer can be reused.
+ */
+ bool isCacheable;
+
+ /**
+ * When set to true, this layer must be treated as a texture
+ * layer.
+ */
+ bool isTextureLayer;
+
+ /**
+ * Optional texture coordinates transform.
+ */
+ mat4 texTransform;
+
+ /**
+ * Indicates the render target.
+ */
+ GLenum renderTarget;
}; // struct Layer
}; // namespace uirenderer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index a9710ad..b2d795f 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -154,6 +154,8 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh
}
bool LayerCache::put(Layer* layer) {
+ if (!layer->isCacheable) return false;
+
const uint32_t size = layer->width * layer->height * 4;
// Don't even try to cache a layer that's bigger than the cache
if (size < mMaxSize) {
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index f92e20b..f316ba7 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -20,6 +20,7 @@
#include "LayerCache.h"
#include "LayerRenderer.h"
+#include "Matrix.h"
#include "Properties.h"
#include "Rect.h"
@@ -92,11 +93,7 @@ Region* LayerRenderer::getRegion() {
void LayerRenderer::generateMesh() {
#if RENDER_LAYERS_AS_REGIONS
-#if RENDER_LAYERS_RECT_AS_RECT
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
-#else
- if (mLayer->region.isEmpty()) {
-#endif
if (mLayer->mesh) {
delete mLayer->mesh;
delete mLayer->meshIndices;
@@ -105,6 +102,8 @@ void LayerRenderer::generateMesh() {
mLayer->meshIndices = NULL;
mLayer->meshElementCount = 0;
}
+
+ mLayer->setRegionAsRect();
return;
}
@@ -167,6 +166,30 @@ void LayerRenderer::generateMesh() {
// Layers management
///////////////////////////////////////////////////////////////////////////////
+Layer* LayerRenderer::createTextureLayer() {
+ LAYER_RENDERER_LOGD("Creating new texture layer");
+
+ Layer* layer = new Layer(0, 0);
+ layer->isCacheable = false;
+ layer->isTextureLayer = true;
+ layer->blend = true;
+ layer->empty = true;
+ layer->fbo = 0;
+ layer->colorFilter = NULL;
+ layer->fbo = 0;
+ layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
+ layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f);
+ layer->alpha = 255;
+ layer->mode = SkXfermode::kSrcOver_Mode;
+ layer->colorFilter = NULL;
+ layer->region.clear();
+
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(1, &layer->texture);
+
+ return layer;
+}
+
Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height);
@@ -246,6 +269,27 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
return true;
}
+void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
+ GLenum renderTarget, float* transform) {
+ if (layer) {
+ layer->width = width;
+ layer->height = height;
+ layer->layer.set(0.0f, 0.0f, width, height);
+ layer->region.set(width, height);
+ layer->regionRect.set(0.0f, 0.0f, width, height);
+ layer->texTransform.load(transform);
+ layer->renderTarget = renderTarget;
+
+ glBindTexture(layer->renderTarget, layer->texture);
+
+ glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+}
+
void LayerRenderer::destroyLayer(Layer* layer) {
if (layer) {
LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo);
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index d2f565e..59cab96 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -53,8 +53,11 @@ public:
Region* getRegion();
GLint getTargetFbo();
+ static Layer* createTextureLayer();
static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
+ static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
+ GLenum renderTarget, float* transform);
static void destroyLayer(Layer* layer);
static void destroyLayerDeferred(Layer* layer);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index e7c0fe3..9fc5131 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -67,6 +67,10 @@ bool Matrix4::isPureTranslate() {
ALMOST_EQUAL(data[kScaleX], 1.0f) && ALMOST_EQUAL(data[kScaleY], 1.0f);
}
+bool Matrix4::isSimple() {
+ return mSimpleMatrix;
+}
+
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 08f5d77..2fa6ab7 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -111,6 +111,7 @@ public:
}
bool isPureTranslate();
+ bool isSimple();
bool changesBounds();
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) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7362473..549d6e9 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -98,6 +98,7 @@ public:
virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
Rect& dirty, uint32_t level = 0);
+ virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
@@ -119,6 +120,7 @@ public:
float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
virtual void drawPath(SkPath* path, SkPaint* paint);
virtual void drawLines(float* points, int count, SkPaint* paint);
+ virtual void drawPoints(float* points, int count, SkPaint* paint);
virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
SkPaint* paint);
@@ -247,6 +249,8 @@ private:
*/
void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false);
+ void drawTextureLayer(Layer* layer, const Rect& rect);
+
/**
* Mark the layer as dirty at the specified coordinates. The coordinates
* are transformed with the supplied matrix.
@@ -279,6 +283,11 @@ private:
void drawShape(float left, float top, const PathTexture* texture, SkPaint* paint);
void drawRectAsShape(float left, float top, float right, float bottom, SkPaint* p);
+ void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
+
+ void drawAARect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode);
+
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
* are transformed by the current snapshot's transform matrix.
@@ -384,6 +393,14 @@ private:
}
/**
+ * Binds the specified EGLImage texture. The texture unit must have been selected
+ * prior to calling this method.
+ */
+ inline void bindExternalTexture(GLuint texture) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+ }
+
+ /**
* Sets the wrap modes for the specified texture. The wrap modes are modified
* only when needed.
*/
@@ -422,6 +439,9 @@ private:
* Various methods to setup OpenGL rendering.
*/
void setupDrawWithTexture(bool isAlpha8 = false);
+ void setupDrawWithExternalTexture();
+ void setupDrawAALine();
+ void setupDrawPoint(float pointSize);
void setupDrawColor(int color);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
@@ -435,11 +455,12 @@ private:
bool swapSrcDst = false);
void setupDrawProgram();
void setupDrawDirtyRegionsDisabled();
- void setupDrawModelViewIdentity();
+ void setupDrawModelViewIdentity(bool offset = false);
void setupDrawModelView(float left, float top, float right, float bottom,
bool ignoreTransform = false, bool ignoreModelView = false);
void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
bool ignoreTransform = false);
+ void setupDrawPointUniforms();
void setupDrawColorUniforms();
void setupDrawPureColorUniforms();
void setupDrawShaderIdentityUniforms();
@@ -447,8 +468,15 @@ private:
void setupDrawColorFilterUniforms();
void setupDrawSimpleMesh();
void setupDrawTexture(GLuint texture);
+ void setupDrawExternalTexture(GLuint texture);
+ void setupDrawTextureTransform();
+ void setupDrawTextureTransformUniforms(mat4& transform);
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
+ void setupDrawVertices(GLvoid* vertices);
+ void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
+ float strokeWidth);
void finishDrawTexture();
+ void accountForClear(SkXfermode::Mode mode);
void drawRegionRects(const Region& region);
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 11eb953..f7dacae 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -152,12 +152,12 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float previousStepY = 0.0f;
float y1 = 0.0f;
+ float y2 = 0.0f;
float v1 = 0.0f;
for (uint32_t i = 0; i < mYCount; i++) {
float stepY = mYDivs[i];
- float y2 = 0.0f;
if (i & 1) {
const float segment = stepY - previousStepY;
y2 = y1 + floorf(segment * stretchY + 0.5f);
@@ -167,8 +167,15 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight;
if (stepY > 0.0f) {
+#if DEBUG_EXPLODE_PATCHES
+ y1 += i * EXPLODE_GAP;
+ y2 += i * EXPLODE_GAP;
+#endif
generateRow(vertex, y1, y2, v1, v2, stretchX, right - left,
bitmapWidth, quadCount);
+#if DEBUG_EXPLODE_PATCHES
+ y2 -= i * EXPLODE_GAP;
+#endif
}
y1 = y2;
@@ -178,7 +185,12 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
}
if (previousStepY != bitmapHeight) {
- generateRow(vertex, y1, bottom - top, v1, 1.0f, stretchX, right - left,
+ y2 = bottom - top;
+#if DEBUG_EXPLODE_PATCHES
+ y1 += mYCount * EXPLODE_GAP;
+ y2 += mYCount * EXPLODE_GAP;
+#endif
+ generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left,
bitmapWidth, quadCount);
}
@@ -202,13 +214,13 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
float previousStepX = 0.0f;
float x1 = 0.0f;
+ float x2 = 0.0f;
float u1 = 0.0f;
// Generate the row quad by quad
for (uint32_t i = 0; i < mXCount; i++) {
float stepX = mXDivs[i];
- float x2 = 0.0f;
if (i & 1) {
const float segment = stepX - previousStepX;
x2 = x1 + floorf(segment * stretchX + 0.5f);
@@ -218,7 +230,14 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth;
if (stepX > 0.0f) {
+#if DEBUG_EXPLODE_PATCHES
+ x1 += i * EXPLODE_GAP;
+ x2 += i * EXPLODE_GAP;
+#endif
generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
+#if DEBUG_EXPLODE_PATCHES
+ x2 -= i * EXPLODE_GAP;
+#endif
}
x1 = x2;
@@ -228,7 +247,12 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
}
if (previousStepX != bitmapWidth) {
- generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount);
+ x2 = width;
+#if DEBUG_EXPLODE_PATCHES
+ x1 += mXCount * EXPLODE_GAP;
+ x2 += mXCount * EXPLODE_GAP;
+#endif
+ generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount);
}
}
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 0f0ffa2..28c9048 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -31,6 +31,12 @@ namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define EXPLODE_GAP 4
+
+///////////////////////////////////////////////////////////////////////////////
// 9-patch structures
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index dc67e16..7ff8b74 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -41,8 +41,7 @@ struct PathCacheEntry: public ShapeCacheEntry {
path = NULL;
}
- PathCacheEntry(const PathCacheEntry& entry):
- ShapeCacheEntry(entry) {
+ PathCacheEntry(const PathCacheEntry& entry): ShapeCacheEntry(entry) {
path = entry.path;
}
@@ -55,6 +54,7 @@ struct PathCacheEntry: public ShapeCacheEntry {
}
SkPath* path;
+
}; // PathCacheEntry
/**
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 2187f24..972dd87 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -124,8 +124,15 @@ GLuint Program::buildShader(const char* source, GLenum type) {
}
void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
- const mat4& transformMatrix) {
+ const mat4& transformMatrix, bool offset) {
mat4 t(projectionMatrix);
+ if (offset) {
+ // offset screenspace xy by an amount that compensates for typical precision issues
+ // in GPU hardware that tends to paint hor/vert lines in pixels shifted up and to the left.
+ // This offset value is based on an assumption that some hardware may use as little
+ // as 12.4 precision, so we offset by slightly more than 1/16.
+ t.translate(.375, .375, 0);
+ }
t.multiply(transformMatrix);
t.multiply(modelViewMatrix);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index afc6f3d..764cb05 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,7 +81,7 @@ public:
* transform matrices.
*/
void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
- const mat4& transformMatrix);
+ const mat4& transformMatrix, bool offset = false);
/**
* Sets the color associated with this shader.
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 0b6c7b5..d419e3e 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -39,8 +39,15 @@ const char* gVS_Header_Attributes =
"attribute vec4 position;\n";
const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
+const char* gVS_Header_Attributes_AAParameters =
+ "attribute float vtxWidth;\n"
+ "attribute float vtxLength;\n";
+const char* gVS_Header_Uniforms_TextureTransform =
+ "uniform mat4 mainTextureTransform;\n";
const char* gVS_Header_Uniforms =
"uniform mat4 transform;\n";
+const char* gVS_Header_Uniforms_IsPoint =
+ "uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient[3] = {
// Linear
"uniform mat4 screenSpace;\n",
@@ -51,11 +58,16 @@ const char* gVS_Header_Uniforms_HasGradient[3] = {
};
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
- "uniform vec2 textureDimension;\n";
+ "uniform mediump vec2 textureDimension;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
+const char* gVS_Header_Varyings_IsAA =
+ "varying float widthProportion;\n"
+ "varying float lengthProportion;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_PointHasBitmap =
+ "varying vec2 outPointBitmapTexCoords;\n";
const char* gVS_Header_Varyings_HasGradient[3] = {
// Linear
"varying vec2 linear;\n",
@@ -68,6 +80,8 @@ const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
" outTexCoords = texCoords;\n";
+const char* gVS_Main_OutTransformedTexCoords =
+ " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
const char* gVS_Main_OutGradient[3] = {
// Linear
" linear = vec2((screenSpace * position).x, 0.5);\n",
@@ -78,8 +92,15 @@ const char* gVS_Main_OutGradient[3] = {
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
+const char* gVS_Main_OutPointBitmapTexCoords =
+ " outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
" gl_Position = transform * position;\n";
+const char* gVS_Main_PointSize =
+ " gl_PointSize = pointSize;\n";
+const char* gVS_Main_AA =
+ " widthProportion = vtxWidth;\n"
+ " lengthProportion = vtxLength;\n";
const char* gVS_Footer =
"}\n\n";
@@ -89,12 +110,24 @@ const char* gVS_Footer =
const char* gFS_Header_Extension_FramebufferFetch =
"#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
+const char* gFS_Header_Extension_ExternalTexture =
+ "#extension GL_OES_EGL_image_external : require\n\n";
const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
+const char* gFS_Uniforms_AA =
+ "uniform float boundaryWidth;\n"
+ "uniform float inverseBoundaryWidth;\n"
+ "uniform float boundaryLength;\n"
+ "uniform float inverseBoundaryLength;\n";
+const char* gFS_Header_Uniforms_PointHasBitmap =
+ "uniform vec2 textureDimension;\n"
+ "uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D sampler;\n";
+const char* gFS_Uniforms_ExternalTextureSampler =
+ "uniform samplerExternalOES sampler;\n";
const char* gFS_Uniforms_GradientSampler[3] = {
// Linear
"uniform sampler2D gradientSampler;\n",
@@ -121,6 +154,10 @@ const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
+const char* gFS_Main_PointBitmapTexCoords =
+ " vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
+ "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
+
// Fast cases
const char* gFS_Fast_SingleColor =
"\nvoid main(void) {\n"
@@ -154,6 +191,19 @@ const char* gFS_Fast_SingleModulateGradient =
// General case
const char* gFS_Main_FetchColor =
" fragColor = color;\n";
+const char* gFS_Main_ModulateColor =
+ " fragColor *= color.a;\n";
+const char* gFS_Main_AccountForAA =
+ " if (widthProportion < boundaryWidth) {\n"
+ " fragColor *= (widthProportion * inverseBoundaryWidth);\n"
+ " } else if (widthProportion > (1.0 - boundaryWidth)) {\n"
+ " fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n"
+ " }\n"
+ " if (lengthProportion < boundaryLength) {\n"
+ " fragColor *= (lengthProportion * inverseBoundaryLength);\n"
+ " } else if (lengthProportion > (1.0 - boundaryLength)) {\n"
+ " fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n"
+ " }\n";
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
" fragColor = texture2D(sampler, outTexCoords);\n",
@@ -336,38 +386,62 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
// Add attributes
String8 shader(gVS_Header_Attributes);
- if (description.hasTexture) {
+ if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
+ if (description.isAA) {
+ shader.append(gVS_Header_Attributes_AAParameters);
+ }
// Uniforms
shader.append(gVS_Header_Uniforms);
+ if (description.hasTextureTransform) {
+ shader.append(gVS_Header_Uniforms_TextureTransform);
+ }
if (description.hasGradient) {
shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
+ if (description.isPoint) {
+ shader.append(gVS_Header_Uniforms_IsPoint);
+ }
// Varyings
- if (description.hasTexture) {
+ if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
+ if (description.isAA) {
+ shader.append(gVS_Header_Varyings_IsAA);
+ }
if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
- shader.append(gVS_Header_Varyings_HasBitmap);
+ shader.append(description.isPoint ?
+ gVS_Header_Varyings_PointHasBitmap :
+ gVS_Header_Varyings_HasBitmap);
}
// Begin the shader
shader.append(gVS_Main); {
- if (description.hasTexture) {
+ if (description.hasTextureTransform) {
+ shader.append(gVS_Main_OutTransformedTexCoords);
+ } else if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Main_OutTexCoords);
}
+ if (description.isAA) {
+ shader.append(gVS_Main_AA);
+ }
if (description.hasGradient) {
shader.append(gVS_Main_OutGradient[description.gradientType]);
}
if (description.hasBitmap) {
- shader.append(gVS_Main_OutBitmapTexCoords);
+ shader.append(description.isPoint ?
+ gVS_Main_OutPointBitmapTexCoords :
+ gVS_Main_OutBitmapTexCoords);
+ }
+ if (description.isPoint) {
+ shader.append(gVS_Main_PointSize);
}
// Output transformed position
shader.append(gVS_Main_Position);
@@ -388,23 +462,31 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (blendFramebuffer) {
shader.append(gFS_Header_Extension_FramebufferFetch);
}
+ if (description.hasExternalTexture) {
+ shader.append(gFS_Header_Extension_ExternalTexture);
+ }
shader.append(gFS_Header);
// Varyings
- if (description.hasTexture) {
+ if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
+ if (description.isAA) {
+ shader.append(gVS_Header_Varyings_IsAA);
+ }
if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
- shader.append(gVS_Header_Varyings_HasBitmap);
+ shader.append(description.isPoint ?
+ gVS_Header_Varyings_PointHasBitmap :
+ gVS_Header_Varyings_HasBitmap);
}
// Uniforms
int modulateOp = MODULATE_OP_NO_MODULATE;
- const bool singleColor = !description.hasTexture &&
+ const bool singleColor = !description.hasTexture && !description.hasExternalTexture &&
!description.hasGradient && !description.hasBitmap;
if (description.modulate || singleColor) {
@@ -413,21 +495,30 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
if (description.hasTexture) {
shader.append(gFS_Uniforms_TextureSampler);
+ } else if (description.hasExternalTexture) {
+ shader.append(gFS_Uniforms_ExternalTextureSampler);
+ }
+ if (description.isAA) {
+ shader.append(gFS_Uniforms_AA);
}
if (description.hasGradient) {
shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
}
+ if (description.hasBitmap && description.isPoint) {
+ shader.append(gFS_Header_Uniforms_PointHasBitmap);
+ }
// Optimization for common cases
- if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) {
+ if (!description.isAA && !blendFramebuffer &&
+ description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
- const bool singleTexture = description.hasTexture &&
+ const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
!description.hasAlpha8Texture && noShader;
const bool singleA8Texture = description.hasTexture &&
description.hasAlpha8Texture && noShader;
- const bool singleGradient = !description.hasTexture &&
+ const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
description.hasGradient && !description.hasBitmap &&
description.gradientType == ProgramDescription::kGradientLinear;
@@ -490,7 +581,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Begin the shader
shader.append(gFS_Main); {
// Stores the result in fragColor directly
- if (description.hasTexture) {
+ if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
shader.append(gFS_Main_FetchA8Texture[modulateOp]);
@@ -503,16 +594,23 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Main_FetchColor);
}
}
+ if (description.isAA) {
+ shader.append(gFS_Main_AccountForAA);
+ }
if (description.hasGradient) {
shader.append(gFS_Main_FetchGradient[description.gradientType]);
}
if (description.hasBitmap) {
+ if (description.isPoint) {
+ shader.append(gFS_Main_PointBitmapTexCoords);
+ }
if (!description.isBitmapNpot) {
shader.append(gFS_Main_FetchBitmap);
} else {
shader.append(gFS_Main_FetchBitmapNpot);
}
}
+ bool applyModulate = false;
// Case when we have two shaders set
if (description.hasGradient && description.hasBitmap) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
@@ -522,15 +620,21 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Main_BlendShadersGB);
}
shader.append(gFS_Main_BlendShaders_Modulate[op]);
+ applyModulate = true;
} else {
if (description.hasGradient) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
shader.append(gFS_Main_GradientShader_Modulate[op]);
+ applyModulate = true;
} else if (description.hasBitmap) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
shader.append(gFS_Main_BitmapShader_Modulate[op]);
+ applyModulate = true;
}
}
+ if (description.modulate && applyModulate) {
+ shader.append(gFS_Main_ModulateColor);
+ }
// Apply the color op if needed
shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
// Output the fragment
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index ead5b92..5c7197b 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -71,7 +71,14 @@ namespace uirenderer {
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
#define PROGRAM_GRADIENT_TYPE_SHIFT 33
-#define PROGRAM_MODULATE 35
+#define PROGRAM_MODULATE_SHIFT 35
+
+#define PROGRAM_IS_POINT_SHIFT 36
+
+#define PROGRAM_HAS_AA_SHIFT 37
+
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -109,6 +116,8 @@ struct ProgramDescription {
// Texturing
bool hasTexture;
bool hasAlpha8Texture;
+ bool hasExternalTexture;
+ bool hasTextureTransform;
// Modulate, this should only be set when setColor() return true
bool modulate;
@@ -117,6 +126,8 @@ struct ProgramDescription {
bool hasBitmap;
bool isBitmapNpot;
+ bool isAA;
+
bool hasGradient;
Gradient gradientType;
@@ -135,6 +146,9 @@ struct ProgramDescription {
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
+ bool isPoint;
+ float pointSize;
+
/**
* Resets this description. All fields are reset back to the default
* values they hold after building a new instance.
@@ -142,6 +156,10 @@ struct ProgramDescription {
void reset() {
hasTexture = false;
hasAlpha8Texture = false;
+ hasExternalTexture = false;
+ hasTextureTransform = false;
+
+ isAA = false;
modulate = false;
@@ -162,6 +180,9 @@ struct ProgramDescription {
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
+
+ isPoint = false;
+ pointSize = 0.0f;
}
/**
@@ -223,7 +244,11 @@ struct ProgramDescription {
}
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
- if (modulate) key |= programid(0x1) << PROGRAM_MODULATE;
+ if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
+ if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
+ if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
+ if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
+ if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
return key;
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1aef99b..7c10518 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -27,8 +27,9 @@
// If turned on, layers drawn inside FBOs are optimized with regions
#define RENDER_LAYERS_AS_REGIONS 1
-// If turned on, layers that map to a single rect are drawn as a rect
-#define RENDER_LAYERS_RECT_AS_RECT 0
+
+// If turned on, text is interpreted as glyphs instead of UTF-16
+#define RENDER_TEXT_AS_GLYPHS 1
/**
* Debug level for app developers.
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 4c626dd..b5cc29c 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -89,14 +89,17 @@ struct ShapeCacheEntry {
join = SkPaint::kDefault_Join;
cap = SkPaint::kDefault_Cap;
style = SkPaint::kFill_Style;
- miter = 4.0f;
- strokeWidth = 1.0f;
+ float v = 4.0f;
+ miter = *(uint32_t*) &v;
+ v = 1.0f;
+ strokeWidth = *(uint32_t*) &v;
+ pathEffect = NULL;
}
ShapeCacheEntry(const ShapeCacheEntry& entry):
shapeType(entry.shapeType), join(entry.join), cap(entry.cap),
style(entry.style), miter(entry.miter),
- strokeWidth(entry.strokeWidth) {
+ strokeWidth(entry.strokeWidth), pathEffect(entry.pathEffect) {
}
ShapeCacheEntry(ShapeType type, SkPaint* paint) {
@@ -108,18 +111,19 @@ struct ShapeCacheEntry {
v = paint->getStrokeWidth();
strokeWidth = *(uint32_t*) &v;
style = paint->getStyle();
+ pathEffect = paint->getPathEffect();
}
virtual ~ShapeCacheEntry() {
}
- // shapeType must be checked in subclasses operator<
ShapeType shapeType;
SkPaint::Join join;
SkPaint::Cap cap;
SkPaint::Style style;
uint32_t miter;
uint32_t strokeWidth;
+ SkPathEffect* pathEffect;
bool operator<(const ShapeCacheEntry& rhs) const {
LTE_INT(shapeType) {
@@ -128,7 +132,9 @@ struct ShapeCacheEntry {
LTE_INT(style) {
LTE_INT(miter) {
LTE_INT(strokeWidth) {
- return lessThan(rhs);
+ LTE_INT(pathEffect) {
+ return lessThan(rhs);
+ }
}
}
}
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index d46686d..28dba13 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -73,7 +73,6 @@ struct ShadowText {
text = str.string();
}
- // TODO: Should take into account fake bold and text skew
bool operator<(const ShadowText& rhs) const {
LTE_INT(len) {
LTE_INT(radius) {
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index bbf4d4a..38455dc 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -23,6 +23,18 @@ namespace uirenderer {
/**
* Simple structure to describe a vertex with a position and a texture.
*/
+struct Vertex {
+ float position[2];
+
+ static inline void set(Vertex* vertex, float x, float y) {
+ vertex[0].position[0] = x;
+ vertex[0].position[1] = y;
+ }
+}; // struct Vertex
+
+/**
+ * Simple structure to describe a vertex with a position and a texture.
+ */
struct TextureVertex {
float position[2];
float texture[2];
@@ -40,6 +52,41 @@ struct TextureVertex {
}
}; // struct TextureVertex
+/**
+ * Simple structure to describe a vertex with a position and an alpha value.
+ */
+struct AlphaVertex : Vertex {
+ float alpha;
+
+ static inline void set(AlphaVertex* vertex, float x, float y, float alpha) {
+ Vertex::set(vertex, x, y);
+ vertex[0].alpha = alpha;
+ }
+
+ static inline void setColor(AlphaVertex* vertex, float alpha) {
+ vertex[0].alpha = alpha;
+ }
+}; // struct AlphaVertex
+
+/**
+ * Simple structure to describe a vertex with a position and an alpha value.
+ */
+struct AAVertex : Vertex {
+ float width;
+ float length;
+
+ static inline void set(AAVertex* vertex, float x, float y, float width, float length) {
+ Vertex::set(vertex, x, y);
+ vertex[0].width = width;
+ vertex[0].length = length;
+ }
+
+ static inline void setColor(AAVertex* vertex, float width, float length) {
+ vertex[0].width = width;
+ vertex[0].length = length;
+ }
+}; // struct AlphaVertex
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h
deleted file mode 100644
index 42e6d9b..0000000
--- a/libs/hwui/utils/GenerationCache.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_GENERATION_CACHE_H
-#define ANDROID_HWUI_GENERATION_CACHE_H
-
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-
-namespace android {
-namespace uirenderer {
-
-template<typename EntryKey, typename EntryValue>
-class OnEntryRemoved {
-public:
- virtual ~OnEntryRemoved() { };
- virtual void operator()(EntryKey& key, EntryValue& value) = 0;
-}; // class OnEntryRemoved
-
-template<typename EntryKey, typename EntryValue>
-struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
- Entry() { }
- Entry(const Entry<EntryKey, EntryValue>& e):
- key(e.key), value(e.value), parent(e.parent), child(e.child) { }
- Entry(sp<Entry<EntryKey, EntryValue> > e):
- key(e->key), value(e->value), parent(e->parent), child(e->child) { }
-
- EntryKey key;
- EntryValue value;
-
- sp<Entry<EntryKey, EntryValue> > parent;
- sp<Entry<EntryKey, EntryValue> > child;
-}; // struct Entry
-
-template<typename K, typename V>
-class GenerationCache {
-public:
- GenerationCache(uint32_t maxCapacity);
- virtual ~GenerationCache();
-
- enum Capacity {
- kUnlimitedCapacity,
- };
-
- void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
-
- void clear();
-
- bool contains(K key) const;
- V get(K key);
- K getKeyAt(uint32_t index) const;
- bool put(K key, V value);
- V remove(K key);
- V removeOldest();
- V getValueAt(uint32_t index) const;
-
- uint32_t size() const;
-
- void addToCache(sp<Entry<K, V> > entry, K key, V value);
- void attachToCache(sp<Entry<K, V> > entry);
- void detachFromCache(sp<Entry<K, V> > entry);
-
- V removeAt(ssize_t index);
-
- KeyedVector<K, sp<Entry<K, V> > > mCache;
- uint32_t mMaxCapacity;
-
- OnEntryRemoved<K, V>* mListener;
-
- sp<Entry<K, V> > mOldest;
- sp<Entry<K, V> > mYoungest;
-}; // class GenerationCache
-
-template<typename K, typename V>
-GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) {
-};
-
-template<typename K, typename V>
-GenerationCache<K, V>::~GenerationCache() {
- clear();
-};
-
-template<typename K, typename V>
-uint32_t GenerationCache<K, V>::size() const {
- return mCache.size();
-}
-
-template<typename K, typename V>
-void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
- mListener = listener;
-}
-
-template<typename K, typename V>
-void GenerationCache<K, V>::clear() {
- if (mListener) {
- for (uint32_t i = 0; i < mCache.size(); i++) {
- sp<Entry<K, V> > entry = mCache.valueAt(i);
- if (mListener) {
- (*mListener)(entry->key, entry->value);
- }
- }
- }
- mCache.clear();
- mYoungest.clear();
- mOldest.clear();
-}
-
-template<typename K, typename V>
-bool GenerationCache<K, V>::contains(K key) const {
- return mCache.indexOfKey(key) >= 0;
-}
-
-template<typename K, typename V>
-K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
- return mCache.keyAt(index);
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::getValueAt(uint32_t index) const {
- return mCache.valueAt(index)->value;
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::get(K key) {
- ssize_t index = mCache.indexOfKey(key);
- if (index >= 0) {
- sp<Entry<K, V> > entry = mCache.valueAt(index);
- if (entry.get()) {
- detachFromCache(entry);
- attachToCache(entry);
- return entry->value;
- }
- }
-
- return NULL;
-}
-
-template<typename K, typename V>
-bool GenerationCache<K, V>::put(K key, V value) {
- if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
- removeOldest();
- }
-
- ssize_t index = mCache.indexOfKey(key);
- if (index < 0) {
- sp<Entry<K, V> > entry = new Entry<K, V>;
- addToCache(entry, key, value);
- return true;
- }
-
- return false;
-}
-
-template<typename K, typename V>
-void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) {
- entry->key = key;
- entry->value = value;
- mCache.add(key, entry);
- attachToCache(entry);
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::remove(K key) {
- ssize_t index = mCache.indexOfKey(key);
- if (index >= 0) {
- return removeAt(index);
- }
-
- return NULL;
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::removeAt(ssize_t index) {
- sp<Entry<K, V> > entry = mCache.valueAt(index);
- if (mListener) {
- (*mListener)(entry->key, entry->value);
- }
- mCache.removeItemsAt(index, 1);
- detachFromCache(entry);
-
- return entry->value;
-}
-
-template<typename K, typename V>
-V GenerationCache<K, V>::removeOldest() {
- if (mOldest.get()) {
- ssize_t index = mCache.indexOfKey(mOldest->key);
- if (index >= 0) {
- return removeAt(index);
- }
- }
-
- return NULL;
-}
-
-template<typename K, typename V>
-void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
- if (!mYoungest.get()) {
- mYoungest = mOldest = entry;
- } else {
- entry->parent = mYoungest;
- mYoungest->child = entry;
- mYoungest = entry;
- }
-}
-
-template<typename K, typename V>
-void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
- if (entry->parent.get()) {
- entry->parent->child = entry->child;
- }
-
- if (entry->child.get()) {
- entry->child->parent = entry->parent;
- }
-
- if (mOldest == entry) {
- mOldest = entry->child;
- }
-
- if (mYoungest == entry) {
- mYoungest = entry->parent;
- }
-
- entry->parent.clear();
- entry->child.clear();
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_GENERATION_CACHE_H