Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d27977d1 authored by Romain Guy's avatar Romain Guy
Browse files

Add support for BitmapShader.

This change also fixes an issue with the clip and layers.

Change-Id: I5fd9832098d8cf7ae8eb781ff9bffe7defaea279
parent 7ba6617a
Loading
Loading
Loading
Loading
+38 −1
Original line number Diff line number Diff line
@@ -17,16 +17,21 @@
package android.view;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.PorterDuff;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.SweepGradient;

import javax.microedition.khronos.opengles.GL;

@@ -369,6 +374,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
        // Shaders are ignored when drawing patches
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top,
                dst.right, dst.bottom, nativePaint);
@@ -379,6 +385,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
    }
@@ -387,6 +394,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
    }
@@ -395,6 +403,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint
@@ -403,6 +412,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
                dst.left, dst.top, dst.right, dst.bottom, nativePaint
@@ -416,6 +426,7 @@ class GLES20Canvas extends Canvas {
    @Override
    public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
            int width, int height, boolean hasAlpha, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
        final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
@@ -426,6 +437,7 @@ class GLES20Canvas extends Canvas {
    @Override
    public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
            int width, int height, boolean hasAlpha, Paint paint) {
        // Shaders are ignored when drawing bitmaps
        drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
    }

@@ -532,7 +544,9 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
        boolean hasShader = setupShader(paint);
        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
        if (hasShader) nResetShader(mRenderer);
    }

    private native void nDrawRect(int renderer, float left, float top, float right, float bottom,
@@ -555,7 +569,7 @@ class GLES20Canvas extends Canvas {

    @Override
    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
        throw new UnsupportedOperationException();
        // TODO: Implement
    }

    @Override
@@ -607,4 +621,27 @@ class GLES20Canvas extends Canvas {
            int indexOffset, int indexCount, Paint paint) {
        // TODO: Implement
    }

    private boolean setupShader(Paint paint) {
        final Shader shader = paint.getShader();
        if (shader != null) {
            if (shader instanceof BitmapShader) {
                final BitmapShader bs = (BitmapShader) shader;
                nSetupBitmapShader(mRenderer, bs.native_instance, bs.mBitmap.mNativeBitmap,
                        bs.mTileX, bs.mTileY, bs.mLocalMatrix);
                return true;
            } else if (shader instanceof LinearGradient) {
                // TODO: Implement
            } else if (shader instanceof RadialGradient) {
                // TODO: Implement
            } else if (shader instanceof SweepGradient) {
                // TODO: Implement
            }
        }
        return false;
    }
    
    private native void nSetupBitmapShader(int renderer, int shader, int bitmap,
            int tileX, int tileY, int matrix);
    private native void nResetShader(int renderer);  
}
+19 −0
Original line number Diff line number Diff line
@@ -220,6 +220,22 @@ static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas,
    renderer->drawRect(left, top, right, bottom, paint);
}

// ----------------------------------------------------------------------------
// Shaders
// ----------------------------------------------------------------------------

static void android_view_GLES20Canvas_resetShader(JNIEnv* env, jobject canvas,
        OpenGLRenderer* renderer) {
    renderer->resetShader();
}

static void android_view_GLES20Canvas_setupBitmapShader(JNIEnv* env, jobject canvas,
        OpenGLRenderer* renderer, SkShader* shader, SkBitmap* bitmap,
        SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix) {
    renderer->setupBitmapShader(bitmap, tileX, tileY, matrix,
            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
}

// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -259,6 +275,9 @@ static JNINativeMethod gMethods[] = {
    {   "nDrawColor",         "(III)V",          (void*) android_view_GLES20Canvas_drawColor },
    {   "nDrawRect",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawRect },

    {   "nResetShader",       "(I)V",            (void*) android_view_GLES20Canvas_resetShader },
    {   "nSetupBitmapShader", "(IIIIII)V",       (void*) android_view_GLES20Canvas_setupBitmapShader },

    {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
            (void*) android_view_GLES20Canvas_getClipBounds },
};
+15 −4
Original line number Diff line number Diff line
@@ -23,9 +23,19 @@ package android.graphics;
 * drawn with that paint will get its color(s) from the shader.
 */
public class Shader {
    /**
     * Local matrix native instance.
     * 
     * @hide
     */
    public int mLocalMatrix;

    // this is set by subclasses, but don't make it public
    /* package */ int native_instance;
    /**
     * This is set by subclasses, but don't make it public.
     * 
     * @hide 
     */
    public int native_instance;

    public enum TileMode {
        /**
@@ -64,11 +74,12 @@ public class Shader {
     * @param localM The shader's new local matrix, or null to specify identity
     */
    public void setLocalMatrix(Matrix localM) {
        nativeSetLocalMatrix(native_instance,
                             localM != null ? localM.native_instance : 0);
        mLocalMatrix = localM != null ? localM.native_instance : 0;
        nativeSetLocalMatrix(native_instance, mLocalMatrix);
    }

