Handle shader matrix correctly when ignoring canvas transform

bug:20063841

Restores old SkShader matrix behavior from before the Glop refactor.

Many drawing operations draw without sending the canvas transform to
the GL shader. In such cases, we need to adapt the matrix sent to the
SkShader logic to invert the canvas transform that's built into
the mesh.

Change-Id: I42b6f59df36ce46436322b95bf9ad2140795ee58
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index c79ae77..9664f58 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -51,17 +51,20 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
-    int textureFillFlags = static_cast<int>(texture.getFormat() == GL_ALPHA
-            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
-    if (linearFiltering) {
-        textureFillFlags |= TextureFillFlags::kForceFilter;
+    int textureFillFlags = TextureFillFlags::None;
+    if (texture.getFormat() == GL_ALPHA) {
+        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
     }
-    const Matrix4& transform = pureTranslate ? Matrix4::identity() : *(renderer->currentTransform());
+    if (linearFiltering) {
+        textureFillFlags |= TextureFillFlags::ForceFilter;
+    }
+    int transformFlags = pureTranslate
+            ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     Glop glop;
     GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
-            .setTransform(renderer->currentSnapshot()->getOrthoMatrix(), transform, false)
+            .setTransform(*(renderer->currentSnapshot()), transformFlags)
             .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
             .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
             .build();
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 2c6f6c1..fa20b08 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -41,13 +41,32 @@
  * Position is always enabled by MeshState, these other attributes
  * are enabled/disabled dynamically based on mesh content.
  */
-enum class VertexAttribFlags {
-    kNone = 0,
-    kTextureCoord = 1 << 0,
-    kColor = 1 << 1,
-    kAlpha = 1 << 2,
+
+namespace VertexAttribFlags {
+    enum {
+        None = 0,
+        TextureCoord = 1 << 0,
+        Color = 1 << 1,
+        Alpha = 1 << 2,
+    };
 };
-MAKE_FLAGS_ENUM(VertexAttribFlags)
+
+/*
+ * Enumerates transform features
+ */
+namespace TransformFlags {
+    enum {
+        None = 0,
+
+        // offset the eventual drawing matrix by a tiny amount to
+        // disambiguate sampling patterns with non-AA rendering
+        OffsetByFudgeFactor = 1 << 0,
+
+        // Canvas transform isn't applied to the mesh at draw time,
+        //since it's already built in.
+        MeshIgnoresCanvasTransform = 1 << 1,
+    };
+};
 
 /**
  * Structure containing all data required to issue an OpenGL draw
@@ -116,10 +135,22 @@
     } fill;
 
     struct Transform {
-        Matrix4 ortho; // TODO: out of op, since this is static per FBO
+        // Orthographic projection matrix for current FBO
+        // TODO: move out of Glop, since this is static per FBO
+        Matrix4 ortho;
+
+        // modelView transform, accounting for delta between mesh transform and content of the mesh
+        // often represents x/y offsets within command, or scaling for mesh unit size
         Matrix4 modelView;
+
+        // Canvas transform of Glop - not necessarily applied to geometry (see flags)
         Matrix4 canvas;
-        bool fudgingOffset;
+        int transformFlags;
+
+       const Matrix4& meshTransform() const {
+           return (transformFlags & TransformFlags::MeshIgnoresCanvasTransform)
+                   ? Matrix4::identity() : canvas;
+       }
     } transform;
 
     const RoundRectClipState* roundRectClipState;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index e25f81e..b7cdaa2 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -66,7 +66,7 @@
     mOutGlop->mesh.indices = { 0, nullptr };
     mOutGlop->mesh.vertices = {
             mRenderState.meshState().getUnitQuadVBO(),
-            static_cast<int>(VertexAttribFlags::kNone),
+            VertexAttribFlags::None,
             nullptr, nullptr, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
@@ -85,7 +85,7 @@
     mOutGlop->mesh.indices = { 0, nullptr };
     mOutGlop->mesh.vertices = {
             mRenderState.meshState().getUnitQuadVBO(),
-            static_cast<int>(VertexAttribFlags::kTextureCoord),
+            VertexAttribFlags::TextureCoord,
             nullptr, (const void*) kMeshTextureOffset, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
@@ -105,7 +105,7 @@
     mOutGlop->mesh.indices = { 0, nullptr };
     mOutGlop->mesh.vertices = {
             0,
-            static_cast<int>(VertexAttribFlags::kTextureCoord),
+            VertexAttribFlags::TextureCoord,
             &textureVertex[0].x, &textureVertex[0].u, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = 4;
@@ -119,7 +119,7 @@
     mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
     mOutGlop->mesh.vertices = {
             0,
-            static_cast<int>(VertexAttribFlags::kNone),
+            VertexAttribFlags::None,
             vertexData, nullptr, nullptr,
             kVertexStride };
     mOutGlop->mesh.elementCount = 6 * quadCount;
@@ -133,7 +133,7 @@
     mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
     mOutGlop->mesh.vertices = {
             0,
-            static_cast<int>(VertexAttribFlags::kTextureCoord),
+            VertexAttribFlags::TextureCoord,
             &vertexData[0].x, &vertexData[0].u, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
@@ -147,7 +147,7 @@
     mOutGlop->mesh.indices = { 0, nullptr };
     mOutGlop->mesh.vertices = {
             0,
-            static_cast<int>(VertexAttribFlags::kTextureCoord),
+            VertexAttribFlags::TextureCoord,
             &vertexData[0].x, &vertexData[0].u, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
@@ -161,7 +161,7 @@
     mOutGlop->mesh.indices = { 0, nullptr };
     mOutGlop->mesh.vertices = {
             0,
-            VertexAttribFlags::kTextureCoord | VertexAttribFlags::kColor,
+            VertexAttribFlags::TextureCoord | VertexAttribFlags::Color,
             &vertexData[0].x, &vertexData[0].u, &vertexData[0].r,
             kColorTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
@@ -180,7 +180,7 @@
     mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() };
     mOutGlop->mesh.vertices = {
             0,
-            static_cast<int>(alphaVertex ? VertexAttribFlags::kAlpha : VertexAttribFlags::kNone),
+            alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None,
             vertexBuffer.getBuffer(), nullptr, nullptr,
             alphaVertex ? kAlphaVertexStride : kVertexStride };
     mOutGlop->mesh.elementCount = indices
@@ -197,7 +197,7 @@
     mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
     mOutGlop->mesh.vertices = {
             mCaches.patchCache.getMeshBuffer(),
-            static_cast<int>(VertexAttribFlags::kTextureCoord),
+            VertexAttribFlags::TextureCoord,
             (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = patch.indexCount;
@@ -230,7 +230,7 @@
 
     mOutGlop->blend = { GL_ZERO, GL_ZERO };
     if (mOutGlop->fill.color.a < 1.0f
-            || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+            || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
             || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend)
             || mOutGlop->roundRectClipState
             || PaintUtils::isBlendedShader(shader)
@@ -298,12 +298,12 @@
     }
 }
 
-GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, int textureFillFlags,
-        const SkPaint* paint, float alphaScale) {
+GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
+        const int textureFillFlags, const SkPaint* paint, float alphaScale) {
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    GLenum filter = (textureFillFlags & TextureFillFlags::kForceFilter)
+    GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter)
             ? GL_LINEAR : PaintUtils::getFilter(paint);
     mOutGlop->fill.texture = { &texture,
             GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr };
@@ -312,7 +312,7 @@
         int color = paint->getColor();
         SkShader* shader = paint->getShader();
 
-        if (!(textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture)) {
+        if (!(textureFillFlags & TextureFillFlags::IsAlphaMaskTexture)) {
             // Texture defines color, so disable shaders, and reset all non-alpha color channels
             color |= 0x00FFFFFF;
             shader = nullptr;
@@ -324,7 +324,7 @@
         mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
 
         if (alphaScale < 1.0f
-                || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+                || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
                 || texture.blend
                 || mOutGlop->roundRectClipState) {
             Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
@@ -334,7 +334,7 @@
         }
     }
 
-    if (textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture) {
+    if (textureFillFlags & TextureFillFlags::IsAlphaMaskTexture) {
         mDescription.modulate = mOutGlop->fill.color.isNotBlack();
         mDescription.hasAlpha8Texture = true;
     } else {
@@ -452,14 +452,13 @@
 // Transform
 ////////////////////////////////////////////////////////////////////////////////
 
-GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
-        const Matrix4& transform, bool fudgingOffset) {
+void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
+        const int transformFlags) {
     TRIGGER_STAGE(kTransformStage);
 
     mOutGlop->transform.ortho.load(ortho);
-    mOutGlop->transform.canvas.load(transform);
-    mOutGlop->transform.fudgingOffset = fudgingOffset;
-    return *this;
+    mOutGlop->transform.canvas.load(canvas);
+    mOutGlop->transform.transformFlags = transformFlags;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -482,11 +481,11 @@
     float left = destination.left;
     float top = destination.top;
 
-    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
-    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
+    const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
+    if (CC_LIKELY(meshTransform.isPureTranslate())) {
         // snap by adjusting the model view matrix
-        const float translateX = canvasTransform.getTranslateX();
-        const float translateY = canvasTransform.getTranslateY();
+        const float translateX = meshTransform.getTranslateX();
+        const float translateY = meshTransform.getTranslateY();
 
         left = (int) floorf(left + translateX + 0.5f) - translateX;
         top = (int) floorf(top + translateY + 0.5f) - translateY;
@@ -512,11 +511,11 @@
     TRIGGER_STAGE(kModelViewStage);
     REQUIRE_STAGES(kTransformStage | kFillStage);
 
-    const Matrix4& canvasTransform = mOutGlop->transform.canvas;
-    if (CC_LIKELY(canvasTransform.isPureTranslate())) {
+    const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
+    if (CC_LIKELY(meshTransform.isPureTranslate())) {
         // snap by adjusting the model view matrix
-        const float translateX = canvasTransform.getTranslateX();
-        const float translateY = canvasTransform.getTranslateY();
+        const float translateX = meshTransform.getTranslateX();
+        const float translateY = meshTransform.getTranslateY();
 
         offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
         offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
@@ -549,7 +548,7 @@
     if (glop.fill.texture.texture != nullptr) {
         LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
                         || (!description.hasTexture && !description.hasExternalTexture)
-                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) == 0)),
+                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0)),
                 "Texture %p, hT%d, hET %d, attribFlags %x",
                 glop.fill.texture.texture,
                 description.hasTexture, description.hasExternalTexture,
@@ -557,13 +556,13 @@
     } else {
         LOG_ALWAYS_FATAL_IF((description.hasTexture
                         || description.hasExternalTexture
-                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) != 0)),
+                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)),
                 "No texture, hT%d, hET %d, attribFlags %x",
                 description.hasTexture, description.hasExternalTexture,
                 glop.mesh.vertices.attribFlags);
     }
 
-    if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kAlpha)
+    if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
             && glop.mesh.vertices.bufferObject) {
         LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
     }
@@ -575,16 +574,16 @@
 
 void GlopBuilder::build() {
     REQUIRE_STAGES(kAllStages);
-    if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) {
+    if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) {
         if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) {
             mDescription.hasTexture = true;
         } else {
             mDescription.hasExternalTexture = true;
         }
-
     }
-    mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kColor;
-    mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha;
+
+    mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
+    mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha;
 
     // Enable debug highlight when what we're about to draw is tested against
     // the stencil buffer and if stencil highlight debugging is on
@@ -594,8 +593,22 @@
 
     // serialize shader info into ShaderData
     GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
-    SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView,
-            &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
+
+    if (CC_LIKELY(!mShader)) {
+        mOutGlop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
+    } else {
+        Matrix4 shaderMatrix;
+        if (mOutGlop->transform.transformFlags & TransformFlags::MeshIgnoresCanvasTransform) {
+            // canvas level transform was built into the modelView and geometry,
+            // so the shader matrix must reverse this
+            shaderMatrix.loadInverse(mOutGlop->transform.canvas);
+            shaderMatrix.multiply(mOutGlop->transform.modelView);
+        } else {
+            shaderMatrix.load(mOutGlop->transform.modelView);
+        }
+        SkiaShader::store(mCaches, *mShader, shaderMatrix,
+                &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
+    }
 
     // duplicates ProgramCache's definition of color uniform presence
     const bool singleColor = !mDescription.hasTexture
@@ -608,7 +621,7 @@
 
     // Final step: populate program and map bounds into render target space
     mOutGlop->fill.program = mCaches.programCache.get(mDescription);
-    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
+    mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index b335a2c..549bb21 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -16,6 +16,7 @@
 #ifndef RENDERSTATE_GLOPBUILDER_H
 #define RENDERSTATE_GLOPBUILDER_H
 
+#include "Glop.h"
 #include "OpenGLRenderer.h"
 #include "Program.h"
 #include "renderstate/Blend.h"
@@ -32,14 +33,14 @@
 class RenderState;
 class Texture;
 class VertexBuffer;
-struct Glop;
 
-enum class TextureFillFlags {
-    kNone = 0,
-    kIsAlphaMaskTexture = 1 << 0,
-    kForceFilter = 1 << 1,
-};
-MAKE_FLAGS_ENUM(TextureFillFlags);
+namespace TextureFillFlags {
+    enum {
+        None = 0,
+        IsAlphaMaskTexture = 1 << 0,
+        ForceFilter = 1 << 1,
+    };
+}
 
 class GlopBuilder {
     PREVENT_COPY_AND_ASSIGN(GlopBuilder);
@@ -57,7 +58,7 @@
     GlopBuilder& setMeshPatchQuads(const Patch& patch);
 
     GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale);
-    GlopBuilder& setFillTexturePaint(Texture& texture, int textureFillFlags,
+    GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags,
             const SkPaint* paint, float alphaScale);
     GlopBuilder& setFillPathTexturePaint(PathTexture& texture,
             const SkPaint& paint, float alphaScale);
@@ -69,7 +70,10 @@
             float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
     GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
 
-    GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
+    GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
+        setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
+        return *this;
+    }
 
     GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
     GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
@@ -98,6 +102,8 @@
     void setFill(int color, float alphaScale,
             SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
             const SkShader* shader, const SkColorFilter* colorFilter);
+    void setTransform(const Matrix4& ortho, const Matrix4& canvas,
+            const int transformFlags);
 
     enum StageFlags {
         kInitialStage = 0,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8f91620..5769376 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -827,7 +827,7 @@
         // the layer contains screen buffer content that shouldn't be alpha modulated
         // (and any necessary alpha modulation was handled drawing into the layer)
         writableSnapshot()->alpha = 1.0f;
-        composeLayerRect(layer, rect, true);
+        composeLayerRectSwapped(layer, rect);
         restore();
     }
 
@@ -849,31 +849,40 @@
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
             .setFillTextureLayer(*layer, getLayerAlpha(layer))
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(), TransformFlags::None)
             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
     renderGlop(glop);
 }
 
-void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
+void OpenGLRenderer::composeLayerRectSwapped(Layer* layer, const Rect& rect) {
+    Glop glop;
+    GlopBuilder(mRenderState, mCaches, &glop)
+            .setMeshTexturedUvQuad(nullptr, layer->texCoords)
+            .setFillLayer(layer->getTexture(), layer->getColorFilter(),
+                    getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::Swap)
+            .setTransform(*currentSnapshot(), TransformFlags::MeshIgnoresCanvasTransform)
+            .setModelViewMapUnitToRect(rect)
+            .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+            .build();
+    renderGlop(glop);
+}
+
+void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect) {
     if (layer->isTextureLayer()) {
         EVENT_LOGD("composeTextureLayerRect");
         drawTextureLayer(layer, rect);
     } else {
         EVENT_LOGD("composeHardwareLayerRect");
 
-        Blend::ModeOrderSwap modeUsage = swap ?
-                Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap;
-        const Matrix4& transform = swap ? Matrix4::identity() : *currentTransform();
-        const bool tryToSnap = !swap
-                && layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
+        const bool tryToSnap = layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
                 && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
         Glop glop;
         GlopBuilder(mRenderState, mCaches, &glop)
                 .setMeshTexturedUvQuad(nullptr, layer->texCoords)
-                .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), modeUsage)
-                .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+                .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+                .setTransform(*currentSnapshot(), TransformFlags::None)
                 .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
                 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                 .build();
@@ -1014,7 +1023,7 @@
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
             .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1128,11 +1137,12 @@
         // stencil setup from doing the same thing again
         mLayers.clear();
 
+        const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
         Glop glop;
         GlopBuilder(mRenderState, mCaches, &glop)
                 .setMeshIndexedQuads(&mesh[0], quadCount)
                 .setFillClear()
-                .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+                .setTransform(*currentSnapshot(), transformFlags)
                 .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
                 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                 .build();
@@ -1316,13 +1326,13 @@
 
     mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
             scissorBox.getWidth(), scissorBox.getHeight());
-
+    const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
     Glop glop;
     Vertex* vertices = &rectangleVertices[0];
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
             .setFillBlack()
-            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRect(0, 0, scissorBox)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1518,13 +1528,15 @@
     bool snap = pureTranslate;
     const float x = floorf(bounds.left + 0.5f);
     const float y = floorf(bounds.top + 0.5f);
-    int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
-            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+
+    const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
+            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
+    const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedMesh(vertices, bitmapCount * 6)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1541,13 +1553,13 @@
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
-    int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
-            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+    const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
+            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedUnitQuad(texture->uvMapper)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1632,11 +1644,12 @@
      * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
      * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
      */
+    const int textureFillFlags = TextureFillFlags::None;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshColoredTexturedMesh(mesh.get(), elementCount)
-            .setFillTexturePaint(*texture, static_cast<int>(TextureFillFlags::kNone), paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1657,16 +1670,15 @@
             fmin(1.0f, src.right / texture->width),
             fmin(1.0f, src.bottom / texture->height));
 
-    const int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType)
-            ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone);
+    const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
+            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
     const bool tryToSnap = MathUtils::areEqual(src.getWidth(), dst.getWidth())
             && MathUtils::areEqual(src.getHeight(), dst.getHeight());
-
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedUvQuad(texture->uvMapper, uv)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewMapUnitToRectOptionalSnap(tryToSnap, dst)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1684,15 +1696,15 @@
     if (!texture) return;
 
     // 9 patches are built for stretching - always filter
-    int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
+    int textureFillFlags = TextureFillFlags::ForceFilter;
     if (bitmap->colorType() == kAlpha_8_SkColorType) {
-        textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
+        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
     }
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshPatchQuads(*mesh)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1712,16 +1724,17 @@
     const AutoTexture autoCleanup(texture);
 
     // TODO: get correct bounds from caller
+    const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
     // 9 patches are built for stretching - always filter
-    int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter);
+    int textureFillFlags = TextureFillFlags::ForceFilter;
     if (bitmap->colorType() == kAlpha_8_SkColorType) {
-        textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture;
+        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
     }
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedIndexedQuads(vertices, elementCount)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -1736,13 +1749,13 @@
         return;
     }
 
-    bool fudgeOffset = displayFlags & kVertexBuffer_Offset;
     bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
+    const int transformFlags = TransformFlags::OffsetByFudgeFactor;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshVertexBuffer(vertexBuffer, shadowInterp)
             .setFillPaint(*paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -2028,7 +2041,7 @@
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedUnitQuad(nullptr)
             .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -2353,7 +2366,7 @@
             GlopBuilder(mRenderState, mCaches, &glop)
                     .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
                     .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
-                    .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+                    .setTransform(*currentSnapshot(),  TransformFlags::None)
                     .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
                     .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                     .build();
@@ -2411,7 +2424,7 @@
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedUnitQuad(nullptr)
             .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+            .setTransform(*currentSnapshot(),  TransformFlags::None)
             .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -2543,12 +2556,13 @@
         return;
     }
 
-    const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+    const int transformFlags = ignoreTransform
+            ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshIndexedQuads(&mesh[0], count / 4)
             .setFillPaint(*paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
@@ -2557,12 +2571,13 @@
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         const SkPaint* paint, bool ignoreTransform) {
-    const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+    const int transformFlags = ignoreTransform
+            ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshUnitQuad()
             .setFillPaint(*paint, currentSnapshot()->alpha)
-            .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+            .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 8dae82c..29fbf0c 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -640,13 +640,20 @@
     void composeLayerRegion(Layer* layer, const Rect& rect);
 
     /**
-     * Compose the specified layer as a simple rectangle.
+     * Restores the content in layer to the screen, swapping the blend mode,
+     * specifically used in the restore() of a saveLayerAlpha().
      *
-     * @param layer The layer to compose
-     * @param rect The layer's bounds
-     * @param swap If true, the source and destination are swapped
+     * This allows e.g. a layer that would have been drawn on top of existing content (with SrcOver)
+     * to be drawn underneath.
+     *
+     * This will always ignore the canvas transform.
      */
-    void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false);
+    void composeLayerRectSwapped(Layer* layer, const Rect& rect);
+
+    /**
+     * Draws the content in layer to the screen.
+     */
+    void composeLayerRect(Layer* layer, const Rect& rect);
 
     /**
      * Clears all the regions corresponding to the current list of layers.
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index ecf8b6b..2cfb9e1 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -346,33 +346,28 @@
     glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
 }
 
-void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
         GLuint* textureUnit, ProgramDescription* description,
         SkiaShaderData* outData) {
-    if (!shader) {
-        outData->skiaShaderType = kNone_SkiaShaderType;
-        return;
-    }
-
-    if (tryStoreGradient(caches, *shader, modelViewMatrix,
+    if (tryStoreGradient(caches, shader, modelViewMatrix,
             textureUnit, description, &outData->gradientData)) {
         outData->skiaShaderType = kGradient_SkiaShaderType;
         return;
     }
 
-    if (tryStoreBitmap(caches, *shader, modelViewMatrix,
+    if (tryStoreBitmap(caches, shader, modelViewMatrix,
             textureUnit, description, &outData->bitmapData)) {
         outData->skiaShaderType = kBitmap_SkiaShaderType;
         return;
     }
 
-    if (tryStoreCompose(caches, *shader, modelViewMatrix,
+    if (tryStoreCompose(caches, shader, modelViewMatrix,
             textureUnit, description, outData)) {
         outData->skiaShaderType = kCompose_SkiaShaderType;
         return;
     }
 
-    if (tryStoreLayer(caches, *shader, modelViewMatrix,
+    if (tryStoreLayer(caches, shader, modelViewMatrix,
             textureUnit, description, &outData->layerData)) {
         outData->skiaShaderType = kLayer_SkiaShaderType;
     }
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 5b8aa86..884196d 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -87,7 +87,7 @@
 
 class SkiaShader {
 public:
-    static void store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix,
+    static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
             GLuint* textureUnit, ProgramDescription* description,
             SkiaShaderData* outData);
     static void apply(Caches& caches, const SkiaShaderData& data);
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index df12e63..3ebd57b 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -217,8 +217,8 @@
 
     fill.program->set(glop.transform.ortho,
             glop.transform.modelView,
-            glop.transform.canvas,
-            glop.transform.fudgingOffset);
+            glop.transform.meshTransform(),
+            glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
 
     // Color filter uniforms
     if (fill.filterMode == ProgramDescription::kColorBlend) {
@@ -260,7 +260,7 @@
     // indices
     meshState().bindIndicesBufferInternal(indices.bufferObject);
 
-    if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) {
+    if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
         const Glop::Fill::TextureData& texture = fill.texture;
         // texture always takes slot 0, shader samplers increment from there
         mCaches->textureState().activateTexture(0);
@@ -284,13 +284,13 @@
         meshState().disableTexCoordsVertexArray();
     }
     int colorLocation = -1;
-    if (vertices.attribFlags & VertexAttribFlags::kColor) {
+    if (vertices.attribFlags & VertexAttribFlags::Color) {
         colorLocation = fill.program->getAttrib("colors");
         glEnableVertexAttribArray(colorLocation);
         glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, vertices.color);
     }
     int alphaLocation = -1;
-    if (vertices.attribFlags & VertexAttribFlags::kAlpha) {
+    if (vertices.attribFlags & VertexAttribFlags::Alpha) {
         // NOTE: alpha vertex position is computed assuming no VBO
         const void* alphaCoords = ((const GLbyte*) vertices.position) + kVertexAlphaOffset;
         alphaLocation = fill.program->getAttrib("vtxAlpha");
@@ -318,7 +318,7 @@
 
             // rebind pointers without forcing, since initial bind handled above
             meshState().bindPositionVertexPointer(false, vertexData, vertices.stride);
-            if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) {
+            if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
                 meshState().bindTexCoordsVertexPointer(false,
                         vertexData + kMeshTextureOffset, vertices.stride);
             }
@@ -336,10 +336,10 @@
     // -----------------------------------
     // ---------- Mesh teardown ----------
     // -----------------------------------
-    if (vertices.attribFlags & VertexAttribFlags::kAlpha) {
+    if (vertices.attribFlags & VertexAttribFlags::Alpha) {
         glDisableVertexAttribArray(alphaLocation);
     }
-    if (vertices.attribFlags & VertexAttribFlags::kColor) {
+    if (vertices.attribFlags & VertexAttribFlags::Color) {
         glDisableVertexAttribArray(colorLocation);
     }
 }
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index d8a9921..6d9acd4 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,11 +52,12 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-enum class DumpFlags {
-        kFrameStats = 1 << 0,
-        kReset      = 1 << 1,
+namespace DumpFlags {
+    enum {
+        FrameStats = 1 << 0,
+        Reset      = 1 << 1,
+    };
 };
-MAKE_FLAGS_ENUM(DumpFlags)
 
 CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
         RenderNode* rootRenderNode, IContextFactory* contextFactory) {
@@ -409,10 +410,10 @@
         int fd, int dumpFlags) {
     args->context->profiler().dumpData(args->fd);
     args->thread->jankTracker().dump(args->fd);
-    if (args->dumpFlags & DumpFlags::kFrameStats) {
+    if (args->dumpFlags & DumpFlags::FrameStats) {
         args->context->dumpFrames(args->fd);
     }
-    if (args->dumpFlags & DumpFlags::kReset) {
+    if (args->dumpFlags & DumpFlags::Reset) {
         args->context->resetFrameStats();
     }
     return nullptr;