Discussion:
[cairo] 32k limit with image surface?
Ian Britten
2009-07-27 16:16:48 UTC
Permalink
Hi all,
Is there a 32k limit to the number of pixels (in a scanline) that
an image surface may contain (ie: Its width)?

I'm pursuing a problem where my rendered image doesn't seem to
contain any data after pixel 32767, and I'm wondering if it's due
to something within the Cairo stack?

I'm currently on 64-bit Mandriva 2008.1, with:
Cairomm = 1.8.0
Cairo = 1.8.8
Pixman = 0.12.0

Many thanks for any info!
Ian
Ian Britten
2009-07-29 16:41:04 UTC
Permalink
Post by Ian Britten
Is there a 32k limit to the number of pixels (in a scanline) that
an image surface may contain (ie: Its width)?
I'm pursuing a problem where my rendered image doesn't seem to
contain any data after pixel 32767, and I'm wondering if it's due
to something within the Cairo stack?
I'm still looking into this, and have encountered a weird result
that may be related to my problem. I'm hoping someone can shed
some light on this, so I can get to the root of my problem.

In the attached example, I simply create a (large) image surface,
paint it red, draw one 2-point diagonal black line, and save it
to a PNG. I'm using the default transformations.

- If the X point of the line is less than 32767, the line draws as
expected.
- If the X point of the line is 32768 (or greater), I get an image
that is red in the upper-right, but the entire lower-left
diagonal half is black. The image no longer looks like a black
line on a red background, but looks like a black+red pair of
triangles (When viewed in Gimp).
- Strangely, a horizontal line seems to work ok, and draws fine
irregardless of how big of an X value I use.

Can anyone explain what I'm seeing? It is me, or Cairo?
Many thanks for any info!
Ian
-------------- next part --------------
A non-text attachment was scrubbed...
Name: main.c
Type: text/x-csrc
Size: 1003 bytes
Desc: not available
Url : http://lists.cairographics.org/archives/cairo/attachments/20090729/43f55467/attachment.c
James Cloos
2009-07-29 21:50:02 UTC
Permalink
I see the same thing here from your test app.

I can confirm that the bug is not in _cairo_fixed_from_double().

But I didn't see any likely 16 bit choke point stepping through it.

OTOH, if I convert the test to use a pdf surface and use gs to render
that pdf to a png, it works correctly at sizes beyond 32.

So the bug is surface-specific, affecting at least the image surface.

-JimC
--
James Cloos <cloos at jhcloos.com> OpenPGP: 1024D/ED7DAEA6
Chris Wilson
2009-07-29 22:13:36 UTC
Permalink
Post by James Cloos
I see the same thing here from your test app.
Help me... Can't resist.... WORKSFORME?.

So all you have to do then is run my experimental tessellator branch,
that also includes stroke-using-spans.

The issue you have run across here is the 16-bit (16.16 fixed point)
coordinate limitation with using pixman to rasterise trapezoids. Hence
why by performing the rastiseration ourselves using the span polygon
rasteriser we avoid this particular bug, but only on this occasion.
16-bit coordinates are also a fundamental limitation of the X protocol.
Post by James Cloos
I can confirm that the bug is not in _cairo_fixed_from_double().
But I didn't see any likely 16 bit choke point stepping through it.
OTOH, if I convert the test to use a pdf surface and use gs to render
that pdf to a png, it works correctly at sizes beyond 32.
So the bug is surface-specific, affecting at least the image surface.
Cairo uses 24.8 fixed point coordinates internally (having changed over
from 16.16 for the 1.6 release), so will itself suffer range errors with
surfaces greater than ~8 million device units.

The suggested workaround for this, similar to handling the limited
texture sizes in OpenGL, is for the application to do its own tiling in
a layer above a cairo surface. Not perfect, but it may actually improve
your application's performance... (Positive spin).

