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

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

Work-around for a Skia rasterization bug

Bug #6411457

Skia does not generates the bottom right pixel of a rect when
drawing a rect as an SkPath into an alpha8 bitmap.

Change-Id: Ifb5286ae67745c9e44ee387b6d6ad607a9a2e6ce
parent a44a63ac
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -25,10 +25,15 @@ namespace android {
namespace uirenderer {

// Defined in ShapeCache.h

void computePathBounds(const SkPath* path, const SkPaint* paint,
        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
    const SkRect& bounds = path->getBounds();
    computeBounds(bounds, paint, left, top, offset, width, height);
}

void computeBounds(const SkRect& bounds, const SkPaint* paint,
        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
    const float pathWidth = fmax(bounds.width(), 1.0f);
    const float pathHeight = fmax(bounds.height(), 1.0f);

+22 −3
Original line number Diff line number Diff line
@@ -105,10 +105,29 @@ PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint)
    PathTexture* texture = get(entry);

    if (!texture) {
        SkPath path;
        path.addRect(0.0f, 0.0f, width, height, SkPath::kCW_Direction);
        SkRect bounds;
        bounds.set(0.0f, 0.0f, width, height);

        texture = addTexture(entry, &path, paint);
        float left, top, offset;
        uint32_t rectWidth, rectHeight;
        computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight);

        if (!checkTextureSize(rectWidth, rectHeight)) return NULL;

        purgeCache(rectWidth, rectHeight);

        SkBitmap bitmap;
        initBitmap(bitmap, rectWidth, rectHeight);

        SkPaint pathPaint(*paint);
        initPaint(pathPaint);

        SkCanvas canvas(bitmap);
        canvas.translate(-left + offset, -top + offset);
        canvas.drawRect(bounds, pathPaint);

        texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0);
        addTexture(entry, &bitmap, texture);
    }

    return texture;
+80 −34
Original line number Diff line number Diff line
@@ -336,6 +336,19 @@ public:

protected:
    PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
    PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
    void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);

    /**
     * Ensures there is enough space in the cache for a texture of the specified
     * dimensions.
     */
    void purgeCache(uint32_t width, uint32_t height);

    void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
    void initPaint(SkPaint& paint);

    bool checkTextureSize(uint32_t width, uint32_t height);

    PathTexture* get(Entry entry) {
        return mCache.get(entry);
@@ -491,21 +504,23 @@ void ShapeCache<Entry>::removeTexture(PathTexture* texture) {

void computePathBounds(const SkPath* path, const SkPaint* paint,
        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
void computeBounds(const SkRect& bounds, const SkPaint* paint,
        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);

template<class Entry>
PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
        const SkPaint* paint) {

    float left, top, offset;
    uint32_t width, height;
    computePathBounds(path, paint, left, top, offset, width, height);

    if (width > mMaxTextureSize || height > mMaxTextureSize) {
        ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
                mName, width, height, mMaxTextureSize, mMaxTextureSize);
        return NULL;
static PathTexture* createTexture(float left, float top, float offset,
        uint32_t width, uint32_t height, uint32_t id) {
    PathTexture* texture = new PathTexture;
    texture->left = left;
    texture->top = top;
    texture->offset = offset;
    texture->width = width;
    texture->height = height;
    texture->generation = id;
    return texture;
}

template<class Entry>
void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
    const uint32_t size = width * height;
    // Don't even try to cache a bitmap that's bigger than the cache
    if (size < mMaxSize) {
@@ -513,38 +528,71 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat
            mCache.removeOldest();
        }
    }
}

    PathTexture* texture = new PathTexture;
    texture->left = left;
    texture->top = top;
    texture->offset = offset;
    texture->width = width;
    texture->height = height;
    texture->generation = path->getGenerationID();

    SkBitmap bitmap;
template<class Entry>
void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
    bitmap.allocPixels();
    bitmap.eraseColor(0);
}

    SkPaint pathPaint(*paint);

template<class Entry>
void ShapeCache<Entry>::initPaint(SkPaint& paint) {
    // Make sure the paint is opaque, color, alpha, filter, etc.
    // will be applied later when compositing the alpha8 texture
    pathPaint.setColor(0xff000000);
    pathPaint.setAlpha(255);
    pathPaint.setColorFilter(NULL);
    pathPaint.setMaskFilter(NULL);
    pathPaint.setShader(NULL);
    paint.setColor(0xff000000);
    paint.setAlpha(255);
    paint.setColorFilter(NULL);
    paint.setMaskFilter(NULL);
    paint.setShader(NULL);
    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
    SkSafeUnref(pathPaint.setXfermode(mode));
    SkSafeUnref(paint.setXfermode(mode));
}

template<class Entry>
bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) {
    if (width > mMaxTextureSize || height > mMaxTextureSize) {
        ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
                mName, width, height, mMaxTextureSize, mMaxTextureSize);
        return false;
    }
    return true;
}

template<class Entry>
PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
        const SkPaint* paint) {

    float left, top, offset;
    uint32_t width, height;
    computePathBounds(path, paint, left, top, offset, width, height);

    if (!checkTextureSize(width, height)) return NULL;

    purgeCache(width, height);

    SkBitmap bitmap;
    initBitmap(bitmap, width, height);

    SkPaint pathPaint(*paint);
    initPaint(pathPaint);

    SkCanvas canvas(bitmap);
    canvas.translate(-left + offset, -top + offset);
    canvas.drawPath(*path, pathPaint);

    generateTexture(bitmap, texture);
    PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
    addTexture(entry, &bitmap, texture);

    return texture;
}

template<class Entry>
void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
    generateTexture(*bitmap, texture);

    uint32_t size = texture->width * texture->height;
    if (size < mMaxSize) {
        mSize += size;
        SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
@@ -556,8 +604,6 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat
    } else {
        texture->cleanup = true;
    }

    return texture;
}

template<class Entry>