+ v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
+ v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
+ v = ((v >> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4);
+ return v;
+}
+
+static cairo_surface_t *
+xbm_to_cairo(const void *xbmdata, int width, int height)
+{
+ const unsigned char *data = xbmdata;
+ unsigned char *sdata;
+ cairo_surface_t *s;
+ int stride;
+
+ s = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
+ if (cairo_surface_status(s) != CAIRO_STATUS_SUCCESS)
+ return s;
+
+ sdata = cairo_image_surface_get_data(s);
+ stride = cairo_image_surface_get_stride(s);
+ assert(stride % 4 == 0);
+ width = (width + 7) / 8;
+
+ cairo_surface_flush(s);
+ for (int y = 0; y < height; y++) {
+ /*
+ * Cairo is nuts. The mapping of bits to pixels in the A1
+ * format actually depends on the host *byte* ordering, so
+ * we actually need these two separate cases.
+ */
+ if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
+ /* Easy, XBM and Cairo match in this case */
+ memcpy(sdata, data, width);
+ } else {
+ /* No such luck, we have to convert manually */
+ for (int x = 0; x < width; x += 4) {
+ guint32 word = 0;
+
+ memcpy(&word, data+x, MIN(4, width-x));
+ word = reverse4b(word);
+ memcpy(sdata+x, &word, 4);
+ }
+ }
+
+ sdata += stride;
+ data += width;
+ }
+ cairo_surface_mark_dirty(s);