Hope this helps,
-ickle
Ian Britten
2009-07-30 12:08:56 UTC
Permalink
Post by Chris Wilson
Help me... Can't resist.... WORKSFORME?.
So all you have to do then is run my experimental tessellator branch,
that also includes stroke-using-spans.
:)
I realize I'm not running the latest/greatest HEAD, but do I gather
that a future version (1.10?) will behave better then? If so, I'm
looking forward to the next major/official release then! :)
Post by Chris Wilson
The issue you have run across here is the 16-bit (16.16 fixed point)
coordinate limitation with using pixman to rasterise trapezoids. Hence
why by performing the rastiseration ourselves using the span polygon
rasteriser we avoid this particular bug, but only on this occasion.
16-bit coordinates are also a fundamental limitation of the X protocol.
Sorry for the naive questions, but I'm not a cairo developer, and not
familiar with the implementation details:
- Does pixman use X for it's work? I thought the image surface was
device/OS independent? I'm familiar with the X limitation, but I'm
not using X directly myself anywhere. Or were you just pointing out
the prevalence of this 32k limitation?
- I'm semi-certain I've also seen this behaviour when rendering image
data to an image surface, and not just vector data - Does that change
anything? (I'm not sure when trapezoids are rasterized ... :P )
Post by Chris Wilson
Post by James Cloos
So the bug is surface-specific, affecting at least the image surface.
Cairo uses 24.8 fixed point coordinates internally (having changed over
from 16.16 for the 1.6 release), so will itself suffer range errors with
surfaces greater than ~8 million device units.
So... Are you saying (in HEAD) there is NOT a 32k limit, and that I
should be able to make an image surface up to ~8 million pixels wide?
(Not that I would go that big...)

And on a separate note - If there is a limit present, should that not
maybe be documented somewhere (eg: cairo_image_surface_create())?
And, shouldn't an error be being raised if it's exceeded (eg:
CAIRO_STATUS_SIZE_ERROR)?
Post by Chris Wilson
The suggested workaround for this, similar to handling the limited
texture sizes in OpenGL, is for the application to do its own tiling in
a layer above a cairo surface. Not perfect, but it may actually improve
your application's performance... (Positive spin).
Believe it or not, this is sortof what I'm working on! :)

To simplify the final processing, I'm trying to tile using just one
tile at the desired width, and altering the height to keep the memory
use down. Thus, I'm trying to create image surfaces in the range of
50000 x 1572 (approx 300M), but encountered this issue.

[ This approach makes it easier to call things like TIFFWriteScanline(),
passing the entire scanline at once, rather than trying to join tiles
together, or producing a tiled TIFF, which isn't wanted for this
project. ]


Many thanks for the information, and for any clarification about
improvements in upcoming releases!
Ian
M Joonas Pihlaja
2009-07-30 13:29:06 UTC
Permalink
Hi Ian,
Post by Ian Britten
To simplify the final processing, I'm trying to tile using just one
tile at the desired width, and altering the height to keep the memory
use down. Thus, I'm trying to create image surfaces in the range of
50000 x 1572 (approx 300M), but encountered this issue.
[ This approach makes it easier to call things like TIFFWriteScanline(),
passing the entire scanline at once, rather than trying to join tiles
together, or producing a tiled TIFF, which isn't wanted for this
project. ]
Large scanline oriented TIFFs are a pain to work with in my
experience. Using tiled TIFFs with TIFFWriteTile() is probably easier
than using a pseudo-tiling system on top of TIFFWriteScanline(). Of
course your final processing determines whether you can do that at
all, but there's my 2c anyway. :)

Joonas
Bill Spitzak
2009-07-30 18:00:20 UTC
Permalink
I beg to differ. Tiled TIFF files are a pita to deal with when reading
them in. They pretty much require the entire image to be read into
memory before any can be accessed.

Yes in theory they could be random-accessed but that only works if the
units the program uses is an integer division or multiple of whatever
tile size the image is using. Needless to say we don't even bother
checking if this is true as it is false 99.9999% of the time.

Scanline TIFF files do allow random access because we know one dimension
of the unit is as big as we will ever require, and the other dimension
is 1, which all possible sizes are a multiple of.

This is based on years of experience doing image processing for Nuke.

Also use PNG as libpng is about a dozen times faster and easier to use
than libtiff.
Post by M Joonas Pihlaja
Hi Ian,
Post by Ian Britten
To simplify the final processing, I'm trying to tile using just one
tile at the desired width, and altering the height to keep the memory
use down. Thus, I'm trying to create image surfaces in the range of
50000 x 1572 (approx 300M), but encountered this issue.
[ This approach makes it easier to call things like TIFFWriteScanline(),
passing the entire scanline at once, rather than trying to join tiles
together, or producing a tiled TIFF, which isn't wanted for this
project. ]
Large scanline oriented TIFFs are a pain to work with in my
experience. Using tiled TIFFs with TIFFWriteTile() is probably easier
than using a pseudo-tiling system on top of TIFFWriteScanline(). Of
course your final processing determines whether you can do that at
all, but there's my 2c anyway. :)
Joonas
_______________________________________________
cairo mailing list
cairo at cairographics.org
http://lists.cairographics.org/mailman/listinfo/cairo
Ian Britten
2009-07-30 18:47:15 UTC
Permalink
Post by Bill Spitzak
I beg to differ. Tiled TIFF files are a pita to deal with when reading
them in. They pretty much require the entire image to be read into
memory before any can be accessed.
I believe this is our experience/impression too, and why we want to
avoid them (Unfortunately, our main TIFF guru is away this week...)

[ snip ]
Post by Bill Spitzak
Also use PNG as libpng is about a dozen times faster and easier to use
than libtiff.
Unfortunately, this isn't an option for us. We're actually using
GeoTIFF - A georeferenced TIFF file widely used in GIS - in this case.
Even if there is some sort of georeferenced PNG, many customers
workflows/data are already based on GeoTIFF (And other georeferenced
image formats), so we have to produce/support them.

Ian
M Joonas Pihlaja
2009-07-30 21:59:57 UTC
Permalink
I beg to differ. Tiled TIFF files are a pita to deal with when reading them
in. They pretty much require the entire image to be read into memory before
any can be accessed.
Yes in theory they could be random-accessed but that only works if the units
the program uses is an integer division or multiple of whatever tile size the
image is using. Needless to say we don't even bother checking if this is true
as it is false 99.9999% of the time.
Sounds like your TIFF reader code could do with some love. Nowhere in
the TIFF spec does it say that image dimensions must be multiples of
the tile dimensions. The rightmost and bottommost tiles are zero
padded to account for the discrepancy to image width/height. In any
case, random access to tiles works just fine since TIFF images contain
a tile index just for that purpose.

Cheers,

Joonas
Bill Spitzak
2009-07-30 23:38:04 UTC
Permalink
Post by M Joonas Pihlaja
I beg to differ. Tiled TIFF files are a pita to deal with when reading them
in. They pretty much require the entire image to be read into memory before
any can be accessed.
Yes in theory they could be random-accessed but that only works if the units
the program uses is an integer division or multiple of whatever tile size the
image is using. Needless to say we don't even bother checking if this is true
as it is false 99.9999% of the time.
Sounds like your TIFF reader code could do with some love. Nowhere in
the TIFF spec does it say that image dimensions must be multiples of
the tile dimensions. The rightmost and bottommost tiles are zero
padded to account for the discrepancy to image width/height. In any
case, random access to tiles works just fine since TIFF images contain
a tile index just for that purpose.
No I meant that the program reading the tiff may use it's own tiles
internally and it is unlikely to be able to adjust their sizes to match
the tiles used in the program. It is prepared to handle tiles that go
off the edge of the image.
Carl Worth
2009-07-31 23:48:18 UTC
Permalink
Post by Ian Britten
Post by Chris Wilson
Help me... Can't resist.... WORKSFORME?.
So all you have to do then is run my experimental tessellator branch,
that also includes stroke-using-spans.
:)
I realize I'm not running the latest/greatest HEAD, but do I gather
that a future version (1.10?) will behave better then? If so, I'm
looking forward to the next major/official release then! :)
Yes, at some point in the future Chris will get his experimental stuff
cooked up and rolled into a final release. I don't know if that will
happen before 1.10, but we can certainly hope so!
Post by Ian Britten
Post by Chris Wilson
The issue you have run across here is the 16-bit (16.16 fixed point)
coordinate limitation with using pixman to rasterise trapezoids. Hence
why by performing the rastiseration ourselves using the span polygon
rasteriser we avoid this particular bug, but only on this occasion.
16-bit coordinates are also a fundamental limitation of the X protocol.
Sorry for the naive questions, but I'm not a cairo developer, and not
- Does pixman use X for it's work? I thought the image surface was
device/OS independent? I'm familiar with the X limitation, but I'm
not using X directly myself anywhere. Or were you just pointing out
the prevalence of this 32k limitation?
No, pixman doesn't use X.

