|
| 1 | +// freedesktop.cairo — draw something, read the pixels back, and check that the |
| 2 | +// feature split actually took effect. |
| 3 | +// |
| 4 | +// Cairo needs no display and no fonts on disk to draw: an image surface is |
| 5 | +// memory, and that is what a compositor's software fallback and every |
| 6 | +// screenshot path use. So this test draws for real rather than only checking |
| 7 | +// that symbols resolve. |
| 8 | +// |
| 9 | +// THE FEATURE ASSERTIONS ARE THE POINT OF THE PACKAGE. `xlib` and `xcb` are off |
| 10 | +// by default, and "off" has to mean the macro is undefined — otherwise a |
| 11 | +// consumer could `#include <cairo-xlib.h>` and fail at link instead of at |
| 12 | +// compile, which is the wrong place to find out. |
| 13 | + |
| 14 | +#ifdef __linux__ |
| 15 | + |
| 16 | +#include <cairo.h> |
| 17 | + |
| 18 | +#include <cstdio> |
| 19 | +#include <cstring> |
| 20 | + |
| 21 | +import freedesktop.cairo; |
| 22 | + |
| 23 | +namespace { |
| 24 | + |
| 25 | +int failures = 0; |
| 26 | + |
| 27 | +void check(bool ok, const char *what) |
| 28 | +{ |
| 29 | + std::printf("%-58s %s\n", what, ok ? "ok" : "FAILED"); |
| 30 | + if (!ok) { |
| 31 | + ++failures; |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +} // namespace |
| 36 | + |
| 37 | +int main() |
| 38 | +{ |
| 39 | + std::printf(" cairo %s\n", cairo_version_string()); |
| 40 | + check(cairo_version() >= 11802, "cairo_version reports 1.18.2 or newer"); |
| 41 | + |
| 42 | + // ── 1. Draw, then read the pixel back ──────────────────────────────── |
| 43 | + // A 64x64 ARGB32 surface, filled with an exact colour, then one pixel |
| 44 | + // sampled. Exact because cairo_set_source_rgb takes doubles and |
| 45 | + // 0.25/0.5/0.75 land on 64/128/191 without rounding ambiguity — the same |
| 46 | + // values the GBM/EGL closed-loop test uses, for the same reason. |
| 47 | + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 64, 64); |
| 48 | + check(cairo_surface_status(s) == CAIRO_STATUS_SUCCESS, |
| 49 | + "cairo_image_surface_create(ARGB32, 64, 64)"); |
| 50 | + |
| 51 | + cairo_t *cr = cairo_create(s); |
| 52 | + check(cairo_status(cr) == CAIRO_STATUS_SUCCESS, "cairo_create on it"); |
| 53 | + |
| 54 | + // ONE drawing pass, THEN one flush, THEN read. The first version of this |
| 55 | + // test flushed and read between the paint and the stroke, and the stroke |
| 56 | + // then changed zero pixels with `cairo_status` still SUCCESS — cairo had |
| 57 | + // handed the buffer out and did not take it back. `cairo_surface_flush` is |
| 58 | + // the end of a drawing sequence, not a checkpoint inside one. |
| 59 | + cairo_set_source_rgb(cr, 0.25, 0.5, 0.75); |
| 60 | + cairo_paint(cr); |
| 61 | + |
| 62 | + cairo_set_source_rgb(cr, 1, 1, 1); |
| 63 | + cairo_set_line_width(cr, 8); |
| 64 | + cairo_move_to(cr, 8, 8); |
| 65 | + cairo_line_to(cr, 56, 56); |
| 66 | + cairo_stroke(cr); |
| 67 | + |
| 68 | + check(cairo_status(cr) == CAIRO_STATUS_SUCCESS, "…and the context is still clean"); |
| 69 | + cairo_surface_flush(s); |
| 70 | + |
| 71 | + const unsigned char *d = cairo_image_surface_get_data(s); |
| 72 | + const int stride = cairo_image_surface_get_stride(s); |
| 73 | + |
| 74 | + // ARGB32 is little-endian: B G R A. |
| 75 | + // (56,8) is far from the diagonal, so it still carries the paint — |
| 76 | + // 0.25/0.5/0.75 land on 64/128/191 with no rounding ambiguity, the same |
| 77 | + // values the GBM/EGL closed-loop test uses and for the same reason. |
| 78 | + const unsigned char *bg = d + 8 * stride + 56 * 4; |
| 79 | + std::printf(" (56,8) = %u %u %u %u (B G R A)\n", bg[0], bg[1], bg[2], bg[3]); |
| 80 | + check(bg[2] == 64 && bg[1] == 128 && bg[0] == 191 && bg[3] == 255, |
| 81 | + "the painted background is exactly what was asked for"); |
| 82 | + |
| 83 | + // (32,32) is on the diagonal, under an 8px white stroke. |
| 84 | + const unsigned char *on = d + 32 * stride + 32 * 4; |
| 85 | + std::printf(" (32,32) = %u %u %u %u\n", on[0], on[1], on[2], on[3]); |
| 86 | + // ── 3. The path machinery, checked directly ───────────────────────── |
| 87 | + // |
| 88 | + // These three are here because of what they caught. With |
| 89 | + // `WORDS_BIGENDIAN` defined to 0 — which cairo tests with `#ifdef`, so 0 |
| 90 | + // means BIG-ENDIAN — every path got garbage fixed-point coordinates while |
| 91 | + // `cairo_status` reported success and `cairo_paint` kept working: |
| 92 | + // |
| 93 | + // path_extents -8.03e+06 -8.03e+06 4.37e+06 4.37e+06 |
| 94 | + // in_fill(40, 40) 1 (the point is outside the rectangle) |
| 95 | + // |
| 96 | + // The pixel check above catches it too, but only as "the stroke drew |
| 97 | + // nothing", which sends you looking for a missing source file. These say |
| 98 | + // where the arithmetic went wrong. |
| 99 | + { |
| 100 | + cairo_t *c3 = cairo_create(s); |
| 101 | + cairo_rectangle(c3, 4, 4, 16, 16); |
| 102 | + double x1, y1, x2, y2; |
| 103 | + cairo_path_extents(c3, &x1, &y1, &x2, &y2); |
| 104 | + std::printf(" path_extents = %g %g %g %g\n", x1, y1, x2, y2); |
| 105 | + check(x1 == 4 && y1 == 4 && x2 == 20 && y2 == 20, |
| 106 | + "a rectangle path has the extents it was given"); |
| 107 | + check(cairo_in_fill(c3, 10, 10) == 1, "…and a point inside it reads as inside"); |
| 108 | + check(cairo_in_fill(c3, 40, 40) == 0, "…and a point outside reads as outside"); |
| 109 | + cairo_destroy(c3); |
| 110 | + } |
| 111 | + |
| 112 | + check(on[0] == 255 && on[1] == 255 && on[2] == 255, |
| 113 | + "…and the stroked diagonal covers the centre in white"); |
| 114 | + |
| 115 | + |
| 116 | + cairo_destroy(cr); |
| 117 | + cairo_surface_destroy(s); |
| 118 | + |
| 119 | + // ── 3. The default feature set ─────────────────────────────────────── |
| 120 | +#ifdef CAIRO_HAS_FT_FONT |
| 121 | + check(true, "feature ft is ON by default (CAIRO_HAS_FT_FONT)"); |
| 122 | +#else |
| 123 | + check(false, "feature ft should be ON by default"); |
| 124 | +#endif |
| 125 | +#ifdef CAIRO_HAS_FC_FONT |
| 126 | + check(true, "feature fc is ON by default (CAIRO_HAS_FC_FONT)"); |
| 127 | +#else |
| 128 | + check(false, "feature fc should be ON by default"); |
| 129 | +#endif |
| 130 | +#ifdef CAIRO_HAS_PNG_FUNCTIONS |
| 131 | + check(true, "feature png is ON by default"); |
| 132 | +#else |
| 133 | + check(false, "feature png should be ON by default"); |
| 134 | +#endif |
| 135 | + |
| 136 | + // ── 4. X11 is OFF, and that is the whole point ─────────────────────── |
| 137 | + // A Wayland program must not acquire libX11 by depending on cairo. If this |
| 138 | + // ever fails, someone made a backend unconditional. |
| 139 | +#ifdef CAIRO_HAS_XLIB_SURFACE |
| 140 | + check(false, "xlib must be OFF unless the consumer asks for it"); |
| 141 | +#else |
| 142 | + check(true, "feature xlib is OFF by default — no libX11 pulled in"); |
| 143 | +#endif |
| 144 | +#ifdef CAIRO_HAS_XCB_SURFACE |
| 145 | + check(false, "xcb must be OFF unless the consumer asks for it"); |
| 146 | +#else |
| 147 | + check(true, "feature xcb is OFF by default"); |
| 148 | +#endif |
| 149 | + |
| 150 | + // ── 5. The module carries what the features enabled ────────────────── |
| 151 | + // `cairo_image_surface_create_from_png` lives behind CAIRO_HAS_PNG_FUNCTIONS |
| 152 | + // inside cairo.h itself, not in a feature header — the module generator has |
| 153 | + // to notice that or a png-enabled consumer cannot reach it through `import`. |
| 154 | + check(&cairo_surface_write_to_png != nullptr, |
| 155 | + "module exports a name guarded inside cairo.h (write_to_png)"); |
| 156 | + check(&cairo_ft_font_face_create_for_ft_face != nullptr, |
| 157 | + "…and one from a feature header (cairo_ft_font_face_create…)"); |
| 158 | + |
| 159 | + std::printf("\n%d check(s) failed\n", failures); |
| 160 | + return failures == 0 ? 0 : 1; |
| 161 | +} |
| 162 | + |
| 163 | +#else |
| 164 | +int main() { return 0; } |
| 165 | +#endif |
0 commit comments