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.