But, the code that is now pixman began its life inside the X server,
(where there's a hard-and-fast, long-existing
no-image-shall-be-larger-than-16-bits-per-coordinate limit).

So this limit does affect the image backend, (which uses pixman), while
of course also affecting the xlib backend, (which uses the X server,
which also, in turn, uses pixman).

In Chris's experimental branch, he's no longer using cairo's new span
rasterizer rather than pixman's trapezoid rasterizer, and thus avoiding
the limit.

Of course, that's a side effect of other work he's doing, and not the
original intent. So it may not be that all such limits end up being
worked around even with his work.
Post by Ian Britten
- I'm semi-certain I've also seen this behaviour when rendering image
data to an image surface, and not just vector data - Does that change
anything? (I'm not sure when trapezoids are rasterized ... :P )
Image data to an image surface *might* invoke trapezoids. An easy way
for that to happen would be if you established some geometry for a clip.
But even without that, cairo might go through an optimization where it
notices that you're doing some rectangular paint() operation and it's
possible that it represents that rectangle with a trapezoid at some
point.

So it's pretty easy to bump into the limit.
Post by Ian Britten
Post by Chris Wilson
Post by James Cloos
So the bug is surface-specific, affecting at least the image surface.
Cairo uses 24.8 fixed point coordinates internally (having changed over
from 16.16 for the 1.6 release), so will itself suffer range errors with
surfaces greater than ~8 million device units.
So... Are you saying (in HEAD) there is NOT a 32k limit, and that I
should be able to make an image surface up to ~8 million pixels wide?
(Not that I would go that big...)
Since cairo was built up around pixman originally[*], it originally used
a 16.16-bit fixed-point format for storing all coordinates internally.
For the cairo 1.6 release, we changed this to use 24.8-bit fixed-point
integers instead. This fixed many bugs.

But pixman still uses 16.16-bit fixed-point integers, so cairo has to
convert to that for some operations (such as trapezoid rasterization),
which can be buggy due to data loss as you've noticed. So to eliminate
more bugs we will want to either teach pixman to accept 24.8-bit
integers instead, or else code things up in such a way to avoid all
16.16-bit interfaces in pixman for the image backend.

[*] Well, if you want to be pedantic, cairo was originally built up
around the Xrender library which in turn talked to an X server which
included the code that would eventually get pulled out to become pixman.
So close enough.
Post by Ian Britten
And on a separate note - If there is a limit present, should that not
maybe be documented somewhere (eg: cairo_image_surface_create())?
CAIRO_STATUS_SIZE_ERROR)?
Yes, we probably should do both of these things. Care to help implement
that?

