PNG outputs alpha channel but no RGB and JPG outputs RGB but no alpha

0

I want to split a PNG file into 4 more PNGs, 3 for RGB and one alpha. When I input a PNG, the alpha channel is output correctly, but the RGB channels output nothing. If I input a JPG the RGB channels output correctly, but alpha fails because there is no alpha channel to get.

I've tried using multiple images all of which were either PNG or JPG and they all fail in the same way I've described.

val original = ImageIO.read(File("images/input.png"))

val alpha = BufferedImage(original.width, original.height, original.type)
val red = BufferedImage(original.width, original.height, original.type)
val green = BufferedImage(original.width, original.height, original.type)
val blue = BufferedImage(original.width, original.height, original.type)

for (y in 0 until original.height) {
    for (x in 0 until original.width) {
        val color = original.getRGB(x,y)

        val a = color  and 0xff000000.toInt()
        val r = color  and 0x00ff0000
        val g = color  and 0x0000ff00
        val b = color  and 0x000000ff

        alpha.setRGB(x,y,a)
        red.setRGB(x,y,r)
        green.setRGB(x,y,g)
        blue.setRGB(x,y,b)

    }
}

ImageIO.write(alpha,"png", File("images/alpha.png"))
ImageIO.write(red,"png", File("images/red.png"))
ImageIO.write(green,"png", File("images/green.png"))
ImageIO.write(blue,"png", File("images/blue.png"))

I expect to receive 4 outputs with their respective channels alone, but I receive only an alpha channel with a PNG and no alpha channel with a JPG.

image-processing
kotlin
png
jpeg
rgba
asked on Stack Overflow Apr 15, 2019 by Aut0

3 Answers

1

setRGB uses TYPE_INT_ARGB as color model. As your bit masks are setting the alpha channel to 0 the images appear to be empty. Set alpha to ff for the images to be shown.

val a = color and 0xff000000.toInt()
val r = (color and 0x00ff0000) or 0xff000000.toInt()
val g = (color and 0x0000ff00) or 0xff000000.toInt()
val b = (color and 0x000000ff) or 0xff000000.toInt() 
answered on Stack Overflow Apr 16, 2019 by Alexander Egger
0

The JPEG File Format does not hold any alpha-channels - what you could do to recreate some sort of transparency with JPEGs is to add another layer consisting of a monochromatic image and then filter the image through that but that is a really special use case and most likely nothing you want to do in this example.

answered on Stack Overflow Apr 16, 2019 by andakawa
0

The reason your code don't work as intended, is partly because you create new BufferedImages based on the original type:

BufferedImage(original.width, original.height, original.type)

The original type will vary based on the input image. For JPEG, it will typically be TYPE_3BYTE_BGR (which has no alpha). For PNG it depends on the type or PNG (grayscale, palette, true color, with or without alpha, etc). Your input PNG seems to be true color with alpha, probably resulting in TYPE_4BYTE_ABGR or TYPE_INT_ARGB.

Instead, you can either create 4 grayscale images (just "levels") using TYPE_BYTE_GRAY, or as you do now, 4 ARGB ones using TYPE_INT_ARGB (sorry if my Kotlin syntax is off, I mostly program Java these days):

BufferedImage(original.width, original.height, BufferedImage.TYPE_INT_ARGB)

Next, as @AlexanderEgger already pointed out, the set/getRGB methods operates in packed ARGB format, so you need to make sure the colors are opaque (anding with just the color mask, will result in fully transparent color):

val a = color and 0xff000000.toInt()
val r = (color and 0x00ff0000) or 0xff000000.toInt()
val g = (color and 0x0000ff00) or 0xff000000.toInt()
val b = (color and 0x000000ff) or 0xff000000.toInt()  

Or, if you like the levels approach, you do as @MarkSetchell suggests, and create all gray images (again, Kotlin syntax may be a little off):

BufferedImage(original.width, original.height, BufferedImage.TYPE_BYTE_GRAY)

...

val a = (color >> 24) and 0xff
val r = (color >> 16) and 0xff
val g = (color >>  8) and 0xff
val b =  color        and 0xff

val aa = 0xff000000.toInt() or (a << 16) or (a << 8) or a
val rr = 0xff000000.toInt() or (r << 16) or (r << 8) or r
val gg = 0xff000000.toInt() or (g << 16) or (g << 8) or g
val bb = 0xff000000.toInt() or (b << 16) or (b << 8) or b

(and pass aa, rr, gg and bb to setRGB).

answered on Stack Overflow Apr 16, 2019 by haraldK

User contributions licensed under CC BY-SA 3.0