    protected void finalize() throws Throwable {
        super.finalize();
        nativeDestructor(native_instance);
    }

+121 −33
Original line number Diff line number Diff line
@@ -90,6 +90,12 @@ static const Blender gBlends[] = {
        { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
};

static const GLint gTileModes[] = {
        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
        GL_REPEAT,          // == SkShader::kRepeat_Mode
        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
};

///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
@@ -116,9 +122,15 @@ OpenGLRenderer::OpenGLRenderer():
        LOGD("  Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE);
    }

    mDrawColorShader = new DrawColorProgram;
    mDrawTextureShader = new DrawTextureProgram;
    mCurrentShader = mDrawTextureShader;
    mDrawColorProgram = new DrawColorProgram;
    mDrawTextureProgram = new DrawTextureProgram;
    mCurrentProgram = mDrawTextureProgram;

    mShader = kShaderNone;
    mShaderTileX = SkShader::kClamp_TileMode;
    mShaderTileY = SkShader::kClamp_TileMode;
    mShaderMatrix = NULL;
    mShaderBitmap = NULL;

    memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
}
@@ -295,7 +307,7 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
        float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {

    LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
    LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
    LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());

    GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
@@ -327,7 +339,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
    saveSnapshot();
    // TODO: This doesn't preserve other transformations (check Skia first)
    mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
    mSnapshot->clipRect.set(left, top, right, bottom);
    mSnapshot->setClip(left, top, right, bottom);
    mSnapshot->height = bottom - top;
    setScissorFromClip();

@@ -512,50 +524,125 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
    // Skia draws using the color's alpha channel if < 255
    // Otherwise, it uses the paint's alpha
    int color = p->getColor();
    if (((color >> 24) & 0xFF) == 255) {
    if (((color >> 24) & 0xff) == 255) {
        color |= p->getAlpha() << 24;
    }

    drawColorRect(left, top, right, bottom, color, mode);
}

///////////////////////////////////////////////////////////////////////////////
// Shaders
///////////////////////////////////////////////////////////////////////////////

void OpenGLRenderer::resetShader() {
    mShader = OpenGLRenderer::kShaderNone;
    mShaderBlend = false;
    mShaderTileX = SkShader::kClamp_TileMode;
    mShaderTileY = SkShader::kClamp_TileMode;
}

void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
        SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
    mShader = kShaderBitmap;
    mShaderBlend = hasAlpha;
    mShaderBitmap = bitmap;
    mShaderTileX = tileX;
    mShaderTileY = tileY;
    mShaderMatrix = matrix;
}

///////////////////////////////////////////////////////////////////////////////
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////

void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
        int color, SkXfermode::Mode mode, bool ignoreTransform) {
    // If a shader is set, preserve only the alpha
    if (mShader != kShaderNone) {
        color |= 0x00ffffff;
    }

    // Render using pre-multiplied alpha
    const int alpha = (color >> 24) & 0xFF;
    const GLfloat a = alpha / 255.0f;
    const GLfloat r = ((color >> 16) & 0xFF) / 255.0f;
    const GLfloat g = ((color >>  8) & 0xFF) / 255.0f;
    const GLfloat b = ((color      ) & 0xFF) / 255.0f;
    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;

    switch (mShader) {
        case kShaderBitmap:
            drawBitmapShader(left, top, right, bottom, a, mode);
            return;
        default:
            break;
    }

    // Pre-multiplication happens when setting the shader color
    chooseBlending(alpha < 255, mode);
    chooseBlending(alpha < 255 || mShaderBlend, mode);

    mModelView.loadTranslate(left, top, 0.0f);
    mModelView.scale(right - left, bottom - top, 1.0f);

    const bool inUse = useShader(mDrawColorShader);
    // TODO: Pick the program matching the current shader
    sp<DrawColorProgram> program = mDrawColorProgram;
    if (!useProgram(program)) {
        const GLvoid* p = &gDrawColorVertices[0].position[0];
        glVertexAttribPointer(program->position, 2, GL_FLOAT, GL_FALSE,
                gDrawColorVertexStride, p);
    }

    if (!ignoreTransform) {
        mDrawColorShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
        program->set(mOrthoMatrix, mModelView, mSnapshot->transform);
    } else {
        mat4 identity;
        mDrawColorShader->set(mOrthoMatrix, mModelView, identity);
        program->set(mOrthoMatrix, mModelView, identity);
    }

    if (!inUse) {
        const GLvoid* p = &gDrawColorVertices[0].position[0];
        glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
                gDrawColorVertexStride, p);
    }
    // Render using pre-multiplied alpha
    glUniform4f(mDrawColorShader->color, r * a, g * a, b * a, a);
    glUniform4f(program->color, r, g, b, a);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
}

