Bug 9274

Summary: strange/wrong behavoiur when using cairo.ImageSurface.create_for_data with alpha values != 255
Product: pycairo Reporter: Tobias Bonnke <tobias.bonnke>
Component: generalAssignee: Steve Chaplin <d74n5pohf9>
Status: RESOLVED NOTABUG QA Contact:
Severity: normal    
Priority: medium CC: tobias.bonnke
Version: unspecified   
Hardware: x86 (IA32)   
OS: Windows (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: The "correctly" created gradient image which looks like expected
The "incorrectly" looking gradient image, the "unexpected/wrong" result so to say

Description Tobias Bonnke 2006-12-07 03:03:36 UTC
When using cairo.ImageSurface.create_for_data in order to create an image in
cairo.FORMAT_ARGB32 using alpha-values != 255 the image contains wrong pixel data.

Please create those two generated images (python 2.5 code using pycairo 1.2.6)
in order to simplify the (follwing) problem-explanation:

# ---- code snippet start ----

imgW = 255
imgH = 255

pelData = array.array('c', '\0x0' * imgW * imgH * 4)

for y in range(imgH):
  for x in range(imgW):
    offset = 4 * ((y * imgW) + x)
    
    pelData[offset+0] = chr(0)   # B
    pelData[offset+1] = chr(0)   # G
    pelData[offset+2] = chr(x)   # R
    pelData[offset+3] = chr(255) # A, when is CONSTANT=255 image is okay

img = cairo.ImageSurface.create_for_data(pelData, cairo.FORMAT_ARGB32, imgW,
imgH, imgW * 4)

img.write_to_png("channel_test_alpha_const.png")



pelData = array.array('c', '\0x0' * imgW * imgH * 4)

for y in range(imgH):
  for x in range(imgW):
    offset = 4 * ((y * imgW) + x)
    
    pelData[offset+0] = chr(0) # B
    pelData[offset+1] = chr(0) # G
    pelData[offset+2] = chr(x) # R
    pelData[offset+3] = chr(y) # A, when varies image seems to be f*cked up

img = cairo.ImageSurface.create_for_data(pelData, cairo.FORMAT_ARGB32, imgW,
imgH, imgW * 4)

img.write_to_png("channel_test_alpha_var.png")

# ---- code snippet end ----



When executed, two images are created for illustrating the problem:

- "channel_test_alpha_const.png" contains a gradient from black (left image
border) to red (right image border), using 255 as constant alpha value for each
pixel

- ""channel_test_alpha_var.png"" SHOULD contain the same gradient as the one in
the previous image, BUT: the alpha-value for each pixel-row is identical to it's
y-index, means in top-most pixel row the gradient should be invisible due to
alpha=0 and in the bottom-most row the gradient should be full saturated due to
alpha=255

Due to some reason that's not true: the gradient-image using variable alpha
values instead of a constant one looks totally different. This seems to be
dependent to the alpha value: if alpha differs from 255 the image seems to have
a "misaligned" stride depending on the alpha value or something like that. There
are "gradient-like" edges in the image, always from top-left to some
"bottom-right" angle.

I hope the two test images illustrate the problem well, because it's difficult
to explain it here. And I need to mention that on the one hand I'm new to Python
(so it's possible I made some mistake creating the array), but on the other hand
the only difference in the code is the byte I set for the alpha-values in each
pixel's data, so I'm quite sure the problem must depend on the data I pass to
the create_for_data method.

Further (hopefully useful) information:

- Win XP
- Python 2.5
- pycairo 1.2.6
Comment 1 Tobias Bonnke 2006-12-07 03:28:10 UTC
Created attachment 7998 [details]
The "correctly" created gradient image which looks like expected
Comment 2 Tobias Bonnke 2006-12-07 03:31:29 UTC
Created attachment 7999 [details]
The "incorrectly" looking gradient image, the "unexpected/wrong" result so to say

This image should look like "channel_test_alpha_const.png", the only difference
should be that each scanline's alpha should increment from y=0, alpha=0 to
y=255, alpha=255. But instead the image is not a linear gradient at all.
Comment 3 Steve Chaplin 2006-12-07 06:26:46 UTC
This is a due to the format CAIRO_FORMAT_ARGB32 using pre-multiplied alpha.
That is, 50% transparent red is 0x80800000, not 0x80ff0000.

Something like this shound produce better results:

pelData = array.array('B', [0] * imgW * imgH * 4)

for y in range(imgH):
  for x in range(imgW):
    offset = (x + (y * imgW)) * 4
    alpha = y

    pelData[offset+0] = 0
    pelData[offset+1] = 0
    pelData[offset+2] = int((x * alpha)/255)
    pelData[offset+3] = alpha

img = cairo.ImageSurface.create_for_data(pelData, cairo.FORMAT_ARGB32, imgW,
                                         imgH, imgW * 4)
img.write_to_png("channel_test_alpha_var.png")


(But if its just gradients you want, cairo has its own functions to create
gradients)

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.