[cairo] CAIRO_FORMAT_ARGB32 format [ was: Re: "data" in cairo_image_surface_get_data ()
Baz
brian.ewins at gmail.com
Fri Nov 30 03:46:30 PST 2007
On Nov 30, 2007 3:58 AM, Nguyen Vu Hung <vuhung16plus at gmail.com> wrote:
> 2007/11/29, Baz <brian.ewins at gmail.com>:
> > Just to spell things out - this is what the previous post implied:
> > for (i = 0; i < h; i++) {
> > ppixel = data + i * wpl;
> > rowptr = (U32 *)(data_cairo + i * w * 4);
> > for ( j = 0; j < w ; j++) {
> > rowptr[j] = ppixel[j] >> 8;
> > }
> > }
> >
> Did you missed something?
> data_cairo, rowptr are pointers of U8*, but you've casted it to (U32 *)?
I am casting it deliberately, so that I am writing the data a word at
a time. Obviously you need to change the declaration of rowptr for
that to compile.
>
> rowptr = (U32 *)(data_cairo + i * w * 4);
>
> Or did you mean I have to use
>
> U32* rowptr instead of U8* rowptr?
Yes.
>
> rowptr[j] = ppixel[j] >> 8; shifts ppixel 8 bits to the right? But
> can I make sure that the the last 8 bits ( alpha chanel ) are OK?
I explained this all a couple of mails back. The PIX format is RGBX,
in MSB->LSB order. Cairo's RGB24 is XRGB, in MSB->LSB order. Hence if
you have a U32 containing a PIX pixel, (ie ppixel[j]) containing, say,
0xaabbcc00, then (ppixel[j] >> 8) is 0x00aabbcc - which is the correct
value for the cairo pixel.
The alpha bits are *unused* in both PIX and RGB24. You don't care what's in it.
>
> When I did so ( declare U32 *rowptr; and loop ), the got an output PNG
> file that has all of its pixels are black :).
If the code you posted below is close to what you used, there are
multiple bugs in it that could have caused this problem.
>
> Is that has something doing with CAIRO_FORMAT_RGB24 in
> cairo_image_surface_create_for_data ?
>
No.
>
> > ... if you go back and reread what I said in the previous mail you
> > should see that this loop can be replaced with two memcpy()s, one for
> > the big-endian case and one for the little-endian case.
> >
> It is somewhat tricky and for me, I found it is hard to understand. I
> also want to improve the readability of my code so that the next
> maintainer will read it easy.
>
> Here is my code:
>
> U32 wpl = pixGetWpl(pixCut);
> U32 w = pixGetWidth(pixCut);
> U32 h = pixGetHeight(pixCut);
>
> cairo_surface_t* tmp_surface;
> U32* data_cairo;
^ this is unnecessary. I'd leave it unsigned char *. It also makes
your pointer arithmetic incorrect.
>
> int stride = wpl * 4;
>
> data_cairo = (unsigned char *) malloc(w * 4 * h );
>
> unsigned char* rowptr;
>
> for (i = 0; i < h; i++) {
> ppixel = data + i * wpl;
> rowptr = (U8 *) (data_cairo + i * w * 4);
^U8 is wrong here, because of what you do next. Also, because you
declared data_cairo as U32, the number you're adding to it is a number
of words, not bytes. So you meant (data_cairo + i * w) - think about
what's going on on that previous line with ppixel. If you used this
code with data_cairo being a U32, you were writing the image data in
the wrong place.
That first article on pointer arithmetic maybe didn't make it clear
enough. This:
ptr + 4
is exactly the same as this:
&(ptr[4])
... no matter what type of pointer 'ptr' is.
> for ( j = 0; j < w; j++) {
> rowptr[j] = ppixel[j] >> 8;
^ this will write the wrong data, repeatedly. Suppose rowptr is
looking at these bytes:
xrgbxrgbxrgbxrgb
and the PIX pixels all contain 'RGBX'. Then that line repeatedly tries
to assign XRGB to a U8, which is too small to contain it. IIRC the
value stored in the byte will be just 'B'. So after one iteration the
the cairo row will look like:
Brgbxrgbxrgbxrgb
The second time around, rowptr[1] doesn't point to the second cairo
pixel, but the second /byte/ because you used U8. So now the row will
look like:
BBgbxrgbxrgbxrgb
and after assigning the values from all 4 pixels in this row:
BBBBxrgbxrgbxrgb
> }
> }
>
> tmp_surface = cairo_image_surface_create_for_data (data_cairo,
> CAIRO_FORMAT_RGB24,
> w, h,
> stride);
>
Please try:
U32 wpl = pixGetWpl(pixCut);
U32 w = pixGetWidth(pixCut);
U32 h = pixGetHeight(pixCut);
// as I said in the last mail: since you're not buffer sharing, don't
bother with create_for_data
cairo_surface_t* tmp_surface = cairo_image_surface_create
(CAIRO_FORMAT_RGB24, w, h);
// data_cairo is a pointer to bytes.
unsigned char* data_cairo = cairo_image_surface_get_data (tmp_surface);
// stride is the width of a row of the cairo surface, in bytes.
int stride = cairo_image_surface_get_stride (tmp_surface);
// U32 is the size of an RGB24 pixel.
U32* rowptr;
for (i = 0; i < h; i++) {
ppixel = data + i * wpl;
// in the previous mail I was trying to say that you should use stride when
// dealing with the size of a row of the cairo surface. Note that data_cairo
// is in bytes and that stride is a number of bytes.
rowptr = (U32 *) (data_cairo + i * stride);
for ( j = 0; j < w; j++) {
// rowptr is a pointer to a U32, so its big enough to hold the value being
// assigned here. it also means that rowptr[j] refers to the j'th pixel of
// the row; if rowptr was U8 it would just refer to the j'th byte.
rowptr[j] = ppixel[j] >> 8;
}
}
>
> --
>
> Best Regards,
> Nguyen Hung Vu
> vuhung16plus{remove}@gmail.dot.com
> An inquisitive look at Harajuku
> http://www.flickr.com/photos/vuhung/sets/72157600109218238/
>
More information about the cairo
mailing list