Home > Net >  Incorrect colors when decoding JPEG using image.Decode and writing to PDF?
Incorrect colors when decoding JPEG using image.Decode and writing to PDF?

Time:01-11

I'm trying to create a PDF by decoding image files (in PNG, JPG, GIF, and BMP format) using the image.Decode() method to get the image.Image. Then, I write the pixel data into a PDF stream, which is later compressed. The problem I'm encountering is that when I decode a JPEG, the colors are incorrect in the resulting PDF. All other image formats are working as expected. I've attached a screenshot of the issue.

Screenshot: https://i.imgur.com/Bzz6EnD.png

Does anyone know what could be causing this problem? Is there a specific way that JPEGs need to be handled differently when using image.Decode()? Any suggestions on how to fix this issue would be greatly appreciated!

Edit:

Code:

var iData image.Image
iFile, err := os.Open(path)
if err != nil {
  [...]
} else {
  iData, _, err = image.Decode(iFile)
}
[...]
x.Dictionary.Set("ColorSpace", "/DeviceRGB")
x.Dictionary.Set("BitsPerComponent", 8)
for j := 0; j < iData.Bounds().Dy()/pixelMul; j   {
    for k := 0; k < iData.Bounds().Dx()/pixelMul; k   {
        r, g, b, _ := iData.At(k*pixelMul, j*pixelMul).RGBA()
        x.Write([]byte{byte(r), byte(g), byte(b)})
    }
}
[...]

The resulting image in the pdf looks the same when using the jpeg.Decode directly.

I exptect the image in the resulting pdf to look just like the original png with possibly a bit of degredation.

Original PNG: https://i.imgur.com/cjjOdxj.png

Converted JPG: https://i.imgur.com/I5kxTab.jpeg

Other JPEGs also have the same issue, e.g. the first test JPEG from w3c https://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/A_2_1-BF-01.htm

CodePudding user response:

Color.RGBA() returns the alpha-premultiplied color components in the range of 0..0xffff.

Converting such a value to byte like byte(r) will keep its lowest 8 bits which will seemingly be just random compared to the original value. You need an 8-bit color component, do not convert it to byte but use the higher 8 bits, which means shift right by 8 (or divide by 256):

x.Write([]byte{byte(r>>8), byte(g>>8), byte(b>>8)})

Explanation why it still worked for PNG and GIF, but not for JPEG:

Decoding PNG and GIF images likely uses an image model that uses the color.RGBA color model, which stores components using 8-bit values. But its RGBA.RGBA() method converts these values to 16-bit values by duplicating the original 8-bit values:

func (c RGBA) RGBA() (r, g, b, a uint32) {
    r = uint32(c.R)
    r |= r << 8
    g = uint32(c.G)
    g |= g << 8
    b = uint32(c.B)
    b |= b << 8
    a = uint32(c.A)
    a |= a << 8
    return
}

Which means if you take the lower 8 bits, you get the same original value just as if you take the 8 higher bits. Decoding JPEG images will likely use the color.YCbCr color type which does not reproduce this "implementation behavior".

Do not depend on this. When you need an 8-bit component from a 16-bit component, always use the higher 8 bits.

  • Related