Hello!
I'm looking for help with a problem i'm running into with the Cairo
graphics library. I want to paint a surface onto another surface (in this case an XLib window) in such a way that it will take up as much space as possible while staying at a 4:3 aspect ratio and be centered within the window. However, i seemingly can't figure out the right parameters for cairo_scale(), and am running into issues with a stretched image and an
image that doesn't take up the right amount of space.
Minimum example program demonstrating this behavior below; compile with
`$CC tmp.c -lX11 -lm -lcairo`. Rescale the window and you'll see the
problem i'm facing. (I am fully aware this will lead to a pixelated output [not visible in the example program where it's just pure white]; this is intentional and exactly what i want to happen.)
#include <X11/Xlib.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <stddef.h>
#include <math.h>
const int width = 640, height = 480;
const long double aspect_ratio = 4.0l/3.0l, inverse_aspect_ratio =
3.0l/4.0l;
int main(void)
{
Display * display = XOpenDisplay(NULL);
int screen = DefaultScreen(display);
Window window = XCreateSimpleWindow(display, RootWindow(display,
screen), 10, 10, width, height, 1, BlackPixel(display, screen),
WhitePixel(display, screen));
XSelectInput(display, window, StructureNotifyMask);
XMapWindow(display, window);
cairo_surface_t * surface = cairo_xlib_surface_create(display, window,
DefaultVisual(display, screen), width, height);
cairo_xlib_surface_set_size(surface, width, height);
cairo_t * root_instance = cairo_create(surface);
cairo_surface_destroy(surface);
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width,
height);
cairo_t * instance = cairo_create(surface);
cairo_set_source_rgb(instance, 1, 1, 1);
cairo_paint(instance);
for (;;) {
for (XEvent e; XPending(display) != 0; XNextEvent(display, &e))
if (e.type == ConfigureNotify) {
cairo_identity_matrix(root_instance);
if (e.xconfigure.width < width || e.xconfigure.height <
height) {
XResizeWindow(display, window, fmax(width,
e.xconfigure.width), fmax(width, e.xconfigure.height));
cairo_xlib_surface_set_size(cairo_get_target(root_instance), fmax(width, e.xconfigure.width), fmax(height, e.xconfigure.height));
} else {
cairo_xlib_surface_set_size(cairo_get_target(root_instance), e.xconfigure.width, e.xconfigure.height);
if (e.xconfigure.width >= aspect_ratio *
e.xconfigure.height) {
cairo_translate(root_instance, (e.xconfigure.width
- e.xconfigure.height * aspect_ratio) / 2, 0);
cairo_scale(root_instance, (e.xconfigure.height *
aspect_ratio) / width, e.xconfigure.height /
height);
} else {
cairo_translate(root_instance, 0,
(e.xconfigure.height - e.xconfigure.width *
inverse_aspect_ratio) / 2);
cairo_scale(root_instance, e.xconfigure.width /
width, (e.xconfigure.height * inverse_aspect_ratio)
/ height);
}
}
}
cairo_push_group(root_instance);
cairo_set_source_rgb(root_instance, 0, 0, 0);
cairo_paint(root_instance);
cairo_set_source_surface(root_instance, surface, 0, 0);
cairo_paint(root_instance);
cairo_pop_group_to_source(root_instance);
cairo_paint(root_instance);
cairo_surface_flush(cairo_get_target(root_instance));
}
}
On 6/11/23 20:07, Ben Bacarisse wrote:<cut>
Blue-Maned_Hawk <bluemanedhawk@gmail.com> writes:
cairo_xlib_surface_set_size(cairo_get_target(root_instance), fmax(width, >>> e.xconfigure.width), fmax(height, e.xconfigure.height));This case looks odd. What's the intent? Do you want stop the user
making the window narrower than width or shorted than height?
Yes, i do. I didn't know of any other way to do this.
If that's
what you want you should probably limit e.xconfigure.{width,height}, set
the X window size and then carry on as before because you still need to
set the offset and scale and offset the source in this case as well.
How would i do that?
Blue-Maned_Hawk <bluemanedhawk@gmail.com> writes:
On 6/11/23 20:07, Ben Bacarisse wrote:<cut>
Blue-Maned_Hawk <bluemanedhawk@gmail.com> writes:
cairo_xlib_surface_set_size(cairo_get_target(root_instance), fmax(width, >>>> e.xconfigure.width), fmax(height, e.xconfigure.height));This case looks odd. What's the intent? Do you want stop the user
making the window narrower than width or shorted than height?
Yes, i do. I didn't know of any other way to do this.
If that's
what you want you should probably limit e.xconfigure.{width,height}, set >>> the X window size and then carry on as before because you still need to
set the offset and scale and offset the source in this case as well.
How would i do that?
You could change e.xconfigure.width (and height) but rather than do that
I'd base all the code on two new sizes
int new_width = e.xconfigure.width, new_height = e.xconfigure.height;
if (new_width < width || new_height < height) {
new_width = fmax(width, new_width);
ditto height
set Xwindow size to new_width/new_height
}
what goes here is what was your else clause but using new_width and
new_height rather than the e.xconfigure versions
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 428 |
Nodes: | 16 (2 / 14) |
Uptime: | 110:49:16 |
Calls: | 9,053 |
Files: | 13,395 |
Messages: | 6,016,118 |