void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
        float alpha, SkXfermode::Mode mode) {
    const Texture* texture = mTextureCache.get(mShaderBitmap);

    const float width = texture->width;
    const float height = texture->height;

    // This could be done in the vertex shader but we have only 4 vertices
    float u1 = 0.0f;
    float v1 = 0.0f;
    float u2 = right - left;
    float v2 = bottom - top;

    if (mShaderMatrix) {
        SkMatrix inverse;
        mShaderMatrix->invert(&inverse);
        mat4 m(inverse);
        Rect r(u1, v1, u2, v2);
        m.mapRect(r);

        u1 = r.left;
        u2 = r.right;
        v1 = r.top;
        v2 = r.bottom;
    }

    u1 /= width;
    u2 /= width;
    v1 /= height;
    v2 /= height;

    resetDrawTextureTexCoords(u1, v1, u2, v2);

    drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);

    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}

void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
        const Texture* texture, const SkPaint* paint) {
    int alpha;
@@ -578,21 +665,22 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
    mModelView.loadTranslate(left, top, 0.0f);
    mModelView.scale(right - left, bottom - top, 1.0f);

    useShader(mDrawTextureShader);
    mDrawTextureShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
    useProgram(mDrawTextureProgram);
    mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);

    chooseBlending(blend || alpha < 1.0f, mode);

    // TODO: Only bind/set parameters when needed
    glBindTexture(GL_TEXTURE_2D, texture);

    // TODO handle tiling and filtering here
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]);

    // Always premultiplied
    glUniform4f(mDrawTextureShader->color, alpha, alpha, alpha, alpha);
    glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);

    glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
    glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
            gDrawTextureVertexStride, vertices);
    glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
    glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
            gDrawTextureVertexStride, texCoords);

    if (!indices) {
@@ -630,11 +718,11 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPr
    mBlend = blend;
}

bool OpenGLRenderer::useShader(const sp<Program>& shader) {
    if (!shader->isInUse()) {
        mCurrentShader->remove();
        shader->use();
        mCurrentShader = shader;
bool OpenGLRenderer::useProgram(const sp<Program>& program) {
    if (!program->isInUse()) {
        mCurrentProgram->remove();
        program->use();
        mCurrentProgram = program;
        return false;
    }
    return true;
+48 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <SkBitmap.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkShader.h>
#include <SkXfermode.h>

#include <utils/RefBase.h>
@@ -98,7 +99,22 @@ public:
    void drawColor(int color, SkXfermode::Mode mode);
    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);

    void resetShader();
    void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY,
            SkMatrix* matrix, bool hasAlpha);

private:
    /**
     * Type of Skia shader in use.
     */
    enum ShaderType {
        kShaderNone,
        kShaderBitmap,
        kShaderLinearGradient,
        kShaderCircularGradient,
        kShaderSweepGradient
    };

    /**
     * Saves the current state of the renderer as a new snapshot.
     * The new snapshot is saved in mSnapshot and the previous snapshot
@@ -216,6 +232,19 @@ private:
            float alpha, SkXfermode::Mode mode, bool blend,
            GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);

    /**
     * Fills the specified rectangle with the currently set bitmap shader.
     *
     * @param left The left coordinate of the rectangle
     * @param top The top coordinate of the rectangle
     * @param right The right coordinate of the rectangle
     * @param bottom The bottom coordinate of the rectangle
     * @param alpha An additional translucency parameter, between 0.0f and 1.0f
     * @param mode The blending mode
     */
    void drawBitmapShader(float left, float top, float right, float bottom, float alpha,
            SkXfermode::Mode mode);

    /**
     * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
     * back to default is achieved by calling:
@@ -246,14 +275,16 @@ private:
    inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);

    /**
     * Use the specified shader with the current GL context. If the shader is already
     * in use, it will not be bound again. If it is not in use, the current shader is
     * marked unused and the specified shader becomes used and becomes the new
     * current shader.
     * Use the specified program with the current GL context. If the program is already
     * in use, it will not be bound again. If it is not in use, the current program is
     * marked unused and the specified program becomes used and becomes the new
     * current program.
     *
     * @return true If the specified shader was already in use, false otherwise.
     * @param program The program to use
     *
     * @return true If the specified program was already in use, false otherwise.
     */
    inline bool useShader(const sp<Program>& shader);
    inline bool useProgram(const sp<Program>& program);

    // Dimensions of the drawing surface
    int mWidth, mHeight;
@@ -272,9 +303,9 @@ private:
    sp<Snapshot> mSnapshot;

    // Shaders
    sp<Program> mCurrentShader;
    sp<DrawColorProgram> mDrawColorShader;
    sp<DrawTextureProgram> mDrawTextureShader;
    sp<Program> mCurrentProgram;
    sp<DrawColorProgram> mDrawColorProgram;
    sp<DrawTextureProgram> mDrawTextureProgram;

    // Used to draw textured quads
    TextureVertex mDrawTextureVertices[4];
@@ -284,6 +315,14 @@ private:
    GLenum mLastSrcMode;
    GLenum mLastDstMode;

    // Skia shader
    ShaderType mShader;
    bool mShaderBlend;
    SkBitmap* mShaderBitmap;
    SkShader::TileMode mShaderTileX;
    SkShader::TileMode mShaderTileY;
    SkMatrix* mShaderMatrix;

    // Various caches
    TextureCache mTextureCache;
    LayerCache mLayerCache;
Loading