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

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

Make Bitmap.get/setPixel[s] color space aware

Bug: 32984164
Test: cts-tradefed run singleCommand cts-dev --module CtsGraphicsTestCases --test android.graphics.cts.BitmapColorSpaceTest
Change-Id: Iee8c4abb264f0d2a44bae59788fbf4dcb2d0fdf7
parent 5acc4768
Loading
Loading
Loading
Loading
+72 −15
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
#include "SkImageInfo.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkColorSpace.h"
#include "SkColorSpaceXform.h"
#include "SkHalf.h"
#include "SkMatrix44.h"
#include "SkPM4f.h"
@@ -29,6 +31,7 @@
#include "core_jni_helpers.h"

#include <jni.h>
#include <string.h>
#include <memory>
#include <string>

@@ -448,12 +451,33 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int

    // reset to to actual choice from caller
    dst = dstBitmap.getAddr(x, y);

    SkColorSpace* colorSpace = dstBitmap.colorSpace();
    if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
        // now copy/convert each scanline
        for (int y = 0; y < height; y++) {
            proc(dst, src, width, x, y);
            src += srcStride;
            dst = (char*)dst + dstBitmap.rowBytes();
        }
    } else {
        auto sRGB = SkColorSpace::MakeSRGB();
        auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);

        std::unique_ptr<SkColor[]> row(new SkColor[width]);

        // now copy/convert each scanline
        for (int y = 0; y < height; y++) {
            memcpy(row.get(), src, sizeof(SkColor) * width);
            xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(),
                    SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(), width,
                    SkAlphaType::kUnpremul_SkAlphaType);

            proc(dst, row.get(), width, x, y);
            src += srcStride;
            dst = (char*)dst + dstBitmap.rowBytes();
        }
    }

    dstBitmap.notifyPixelsChanged();

@@ -1179,12 +1203,7 @@ static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) {
    if (!bitmapHolder.valid()) return JNI_TRUE;

    SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
    return colorSpace == nullptr ||
           colorSpace == SkColorSpace::MakeSRGB().get() ||
           colorSpace == SkColorSpace::MakeRGB(
                  SkColorSpace::kSRGB_RenderTargetGamma,
                  SkColorSpace::kSRGB_Gamut,
                  SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get();
    return GraphicsJNI::isColorSpaceSRGB(colorSpace);
}

static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle,
@@ -1246,6 +1265,16 @@ static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,

    SkColor dst[1];
    proc(dst, src, 1, bitmap.getColorTable());

    SkColorSpace* colorSpace = bitmap.colorSpace();
    if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
        auto sRGB = SkColorSpace::MakeSRGB();
        auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
        xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0],
                SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0], 1,
                SkAlphaType::kUnpremul_SkAlphaType);
    }

    return static_cast<jint>(dst[0]);
}

@@ -1268,11 +1297,30 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
    SkColorTable* ctable = bitmap.getColorTable();
    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
    SkColor* d = (SkColor*)dst + offset;

    SkColorSpace* colorSpace = bitmap.colorSpace();
    if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
        while (--height >= 0) {
            proc(d, src, width, ctable);
            d += stride;
            src = (void*)((const char*)src + bitmap.rowBytes());
        }
    } else {
        auto sRGB = SkColorSpace::MakeSRGB();
        auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());

        while (--height >= 0) {
            proc(d, src, width, ctable);

            xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, d,
                    SkColorSpaceXform::kBGRA_8888_ColorFormat, d, width,
                    SkAlphaType::kUnpremul_SkAlphaType);

            d += stride;
            src = (void*)((const char*)src + bitmap.rowBytes());
        }
    }

    env->ReleaseIntArrayElements(pixelArray, dst, 0);
}

@@ -1293,6 +1341,15 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
        return;
    }

    SkColorSpace* colorSpace = bitmap.colorSpace();
    if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
        auto sRGB = SkColorSpace::MakeSRGB();
        auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
        xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color,
                SkColorSpaceXform::kBGRA_8888_ColorFormat, &color, 1,
                SkAlphaType::kUnpremul_SkAlphaType);
    }

    proc(bitmap.getAddr(x, y), &color, 1, x, y);
    bitmap.notifyPixelsChanged();
}
+9 −0
Original line number Diff line number Diff line
@@ -460,6 +460,15 @@ sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) {
    }
}

