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

Commit 413ab3a5 authored by Romain Guy's avatar Romain Guy Committed by Android (Google) Code Review
Browse files

Merge "Improve gradients" into jb-mr1-dev

parents 3805e8ca 42e1e0d4
Loading
Loading
Loading
Loading
+38 −8
Original line number Diff line number Diff line
@@ -150,19 +150,35 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
    jfloat* storedBounds = new jfloat[4];
    storedBounds[0] = x0; storedBounds[1] = y0;
    storedBounds[2] = x1; storedBounds[3] = y1;
    jfloat* storedPositions = new jfloat[count];
    uint32_t* storedColors = new uint32_t[count];
    for (size_t i = 0; i < count; i++) {
        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
    }
    
    bool missFirst = false;
    bool missLast = false;
    size_t stopCount = count;

    jfloat* storedPositions = NULL;
    if (posArray) {
        AutoJavaFloatArray autoPos(env, posArray, count);
        const float* posValues = autoPos.ptr();
        for (size_t i = 0; i < count; i++) {
            storedPositions[i] = posValues[i];

        missFirst = posValues[0] != 0.0f;
        missLast = posValues[count - 1] != 1.0f;

        stopCount += missFirst + missLast;
        storedPositions = new jfloat[stopCount];

        if (missFirst) {
            storedPositions[0] = 0.0f;
        }

        for (size_t i = missFirst; i < count + missFirst; i++) {
            storedPositions[i] = posValues[i - missFirst];
        }

        if (missLast) {
            storedPositions[stopCount - 1] = 1.0f;
        }
    } else {
        storedPositions = new jfloat[count];
        storedPositions[0] = 0.0f;
        const jfloat step = 1.0f / (count - 1);
        for (size_t i = 1; i < count - 1; i++) {
@@ -171,8 +187,22 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
        storedPositions[count - 1] = 1.0f;
    }

    uint32_t* storedColors = new uint32_t[stopCount];

    if (missFirst) {
        storedColors[0] = static_cast<uint32_t>(colorValues[0]);
    }

    for (size_t i = missFirst; i < count + missFirst; i++) {
        storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
    }

    if (missLast) {
        storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
    }

    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
            storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
            storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);

    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
+98 −42
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

#define LOG_TAG "OpenGLRenderer"

#include <SkCanvas.h>
#include <SkGradientShader.h>

#include <utils/threads.h>

#include "Debug.h"
@@ -28,6 +25,22 @@
namespace android {
namespace uirenderer {

///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////

#define GRADIENT_TEXTURE_HEIGHT 2
#define GRADIENT_BYTES_PER_PIXEL 4

///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////

template<typename T>
static inline T min(T a, T b) {
    return a < b ? a : b;
}

///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
@@ -83,7 +96,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {

void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
    if (texture) {
        const uint32_t size = texture->width * texture->height * 4;
        const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
        mSize -= size;
    }

@@ -97,14 +110,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
// Caching
///////////////////////////////////////////////////////////////////////////////

Texture* GradientCache::get(uint32_t* colors, float* positions,
        int count, SkShader::TileMode tileMode) {
Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {

    GradientCacheEntry gradient(colors, positions, count, tileMode);
    GradientCacheEntry gradient(colors, positions, count);
    Texture* texture = mCache.get(gradient);

    if (!texture) {
        texture = addLinearGradient(gradient, colors, positions, count, tileMode);
        texture = addLinearGradient(gradient, colors, positions, count);
    }

    return texture;
@@ -114,39 +126,41 @@ void GradientCache::clear() {
    mCache.clear();
}

Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
        uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
    int width = 256 * (count - 1);
    width = width < mMaxTextureSize ? width : mMaxTextureSize;

    SkBitmap bitmap;
    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4);
    bitmap.allocPixels();
    bitmap.eraseColor(0);
void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
        GradientInfo& info) {
    uint32_t width = 1 << (31 - __builtin_clz(256 * (count - 1)));
    bool hasAlpha = false;

    SkCanvas canvas(bitmap);
    for (int i = 0; i < count; i++) {
        if (((colors[i] >> 24) & 0xff) < 255) {
            hasAlpha = true;
            break;
        }
    }

    SkPoint points[2];
    points[0].set(0.0f, 0.0f);
    points[1].set(bitmap.width(), 0.0f);
    info.width = min(width, uint32_t(mMaxTextureSize));
    info.hasAlpha = hasAlpha;
}

    SkShader* localShader = SkGradientShader::CreateLinear(points,
            reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
        uint32_t* colors, float* positions, int count) {

    SkPaint p;
    p.setStyle(SkPaint::kStrokeAndFill_Style);
    p.setShader(localShader)->unref();
    GradientInfo info;
    getGradientInfo(colors, count, info);

    canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 4.0f, p);
    Texture* texture = new Texture;
    texture->width = info.width;
    texture->height = GRADIENT_TEXTURE_HEIGHT;
    texture->blend = info.hasAlpha;
    texture->generation = 1;

    // Asume the cache is always big enough
    const uint32_t size = bitmap.rowBytes() * bitmap.height();
    const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
    while (mSize + size > mMaxSize) {
        mCache.removeOldest();
    }

    Texture* texture = new Texture;
    generateTexture(&bitmap, texture);
    generateTexture(colors, positions, count, texture);

    mSize += size;
    mCache.put(gradient, texture);
@@ -154,25 +168,67 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
    return texture;
}

void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
    SkAutoLockPixels autoLock(*bitmap);
    if (!bitmap->readyToDraw()) {
        ALOGE("Cannot generate texture from shader");
        return;
void GradientCache::generateTexture(uint32_t* colors, float* positions,
        int count, Texture* texture) {

    const uint32_t width = texture->width;
    const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
    uint32_t pixels[width * texture->height];

    int currentPos = 1;

    float startA = (colors[0] >> 24) & 0xff;
    float startR = (colors[0] >> 16) & 0xff;
    float startG = (colors[0] >>  8) & 0xff;
    float startB = (colors[0] >>  0) & 0xff;

    float endA = (colors[1] >> 24) & 0xff;
    float endR = (colors[1] >> 16) & 0xff;
    float endG = (colors[1] >>  8) & 0xff;
    float endB = (colors[1] >>  0) & 0xff;

    float start = positions[0];
    float distance = positions[1] - start;

    uint8_t* p = (uint8_t*) pixels;
    for (uint32_t x = 0; x < width; x++) {
        float pos = x / float(width - 1);
        if (pos > positions[currentPos]) {
            startA = endA;
            startR = endR;
            startG = endG;
            startB = endB;
            start = positions[currentPos];

            currentPos++;

            endA = (colors[currentPos] >> 24) & 0xff;
            endR = (colors[currentPos] >> 16) & 0xff;
            endG = (colors[currentPos] >>  8) & 0xff;
            endB = (colors[currentPos] >>  0) & 0xff;
            distance = positions[currentPos] - start;
        }

    texture->generation = bitmap->getGenerationID();
    texture->width = bitmap->width();
    texture->height = bitmap->height();
        float amount = (pos - start) / distance;
        float oppAmount = 1.0f - amount;

        *p++ = uint8_t(startR * oppAmount + endR * amount);
        *p++ = uint8_t(startG * oppAmount + endG * amount);
        *p++ = uint8_t(startB * oppAmount + endB * amount);
        *p++ = uint8_t(startA * oppAmount + endA * amount);
    }

    for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
        memcpy(pixels + width * i, pixels, rowBytes);
    }

    glGenTextures(1, &texture->id);

    glBindTexture(GL_TEXTURE_2D, texture->id);
    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
    glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);

    texture->blend = !bitmap->isOpaque();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
            GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
            GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    texture->setFilter(GL_LINEAR);
    texture->setWrap(GL_CLAMP_TO_EDGE);
+21 −20
Original line number Diff line number Diff line
@@ -36,16 +36,14 @@ struct GradientCacheEntry {
        count = 0;
        colors = NULL;
        positions = NULL;
        tileMode = SkShader::kClamp_TileMode;
    }

    GradientCacheEntry(uint32_t* colors, float* positions, int count,
            SkShader::TileMode tileMode) {
        copy(colors, positions, count, tileMode);
    GradientCacheEntry(uint32_t* colors, float* positions, int count) {
        copy(colors, positions, count);
    }

    GradientCacheEntry(const GradientCacheEntry& entry) {
        copy(entry.colors, entry.positions, entry.count, entry.tileMode);
        copy(entry.colors, entry.positions, entry.count);
    }

    ~GradientCacheEntry() {
@@ -58,7 +56,7 @@ struct GradientCacheEntry {
            delete[] colors;
            delete[] positions;

            copy(entry.colors, entry.positions, entry.count, entry.tileMode);
            copy(entry.colors, entry.positions, entry.count);
        }

        return *this;
@@ -67,7 +65,6 @@ struct GradientCacheEntry {
    bool operator<(const GradientCacheEntry& r) const {
        const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
        LTE_INT(count) {
            LTE_INT(tileMode) {
            int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
            if (result< 0) return true;
            else if (result == 0) {
@@ -75,7 +72,6 @@ struct GradientCacheEntry {
                if (result < 0) return true;
            }
        }
        }
        return false;
    }

@@ -86,11 +82,10 @@ struct GradientCacheEntry {

private:

    void copy(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
    void copy(uint32_t* colors, float* positions, int count) {
        this->count = count;
        this->colors = new uint32_t[count];
        this->positions = new float[count];
        this->tileMode = tileMode;

        memcpy(this->colors, colors, count * sizeof(uint32_t));
        memcpy(this->positions, positions, count * sizeof(float));
@@ -118,8 +113,8 @@ public:
    /**
     * Returns the texture associated with the specified shader.
     */
    Texture* get(uint32_t* colors, float* positions,
            int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
    Texture* get(uint32_t* colors, float* positions, int count);

    /**
     * Clears the cache. This causes all textures to be deleted.
     */
@@ -144,10 +139,16 @@ private:
     * returned.
     */
    Texture* addLinearGradient(GradientCacheEntry& gradient,
            uint32_t* colors, float* positions, int count,
            SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
            uint32_t* colors, float* positions, int count);

    void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture);

    struct GradientInfo {
        uint32_t width;
        bool hasAlpha;
    };

    void generateTexture(SkBitmap* bitmap, Texture* texture);
    void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);

    GenerationCache<GradientCacheEntry, Texture*> mCache;

+7 −2
Original line number Diff line number Diff line
@@ -79,6 +79,8 @@ namespace uirenderer {

#define PROGRAM_HAS_GAMMA_CORRECTION 40

#define PROGRAM_IS_SIMPLE_GRADIENT 41

///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -96,14 +98,14 @@ typedef uint64_t programid;
 */
struct ProgramDescription {
    enum ColorModifier {
        kColorNone,
        kColorNone = 0,
        kColorMatrix,
        kColorLighting,
        kColorBlend
    };

    enum Gradient {
        kGradientLinear,
        kGradientLinear = 0,
        kGradientCircular,
        kGradientSweep
    };
@@ -129,6 +131,7 @@ struct ProgramDescription {

    bool hasGradient;
    Gradient gradientType;
    bool isSimpleGradient;

    SkXfermode::Mode shadersMode;

@@ -170,6 +173,7 @@ struct ProgramDescription {

        hasGradient = false;
        gradientType = kGradientLinear;
        isSimpleGradient = false;

        shadersMode = SkXfermode::kClear_Mode;

@@ -255,6 +259,7 @@ struct ProgramDescription {
        if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
        if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
        if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
        return key;
    }

+49 −23
Original line number Diff line number Diff line
@@ -71,13 +71,16 @@ const char* gVS_Header_Varyings_PointHasBitmap =
        "varying highp vec2 outPointBitmapTexCoords;\n";
// TODO: These values are used to sample from textures,
//       they may need to be highp
const char* gVS_Header_Varyings_HasGradient[3] = {
const char* gVS_Header_Varyings_HasGradient[6] = {
        // Linear
        "varying highp vec2 linear;\n",
        "varying highp float linear;\n",
        // Circular
        "varying highp vec2 circular;\n",
        "varying highp vec2 circular;\n",
        // Sweep
        "varying highp vec2 sweep;\n"
        "varying highp vec2 sweep;\n",
        "varying highp vec2 sweep;\n",
};
const char* gVS_Main =
        "\nvoid main(void) {\n";
@@ -85,13 +88,16 @@ 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] = {
const char* gVS_Main_OutGradient[6] = {
        // Linear
        "    linear = vec2((screenSpace * position).x, 0.5);\n",
        "    linear = (screenSpace * position).x;\n",
        // Circular
        "    circular = (screenSpace * position).xy;\n",
        "    circular = (screenSpace * position).xy;\n",
        // Sweep
        "    sweep = (screenSpace * position).xy;\n"
        "    sweep = (screenSpace * position).xy;\n",
        "    sweep = (screenSpace * position).xy;\n",
};
const char* gVS_Main_OutBitmapTexCoords =
        "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -131,13 +137,19 @@ const char* gFS_Uniforms_TextureSampler =
        "uniform sampler2D sampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
        "uniform samplerExternalOES sampler;\n";
const char* gFS_Uniforms_GradientSampler[3] = {
const char* gFS_Uniforms_GradientSampler[6] = {
        // Linear
        "uniform sampler2D gradientSampler;\n",
        "uniform vec4 startColor;\n"
        "uniform vec4 endColor;\n",
        // Circular
        "uniform sampler2D gradientSampler;\n",
        "uniform vec4 startColor;\n"
        "uniform vec4 endColor;\n",
        // Sweep
        "uniform sampler2D gradientSampler;\n"
        "uniform sampler2D gradientSampler;\n",
        "uniform vec4 startColor;\n"
        "uniform vec4 endColor;\n",
};
const char* gFS_Uniforms_BitmapSampler =
        "uniform sampler2D bitmapSampler;\n";
@@ -193,14 +205,22 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
        "\nvoid main(void) {\n"
        "    gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n"
        "}\n\n";
const char* gFS_Fast_SingleGradient =
const char* gFS_Fast_SingleGradient[2] = {
        "\nvoid main(void) {\n"
        "    gl_FragColor = texture2D(gradientSampler, linear);\n"
        "}\n\n";
const char* gFS_Fast_SingleModulateGradient =
        "}\n\n",
        "\nvoid main(void) {\n"
        "    gl_FragColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "}\n\n"
};
const char* gFS_Fast_SingleModulateGradient[2] = {
        "\nvoid main(void) {\n"
        "    gl_FragColor = color.a * texture2D(gradientSampler, linear);\n"
        "}\n\n";
        "}\n\n",
        "\nvoid main(void) {\n"
        "    gl_FragColor = color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
        "}\n\n"
};

// General case
const char* gFS_Main_FetchColor =
@@ -232,15 +252,18 @@ const char* gFS_Main_FetchA8Texture[2] = {
        // Modulate
        "    fragColor = color * texture2D(sampler, outTexCoords).a;\n"
};
const char* gFS_Main_FetchGradient[3] = {
const char* gFS_Main_FetchGradient[6] = {
        // Linear
        "    vec4 gradientColor = texture2D(gradientSampler, linear);\n",
        "    vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
        // Circular
        "    highp float index = length(circular);\n"
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
        "    vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
        // Sweep
        "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n"
        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
        "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
        "    vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
        "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
@@ -395,8 +418,11 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
    String8 vertexShader = generateVertexShader(description);
    String8 fragmentShader = generateFragmentShader(description);

    Program* program = new Program(description, vertexShader.string(), fragmentShader.string());
    return program;
    return new Program(description, vertexShader.string(), fragmentShader.string());
}

static inline size_t gradientIndex(const ProgramDescription& description) {
    return description.gradientType * 2 + description.isSimpleGradient;
}

String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
@@ -430,7 +456,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
        shader.append(gVS_Header_Varyings_IsAA);
    }
    if (description.hasGradient) {
        shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
        shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
    }
    if (description.hasBitmap) {
        shader.append(description.isPoint ?
@@ -449,7 +475,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
            shader.append(gVS_Main_AA);
        }
        if (description.hasGradient) {
            shader.append(gVS_Main_OutGradient[description.gradientType]);
            shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
        }
        if (description.hasBitmap) {
            shader.append(description.isPoint ?
@@ -491,7 +517,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
        shader.append(gVS_Header_Varyings_IsAA);
    }
    if (description.hasGradient) {
        shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
        shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
    }
    if (description.hasBitmap) {
        shader.append(description.isPoint ?
@@ -517,7 +543,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
        shader.append(gFS_Uniforms_AA);
    }
    if (description.hasGradient) {
        shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
        shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
    }
    if (description.hasBitmap && description.isPoint) {
        shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -567,9 +593,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
            fast = true;
        } else if (singleGradient) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleGradient);
                shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
            } else {
                shader.append(gFS_Fast_SingleModulateGradient);
                shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
            }
            fast = true;
        }
@@ -624,7 +650,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
            shader.append(gFS_Main_AccountForAA);
        }
        if (description.hasGradient) {
            shader.append(gFS_Main_FetchGradient[description.gradientType]);
            shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
        }
        if (description.hasBitmap) {
            if (description.isPoint) {
Loading