Search code examples
androidimageraster

Can I generate 1:1 pixel data within an Android application?


I wish to check the Android display is showing the correct tone curve. If I generate a patch with alternate black and white lines surrounded by a 50% grey, and the two grey levels match then the first stop of the tone curve is right. I can then generate a patch with alternate black and 50% grey lines surrounded by a 25% grey, and so on.

I can find the current display size. I can make RGB raster data that fits. If needs be I can make the image with 2x2 pixel replication for things like retina displays. But I cannot see how to get the raster data to the display without risking resizing.

The particular image I have described might be generated using a matte texture, or some other trick. I have other vision test images that more complicated, and I currently generate as raster data in another program. So, I am really looking for something that can take a rectangle of custom RGB data and stick it onto the screen.

Maybe the tool is there in Android Studio, staring me in the face. But I can't see it.

(the following day)

I have found the Bitmap class. This is probably what I wanted.

(several days later)

No. I am still not there. I can generate an ImageView. I would like to generate a region of bitmap data with alternate black and white lines.

My experiments are in Kotlin.

I have found the problems in getting the dimensions of an ImageView in pixels. The layout effectively works the other way: you say define your layout, and the library works out the dimensions and the resize parameters for you. resize and the size cannot usually be calculated until the view has been laid out. See for instance...

Why does setting view dimensions asynchronously not work in Kotlin?

There is a Java solution that uses viewTreeObserver(). I can use this to get the dimensions in pixels in Toast, but I can't get the value out of the observer and into the context.

The exact pixel size is not the issue here. I could make the ImageView 50% of the display height and width, and calculate the number of pixels as a fraction of the screen dimensions. This would be accurate enough for the general layout of the grey border and the block of stripes. I could use the to lay out the canvas, and then force the view to fit that at 1:1 scale.

It feels that these tools are not supposed to work this way. Is there some completely different way of doing this that I am missing?

Postscript:

There was a way to do this...

   override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityToneBinding.inflate(layoutInflater)
    setContentView(binding.root)

    stripeColour = lastColour
    step = resources.displayMetrics.density

    // Initialize the ImageView
    stripeView = findViewById(R.id.stripeView)

    stripeView.doOnLayout {
        val myBitmap = Bitmap.createBitmap(
            it.measuredWidth,
            it.measuredHeight,
            Bitmap.Config.ARGB_8888
        )
        val myCanvas = Canvas(myBitmap)
        val xMax = it.measuredWidth.toFloat()
        val yMax = it.measuredHeight.toFloat()
        val myPaint = Paint()
        myPaint.isAntiAlias = false
        myPaint.color = Color.rgb(0, 0, 0)
        var y = 0.0f
        while (y < yMax) {
            myCanvas.drawRect(0.0f, y, xMax, y+step, myPaint)
            y += 2.0f*step
        }
        stripeView.setImageBitmap(myBitmap)
    }

The black stripes are opaque, and I could update the other stripes by changing the background. The stripes all had nice sharp edges, without the interpolation I had been getting at the edges. This is a rather specific solution for my particular problem, but it seemed to work fine.

If you want to try it yourself, be warned: I am now seeing something I do not understand. It seems the light and dark stripes are on average brighter then they should be, particularly with dark colours. The tone curve for the display seems to fit the sRGB standard well when I measure large patches, but the sum of the light and dark stripes isn't what it should be. So, this is not the test for the tone curve I was hoping for.


Solution

  • Once I found the terms 'Bitmap' and 'Canvas' I found the right sort of tools, but it was hard to see how to string them together to make something that works. Here is a simple exercise that helped me...

    https://developer.android.com/codelabs/advanced-android-kotlin-training-canvas#10

    Ignore the bit that sets SYSTEM_UI_FLAG_FULLSCREEN. This is no longer supported. There are other ways to do the same thing, but this example works perfectly well without it.