bool GraphicsJNI::isColorSpaceSRGB(SkColorSpace* colorSpace) {
    return colorSpace == nullptr
            || colorSpace == SkColorSpace::MakeSRGB().get()
            || colorSpace == SkColorSpace::MakeRGB(
                  SkColorSpace::kSRGB_RenderTargetGamma,
                  SkColorSpace::kSRGB_Gamut,
                  SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get();
}

///////////////////////////////////////////////////////////////////////////////
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
    mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
+1 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ public:
    static sk_sp<SkColorSpace> defaultColorSpace();
    static sk_sp<SkColorSpace> linearColorSpace();
    static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
    static bool isColorSpaceSRGB(SkColorSpace* colorSpace);
};

class HeapAllocator : public SkBRDAllocator {
+10 −6
Original line number Diff line number Diff line
@@ -522,7 +522,7 @@ public final class Bitmap implements Parcelable {
     * <p>The content of the bitmap is copied into the buffer as-is. This means
     * that if this bitmap stores its pixels pre-multiplied
     * (see {@link #isPremultiplied()}, the values in the buffer will also be
     * pre-multiplied.</p>
     * pre-multiplied. The pixels remain in the color space of the bitmap.</p>
     * <p>After this method returns, the current position of the buffer is
     * updated: the position is incremented by the number of elements written
     * in the buffer.</p>
@@ -562,7 +562,8 @@ public final class Bitmap implements Parcelable {
     * <p>Copy the pixels from the buffer, beginning at the current position,
     * overwriting the bitmap's pixels. The data in the buffer is not changed
     * in any way (unlike setPixels(), which converts from unpremultipled 32bit
     * to whatever the bitmap's native format is.</p>
     * to whatever the bitmap's native format is. The pixels in the source
     * buffer are assumed to be in the bitmap's color space.</p>
     * <p>After this method returns, the current position of the buffer is
     * updated: the position is incremented by the number of elements read from
     * the buffer. If you need to read the bitmap from the buffer again you must
@@ -1495,7 +1496,8 @@ public final class Bitmap implements Parcelable {
    /**
     * Returns the {@link Color} at the specified location. Throws an exception
     * if x or y are out of bounds (negative or >= to the width or height
     * respectively). The returned color is a non-premultiplied ARGB value.
     * respectively). The returned color is a non-premultiplied ARGB value in
     * the {@link ColorSpace.Named#SRGB sRGB} color space.
     *
     * @param x    The x coordinate (0...width-1) of the pixel to return
     * @param y    The y coordinate (0...height-1) of the pixel to return
@@ -1517,7 +1519,8 @@ public final class Bitmap implements Parcelable {
     * a packed int representing a {@link Color}. The stride parameter allows
     * the caller to allow for gaps in the returned pixels array between
     * rows. For normal packed results, just pass width for the stride value.
     * The returned colors are non-premultiplied ARGB values.
     * The returned colors are non-premultiplied ARGB values in the
     * {@link ColorSpace.Named#SRGB sRGB} color space.
     *
     * @param pixels   The array to receive the bitmap's colors
     * @param offset   The first index to write into pixels[]
@@ -1610,7 +1613,8 @@ public final class Bitmap implements Parcelable {
    /**
     * <p>Write the specified {@link Color} into the bitmap (assuming it is
     * mutable) at the x,y coordinate. The color must be a
     * non-premultiplied ARGB value.</p>
     * non-premultiplied ARGB value in the {@link ColorSpace.Named#SRGB sRGB}
     * color space.</p>
     *
     * @param x     The x coordinate of the pixel to replace (0...width-1)
     * @param y     The y coordinate of the pixel to replace (0...height-1)
@@ -1632,7 +1636,7 @@ public final class Bitmap implements Parcelable {
    /**
     * <p>Replace pixels in the bitmap with the colors in the array. Each element
     * in the array is a packed int representing a non-premultiplied ARGB
     * {@link Color}.</p>
     * {@link Color} in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
     *
     * @param pixels   The colors to write to the bitmap
     * @param offset   The index of the first color to read from pixels[]