-Carl

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
Url : http://lists.cairographics.org/archives/cairo/attachments/20090731/0345ce6c/attachment.pgp
Soeren Sandmann
2009-08-03 19:54:26 UTC
Permalink
Post by Carl Worth
But pixman still uses 16.16-bit fixed-point integers, so cairo has to
convert to that for some operations (such as trapezoid rasterization),
which can be buggy due to data loss as you've noticed. So to eliminate
more bugs we will want to either teach pixman to accept 24.8-bit
integers instead, or else code things up in such a way to avoid all
16.16-bit interfaces in pixman for the image backend.
Apart from the trapezoid limitation, there is also one in that

pixman_image_composite()

limits its coordinates to 16 bit.

If it's useful, I'd be fine adding a pixman_image_composite_32() for
0.16.0. There are a few 16 bit limitations left internally here and
there, but I think all of them are trivial to fix. Clearly,
pixman_image_composite() could just call the 32 bit version.

The trapezoid limitation is trickier because the X server has 16.16
set in stone in its API. There are various things that could be done:

(1) Add a copy of the trapezoid implementation that uses 24.8
coordinates; then use this path from the image backend.

I am not thrilled about this one because it would either add a
bunch of new code or more complex build-time code
generation. And trapezoids are falling out of favor.

(2) Make the current trap implementation use 24.8 internally, and
add new 24.8 API for trapezoid rasterization.

This would make X trapezoids happen with eight bits of subpixel
precision instead of sixteen, but I don't think anyone would
even notice.

(3) Add a polygon image, then use that from the image backend.

This is a good chunk of work, but it would pay off not only in
higher range, but also in better performance because polygon
rasterization is more efficient than tesselate-then-trap, and
because geometry rendering could then ahppen in one pass. By
adding a polygon picture type to XRender, this would also allow
better performance for the X backend.

Of those, I'd prefer (3), but if someone wants to do (2), I would
probably take the patch.



Soren
Ian Britten
2009-08-04 17:49:29 UTC
Permalink
Carl Worth wrote:

[ snip ]
Post by Carl Worth
No, pixman doesn't use X.
[ snip ]
Post by Carl Worth
But pixman still uses 16.16-bit fixed-point integers, so cairo has to
convert to that for some operations (such as trapezoid rasterization),
which can be buggy due to data loss as you've noticed. So to eliminate
more bugs we will want to either teach pixman to accept 24.8-bit
integers instead, or else code things up in such a way to avoid all
16.16-bit interfaces in pixman for the image backend.
Carl,
Many thanks for all the background info, and clarification of the
current state of things!
Post by Carl Worth
Post by Ian Britten
And on a separate note - If there is a limit present, should that not
maybe be documented somewhere (eg: cairo_image_surface_create())?
CAIRO_STATUS_SIZE_ERROR)?
Yes, we probably should do both of these things. Care to help implement
that?
Well, given that I'm not familiar with Cairo, its internals, Git, the
latest Cairo development, plus my limited time, etc, I think my time
might be better spent with testing/feedback/etc (Such as things like
this). I could enter a bug, if you'd like that?
Meanwhile, I can offer some words for the docs (Probably
cairo_image_surface_create() and cairo_image_surface_create_for_data()),
but it's really not much help, in the big scheme of things... :P
eg:
"Note that due to underlying limitations within Cairo, the maximum size
that an image surface can successfully be made is 32767 x 32767.
Attempting to create a surface larger than this will trigger an error,
since the results would be incorrect otherwise."

Thanks,
Ian

Loading...