-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Does CairoRenderContext::make_image() work on big endian systems? #224
Comments
This does look like an oversight. I'm not sure what the TODO is about. Thanks for all the detail. :) |
It's not a simple endian conversion, there are four choices of byte order for RGBA pixels even on little-endian systems, and you do see all four in use: A in the high or low byte, and either R or B at the other end. It's possible the code or comments could be cleaned up, but I'm not seeing a real problem here. Regarding whether this works on big-endian systems, it's possible there's a problem, but it's a little tricky to investigate, we'd have to find out what order Cairo has, it's not a simple representation question. I've looked into testing when we've had endianness-specific code before (see projectfluent/fluent-langneg-rs#8 for an example of the latter), but it's not easy to get one's hands on a testing environment. |
Sure, no problem. Here is a git repo doing just that: https://github.com/psychon/cairo-endian-test Little endian output (first are the individual bytes for that pixel and then comes the pixel as
Big endian output:
As you can see, the let (r, g, b, a) = whatever_bytes_you_want_to_write_as_u8s();
let (r, g, b, a) = (u32::from(r), u32::from(g), u32::from(b), u32::from(a));
let pixel: u32 = a << 24 | r << 16 | g << 8 | b << 0;
data[dst_off + x * 4..dst_off + x*4 + 4].copy_from_slice(pixel.to_ne_bytes()); // Cairo's pixel are in native endianness, hence to_ne_bytes() |
@psychon do you have any interest in making a PR? |
Not really, sorry. I never even build piet on my computer and have not even checked how to build & test this. |
no worries, thanks for opening this! a fix is going to be pretty low priority, but i'm sure it will happen on a long enough time scale. 😉 |
Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: linebender#224 Signed-off-by: Uli Schlachter <psychon@znc.in>
The image format is based on individual bytes and thus independent of the system's endianness. This commit tries to make that more explicit. This was originally suggested in issue linebender#224. Signed-off-by: Uli Schlachter <psychon@znc.in>
Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: linebender#224 Signed-off-by: Uli Schlachter <psychon@znc.in>
The image format is based on individual bytes and thus independent of the system's endianness. This commit tries to make that more explicit. This was originally suggested in issue linebender#224. Signed-off-by: Uli Schlachter <psychon@znc.in>
Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: linebender#224 Signed-off-by: Uli Schlachter <psychon@znc.in>
Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: #224 Signed-off-by: Uli Schlachter <psychon@znc.in>
The image format is based on individual bytes and thus independent of the system's endianness. This commit tries to make that more explicit. This was originally suggested in issue #224. Signed-off-by: Uli Schlachter <psychon@znc.in>
Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: linebender#224 Signed-off-by: Uli Schlachter <psychon@znc.in>
The image format is based on individual bytes and thus independent of the system's endianness. This commit tries to make that more explicit. This was originally suggested in issue linebender#224. Signed-off-by: Uli Schlachter <psychon@znc.in>
* make image formats optional via features * make sure image is also imported when importing an image format * make the features propperly formatted * fix merge * Move cairo over to 0.14 version * Fix clippy issues * Fix weird addition * Remove snapshots * Fix tests * Fix CI * Make piet-cairo almost unwrap free * Make gradient stops hashable (#454) Implement Hash and Eq traits on gradient stops, which implies doing the same for colors. This facilitates creating a cache of baked gradient ramps. Closes #450 * [cairo] Fix for big-endian systems Cairo defines pixels as "a 32-bit quantity [...] stored in native-endian". However, the existing code was treating a pixel as four 8-bit quantities. Put differently, it was hardcoding little endian. This commit changes the code to first calculate the pixel value as an u32 and then use u32::to_ne_bytes() to get back to "the world of bytes". For readability, this is done in a new helper function. I did not actually check if this fixes anything on big endian, but at least it does not change anything for little endian according to the test-pictures examples. Fixes: #224 Signed-off-by: Uli Schlachter <psychon@znc.in> * Remove unnecessary dst_off parameter Signed-off-by: Uli Schlachter <psychon@znc.in> * Fix clippy warning about single-character names error: 5 bindings with single-character names in scope Error: --> piet-cairo/src/lib.rs:523:47 | 523 | fn write_rgba(data: &mut [u8], offset: usize, x: usize, r: u8, g: u8, b: u8, a: u8) { | ^ ^ ^ ^ ^ | = note: `-D clippy::many-single-char-names` implied by `-D warnings` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names Signed-off-by: Uli Schlachter <psychon@znc.in> * Add a simple criterion benchmark This adds a simple benchmark for piet-cairo's make_image function. Signed-off-by: Uli Schlachter <psychon@znc.in> * Fill benchmark image with random data Signed-off-by: Uli Schlachter <psychon@znc.in> * Make rustfmt happy Signed-off-by: Uli Schlachter <psychon@znc.in> * Try to clarify docs for ImageFormat The image format is based on individual bytes and thus independent of the system's endianness. This commit tries to make that more explicit. This was originally suggested in issue #224. Signed-off-by: Uli Schlachter <psychon@znc.in> * Fix issue with benchmarks * Remove redefinition * Add safety comment Co-authored-by: Raph Levien <raph.levien@gmail.com> Co-authored-by: Uli Schlachter <psychon@znc.in>
Hi,
I just passed by and saw this code:
piet/piet-cairo/src/lib.rs
Lines 270 to 299 in 1568b6d
This code is wrong. I do not understand it completely, but the swapping of the order of channels (stuff like
data[foo + 0] = buf[bar + 2]
) looks very much like a byte order conversion.From https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t,
CAIRO_FORMAT_RGB24
is described as:This means that in C, if you have a pixel that starts at address
ptr
, the blue channel can be read viauint8_t blue = *((uint32_t*)ptr) & 0xff
. This reads anuint32_t
/u32
in the native endianness and then gets the lowest byte from that. On little endian, the least significant byte is stored first, so here this is equivalent touint8_t blue = *ptr
. On big-endian however, this ends up doinguint8_t blue = *(ptr + 3);
.I hope this is understandable.
Perhaps you could also clarify in https://docs.rs/piet/0.1.0/piet/enum.ImageFormat.html that the format is meant to be as "with first byte for the red channel, second byte for green, and third byte for blue" or something like that (to tell it apart from cairo's use of a "32 byte quantity" with its endianness).
Slightly unrelated:
piet/piet-cairo/src/lib.rs
Line 280 in 1568b6d
I am not shure how changing the order of the channels would help here. Right now you copy from offsets
(2,1,0,3)
. After swapping the order of channels, you end up with(3,0,2,1)
. That's not really an improvement. PerhapsABGR
is meant by the comment? That would match cairo's ARGB order on little endian systems... well, no... now you got me really confused. Anyway, something is up with thatTODO
.Edit: Random code link to underline what I mean: https://sources.debian.org/src/gtk+2.0/2.24.32-4/gdk/gdkcairo.c/?hl=211#L238
At the time of writing, this has the following code:
and 20 lines later in the file is the ARGB case:
The text was updated successfully, but these errors were encountered: