gdk: add high-depth RGB image drawing

Add RGB48 and RGBX64 drawing APIs with direct conversion for common 15-, 16-, 24-, 30- and 32-bit TrueColor visuals. Preserve the existing GdkRGB path as a fallback for unsupported visuals and maximum dithering.
This commit is contained in:
Daemonratte 2026-08-05 12:12:13 +02:00
commit 8da0ce4261
4 changed files with 745 additions and 0 deletions

View file

@ -335,6 +335,10 @@ gdk_draw_indexed_image
gdk_draw_gray_image
gdk_draw_rgb_32_image
gdk_draw_rgb_32_image_dithalign
gdk_draw_rgb_48_image
gdk_draw_rgb_48_image_dithalign
gdk_draw_rgbx_64_image
gdk_draw_rgbx_64_image_dithalign
GdkRgbDither
<SUBSECTION>

View file

@ -1118,6 +1118,15 @@ gdk_draw_rgb_image_dithalign
#endif
#endif
#if IN_HEADER(__GDK_RGB_H__)
#if IN_FILE(__GDK_RGB_C__)
gdk_draw_rgb_48_image
gdk_draw_rgb_48_image_dithalign
gdk_draw_rgbx_64_image
gdk_draw_rgbx_64_image_dithalign
#endif
#endif
#if IN_HEADER(__GDK_SCREEN_H__)
#if IN_FILE(__GDK_SCREEN_C__)
gdk_screen_get_type G_GNUC_CONST

View file

@ -3548,6 +3548,696 @@ gdk_rgb_get_info_from_drawable (GdkDrawable *drawable)
return gdk_rgb_get_info_from_colormap (cmap);
}
/* High-depth RGB input. */
#define GDK_RGB16_STORE_RGB(p, r, g, b, a) G_STMT_START { \
(p)[0] = (r) >> 8; \
(p)[1] = (g) >> 8; \
(p)[2] = (b) >> 8; \
(p) += 3; \
} G_STMT_END
#define GDK_RGB16_STORE_BGR(p, r, g, b, a) G_STMT_START { \
(p)[0] = (b) >> 8; \
(p)[1] = (g) >> 8; \
(p)[2] = (r) >> 8; \
(p) += 3; \
} G_STMT_END
#define GDK_RGB16_STORE_BGRA(p, r, g, b, a) G_STMT_START { \
(p)[0] = (b) >> 8; \
(p)[1] = (g) >> 8; \
(p)[2] = (r) >> 8; \
(p)[3] = 0xff; \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_ARGB(p, r, g, b, a) G_STMT_START { \
(p)[0] = 0xff; \
(p)[1] = (r) >> 8; \
(p)[2] = (g) >> 8; \
(p)[3] = (b) >> 8; \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_RGB0_NATIVE(p, r, g, b, a) G_STMT_START { \
guint32 pixel__ = (((b) >> 8) << 16) | \
(((g) >> 8) << 8) | \
((r) >> 8); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_565(p, r, g, b, a) G_STMT_START { \
guint16 pixel__ = ((r) & 0xf800) | \
(((g) & 0xfc00) >> 5) | \
((b) >> 11); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 2; \
} G_STMT_END
#define GDK_RGB16_STORE_565_BR(p, r, g, b, a) G_STMT_START { \
guint16 pixel__ = ((r) & 0xf800) | \
(((g) & 0xfc00) >> 5) | \
((b) >> 11); \
pixel__ = (pixel__ >> 8) | (pixel__ << 8); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 2; \
} G_STMT_END
#define GDK_RGB16_STORE_555(p, r, g, b, a) G_STMT_START { \
guint16 pixel__ = (((r) & 0xf800) >> 1) | \
(((g) & 0xf800) >> 6) | \
((b) >> 11); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 2; \
} G_STMT_END
#define GDK_RGB16_STORE_555_BR(p, r, g, b, a) G_STMT_START { \
guint16 pixel__ = (((r) & 0xf800) >> 1) | \
(((g) & 0xf800) >> 6) | \
((b) >> 11); \
pixel__ = (pixel__ >> 8) | (pixel__ << 8); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 2; \
} G_STMT_END
#define GDK_RGB16_STORE_101010_R_LOW_LE(p, r, g, b, a) \
G_STMT_START { \
guint32 pixel__ = (a) | \
(((b) >> 6) << 20) | \
(((g) >> 6) << 10) | \
((r) >> 6); \
pixel__ = GUINT32_TO_LE (pixel__); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_101010_B_LOW_LE(p, r, g, b, a) \
G_STMT_START { \
guint32 pixel__ = (a) | \
(((r) >> 6) << 20) | \
(((g) >> 6) << 10) | \
((b) >> 6); \
pixel__ = GUINT32_TO_LE (pixel__); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_101010_R_LOW_BE(p, r, g, b, a) \
G_STMT_START { \
guint32 pixel__ = (a) | \
(((b) >> 6) << 20) | \
(((g) >> 6) << 10) | \
((r) >> 6); \
pixel__ = GUINT32_TO_BE (pixel__); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 4; \
} G_STMT_END
#define GDK_RGB16_STORE_101010_B_LOW_BE(p, r, g, b, a) \
G_STMT_START { \
guint32 pixel__ = (a) | \
(((r) >> 6) << 20) | \
(((g) >> 6) << 10) | \
((b) >> 6); \
pixel__ = GUINT32_TO_BE (pixel__); \
memcpy ((p), &pixel__, sizeof (pixel__)); \
(p) += 4; \
} G_STMT_END
#define DEFINE_GDK_RGB16_CONVERTER(name, words, pixel_bytes, store) \
static void \
name (GdkRgbInfo *image_info, \
GdkImage *image, \
gint x0, \
gint y0, \
gint width, \
gint height, \
const guchar *buf, \
gint rowstride, \
gint x_align, \
gint y_align, \
GdkRgbCmap *cmap) \
{ \
const guchar *row = buf; \
const guint16 *src; \
guchar *dst_row; \
guchar *dst; \
guint32 r, g, b; \
guint32 alpha_mask; \
gint x, y; \
\
(void) image_info; \
(void) x_align; \
(void) y_align; \
(void) cmap; \
\
dst_row = ((guchar *) image->mem) + \
y0 * image->bpl + x0 * (pixel_bytes); \
alpha_mask = image->depth == 32 ? 0xc0000000U : 0; \
(void) alpha_mask; \
\
for (y = 0; y < height; y++) \
{ \
src = (const guint16 *) row; \
dst = dst_row; \
\
for (x = 0; x < width; x++) \
{ \
r = src[0]; \
g = src[1]; \
b = src[2]; \
src += (words); \
store (dst, r, g, b, alpha_mask); \
} \
\
row += rowstride; \
dst_row += image->bpl; \
} \
}
#define DEFINE_GDK_RGB16_PAIR(suffix, pixel_bytes, store) \
DEFINE_GDK_RGB16_CONVERTER (gdk_rgb48_convert_ ## suffix, \
3, pixel_bytes, store) \
DEFINE_GDK_RGB16_CONVERTER (gdk_rgbx64_convert_ ## suffix, \
4, pixel_bytes, store)
DEFINE_GDK_RGB16_PAIR (rgb, 3, GDK_RGB16_STORE_RGB)
DEFINE_GDK_RGB16_PAIR (bgr, 3, GDK_RGB16_STORE_BGR)
DEFINE_GDK_RGB16_PAIR (bgra, 4, GDK_RGB16_STORE_BGRA)
DEFINE_GDK_RGB16_PAIR (argb, 4, GDK_RGB16_STORE_ARGB)
DEFINE_GDK_RGB16_PAIR (rgb0_native, 4, GDK_RGB16_STORE_RGB0_NATIVE)
DEFINE_GDK_RGB16_PAIR (565, 2, GDK_RGB16_STORE_565)
DEFINE_GDK_RGB16_PAIR (565_br, 2, GDK_RGB16_STORE_565_BR)
DEFINE_GDK_RGB16_PAIR (555, 2, GDK_RGB16_STORE_555)
DEFINE_GDK_RGB16_PAIR (555_br, 2, GDK_RGB16_STORE_555_BR)
DEFINE_GDK_RGB16_PAIR (101010_r_low_le, 4,
GDK_RGB16_STORE_101010_R_LOW_LE)
DEFINE_GDK_RGB16_PAIR (101010_b_low_le, 4,
GDK_RGB16_STORE_101010_B_LOW_LE)
DEFINE_GDK_RGB16_PAIR (101010_r_low_be, 4,
GDK_RGB16_STORE_101010_R_LOW_BE)
DEFINE_GDK_RGB16_PAIR (101010_b_low_be, 4,
GDK_RGB16_STORE_101010_B_LOW_BE)
#undef DEFINE_GDK_RGB16_PAIR
#undef DEFINE_GDK_RGB16_CONVERTER
#undef GDK_RGB16_STORE_RGB
#undef GDK_RGB16_STORE_BGR
#undef GDK_RGB16_STORE_BGRA
#undef GDK_RGB16_STORE_ARGB
#undef GDK_RGB16_STORE_RGB0_NATIVE
#undef GDK_RGB16_STORE_565
#undef GDK_RGB16_STORE_565_BR
#undef GDK_RGB16_STORE_555
#undef GDK_RGB16_STORE_555_BR
#undef GDK_RGB16_STORE_101010_R_LOW_LE
#undef GDK_RGB16_STORE_101010_B_LOW_LE
#undef GDK_RGB16_STORE_101010_R_LOW_BE
#undef GDK_RGB16_STORE_101010_B_LOW_BE
static void
gdk_rgb16_to_stage (GdkRgbInfo *image_info,
const guchar *buf,
gint rowstride,
gint width,
gint height,
gint words)
{
const guchar *row = buf;
const guint16 *src;
guchar *dst_row = gdk_rgb_ensure_stage (image_info);
guchar *dst;
gint x, y;
for (y = 0; y < height; y++)
{
src = (const guint16 *) row;
dst = dst_row;
for (x = 0; x < width; x++)
{
*dst++ = src[0] >> 8;
*dst++ = src[1] >> 8;
*dst++ = src[2] >> 8;
src += words;
}
row += rowstride;
dst_row += STAGE_ROWSTRIDE;
}
}
static void
gdk_draw_rgb16_fallback (GdkRgbInfo *image_info,
GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
const guchar *buf,
gint words,
gint rowstride,
GdkRgbConvFunc conv,
gint xdith,
gint ydith)
{
GdkScreen *screen;
GdkImage *image;
const guchar *buf_ptr;
gint depth;
gint x0, y0;
gint xs0, ys0;
gint width1, height1;
if (image_info->bitmap)
{
if (image_info->own_gc == NULL)
image_info->own_gc = gdk_gc_new (drawable);
gc = image_info->own_gc;
}
screen = gdk_drawable_get_screen (drawable);
depth = image_info->visual->depth;
for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT)
{
height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT);
for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH)
{
width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH);
buf_ptr = buf + (gssize) y0 * rowstride +
x0 * words * sizeof (guint16);
gdk_rgb16_to_stage (image_info,
buf_ptr,
rowstride,
width1,
height1,
words);
image = _gdk_image_get_scratch (screen,
width1,
height1,
depth,
&xs0,
&ys0);
conv (image_info,
image,
xs0, ys0,
width1, height1,
image_info->stage_buf,
STAGE_ROWSTRIDE,
x + x0 + xdith,
y + y0 + ydith,
NULL);
#ifndef DONT_ACTUALLY_DRAW
gdk_draw_image (drawable,
gc,
image,
xs0, ys0,
x + x0, y + y0,
width1, height1);
#endif
}
}
}
static GdkRgbConvFunc
gdk_rgb16_select_converter (GdkRgbInfo *image_info,
gboolean is_rgbx)
{
GdkVisual *visual = image_info->visual;
GdkByteOrder byte_order;
guint32 red_mask, green_mask, blue_mask;
gint depth;
gint bits_per_pixel;
gboolean byte_reversed;
gboolean mask_rgb;
gboolean mask_bgr;
#define SELECT_PAIR(suffix) \
return is_rgbx ? gdk_rgbx64_convert_ ## suffix \
: gdk_rgb48_convert_ ## suffix
if (visual->type != GDK_VISUAL_TRUE_COLOR)
return NULL;
depth = visual->depth;
byte_order = visual->byte_order;
red_mask = visual->red_mask;
green_mask = visual->green_mask;
blue_mask = visual->blue_mask;
bits_per_pixel = image_info->bpp * 8;
#if G_BYTE_ORDER == G_BIG_ENDIAN
byte_reversed = byte_order == GDK_LSB_FIRST;
#else
byte_reversed = byte_order == GDK_MSB_FIRST;
#endif
mask_rgb = red_mask == 0xff0000 &&
green_mask == 0x00ff00 &&
blue_mask == 0x0000ff;
mask_bgr = red_mask == 0x0000ff &&
green_mask == 0x00ff00 &&
blue_mask == 0xff0000;
if (bits_per_pixel == 16 && depth == 16 &&
red_mask == 0xf800 && green_mask == 0x07e0 &&
blue_mask == 0x001f)
{
if (byte_reversed)
SELECT_PAIR (565_br);
SELECT_PAIR (565);
}
if (bits_per_pixel == 16 && depth == 15 &&
red_mask == 0x7c00 && green_mask == 0x03e0 &&
blue_mask == 0x001f)
{
if (byte_reversed)
SELECT_PAIR (555_br);
SELECT_PAIR (555);
}
if (bits_per_pixel == 24 && depth == 24 &&
((mask_rgb && byte_order == GDK_LSB_FIRST) ||
(mask_bgr && byte_order == GDK_MSB_FIRST)))
SELECT_PAIR (bgr);
if (bits_per_pixel == 24 && depth == 24 &&
((mask_rgb && byte_order == GDK_MSB_FIRST) ||
(mask_bgr && byte_order == GDK_LSB_FIRST)))
SELECT_PAIR (rgb);
if (bits_per_pixel == 32 &&
(depth == 24 || depth == 32) &&
mask_rgb && byte_order == GDK_MSB_FIRST)
SELECT_PAIR (argb);
if (bits_per_pixel == 32 &&
(depth == 24 || depth == 32) &&
mask_rgb && byte_order == GDK_LSB_FIRST)
SELECT_PAIR (bgra);
#if G_BYTE_ORDER == G_BIG_ENDIAN
if (bits_per_pixel == 32 && depth == 24 &&
mask_bgr && byte_order == GDK_MSB_FIRST)
SELECT_PAIR (rgb0_native);
#else
if (bits_per_pixel == 32 && depth == 24 &&
mask_bgr && byte_order == GDK_LSB_FIRST)
SELECT_PAIR (rgb0_native);
#endif
if (bits_per_pixel == 32 &&
(depth == 30 || depth == 32) &&
byte_order == GDK_LSB_FIRST &&
red_mask == 0x000003ff &&
green_mask == 0x000ffc00 &&
blue_mask == 0x3ff00000)
SELECT_PAIR (101010_r_low_le);
if (bits_per_pixel == 32 &&
(depth == 30 || depth == 32) &&
byte_order == GDK_LSB_FIRST &&
red_mask == 0x3ff00000 &&
green_mask == 0x000ffc00 &&
blue_mask == 0x000003ff)
SELECT_PAIR (101010_b_low_le);
if (bits_per_pixel == 32 &&
(depth == 30 || depth == 32) &&
byte_order == GDK_MSB_FIRST &&
red_mask == 0x000003ff &&
green_mask == 0x000ffc00 &&
blue_mask == 0x3ff00000)
SELECT_PAIR (101010_r_low_be);
if (bits_per_pixel == 32 &&
(depth == 30 || depth == 32) &&
byte_order == GDK_MSB_FIRST &&
red_mask == 0x3ff00000 &&
green_mask == 0x000ffc00 &&
blue_mask == 0x000003ff)
SELECT_PAIR (101010_b_low_be);
#undef SELECT_PAIR
return NULL;
}
static void
gdk_draw_rgb16_image (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *buf,
gint rowstride,
gint xdith,
gint ydith,
gboolean is_rgbx)
{
GdkRgbInfo *image_info;
GdkRgbConvFunc converter;
gint words;
gboolean dithered;
g_return_if_fail (GDK_IS_DRAWABLE (drawable));
g_return_if_fail (GDK_IS_GC (gc));
g_return_if_fail (buf != NULL);
g_return_if_fail ((((guintptr) buf) & 1) == 0);
g_return_if_fail ((rowstride & 1) == 0);
if (width <= 0 || height <= 0)
return;
image_info = gdk_rgb_get_info_from_drawable (drawable);
if (image_info == NULL)
return;
words = is_rgbx ? 4 : 3;
converter = NULL;
if (dith == GDK_RGB_DITHER_NONE ||
dith == GDK_RGB_DITHER_NORMAL)
converter = gdk_rgb16_select_converter (image_info, is_rgbx);
if (converter != NULL)
{
gdk_draw_rgb_image_core (image_info,
drawable,
gc,
x, y,
width, height,
(const guchar *) buf,
words * sizeof (guint16),
rowstride,
converter,
NULL,
xdith, ydith);
return;
}
dithered =
!(dith == GDK_RGB_DITHER_NONE ||
(dith == GDK_RGB_DITHER_NORMAL &&
!image_info->dith_default));
converter = dithered ? image_info->conv_d
: image_info->conv;
gdk_draw_rgb16_fallback (image_info,
drawable,
gc,
x, y,
width, height,
(const guchar *) buf,
words,
rowstride,
converter,
xdith, ydith);
}
/**
* gdk_draw_rgb_48_image:
* @drawable: a #GdkDrawable
* @gc: a #GdkGC
* @x: destination X coordinate
* @y: destination Y coordinate
* @width: image width
* @height: image height
* @dith: dithering mode
* @rgb_buf: host-endian RGB data with three 16-bit channels per pixel
* @rowstride: number of bytes between consecutive rows
*
* Draws 48-bit RGB image data.
**/
void
gdk_draw_rgb_48_image (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgb_buf,
gint rowstride)
{
gdk_draw_rgb16_image (drawable,
gc,
x,
y,
width,
height,
dith,
rgb_buf,
rowstride,
0,
0,
FALSE);
}
/**
* gdk_draw_rgb_48_image_dithalign:
* @drawable: a #GdkDrawable
* @gc: a #GdkGC
* @x: destination X coordinate
* @y: destination Y coordinate
* @width: image width
* @height: image height
* @dith: dithering mode
* @rgb_buf: host-endian RGB data with three 16-bit channels per pixel
* @rowstride: number of bytes between consecutive rows
* @xdith: X dither offset
* @ydith: Y dither offset
*
* Draws 48-bit RGB image data with explicit dither alignment.
**/
void
gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgb_buf,
gint rowstride,
gint xdith,
gint ydith)
{
gdk_draw_rgb16_image (drawable,
gc,
x,
y,
width,
height,
dith,
rgb_buf,
rowstride,
xdith,
ydith,
FALSE);
}
/**
* gdk_draw_rgbx_64_image:
* @drawable: a #GdkDrawable
* @gc: a #GdkGC
* @x: destination X coordinate
* @y: destination Y coordinate
* @width: image width
* @height: image height
* @dith: dithering mode
* @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel
* @rowstride: number of bytes between consecutive rows
*
* Draws 64-bit RGBX image data. The X channel is ignored.
**/
void
gdk_draw_rgbx_64_image (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgbx_buf,
gint rowstride)
{
gdk_draw_rgb16_image (drawable,
gc,
x,
y,
width,
height,
dith,
rgbx_buf,
rowstride,
0,
0,
TRUE);
}
/**
* gdk_draw_rgbx_64_image_dithalign:
* @drawable: a #GdkDrawable
* @gc: a #GdkGC
* @x: destination X coordinate
* @y: destination Y coordinate
* @width: image width
* @height: image height
* @dith: dithering mode
* @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel
* @rowstride: number of bytes between consecutive rows
* @xdith: X dither offset
* @ydith: Y dither offset
*
* Draws 64-bit RGBX image data with explicit dither alignment.
* The X channel is ignored.
**/
void
gdk_draw_rgbx_64_image_dithalign (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgbx_buf,
gint rowstride,
gint xdith,
gint ydith)
{
gdk_draw_rgb16_image (drawable,
gc,
x,
y,
width,
height,
dith,
rgbx_buf,
rowstride,
xdith,
ydith,
TRUE);
}
void
gdk_draw_rgb_image (GdkDrawable *drawable,
GdkGC *gc,

View file

@ -147,6 +147,48 @@ gboolean gdk_rgb_colormap_ditherable (GdkColormap *cmap);
#endif
#endif /* GDK_DISABLE_DEPRECATED */
void gdk_draw_rgb_48_image (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgb_buf,
gint rowstride);
void gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgb_buf,
gint rowstride,
gint xdith,
gint ydith);
void gdk_draw_rgbx_64_image (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgbx_buf,
gint rowstride);
void gdk_draw_rgbx_64_image_dithalign
(GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *rgbx_buf,
gint rowstride,
gint xdith,
gint ydith);
G_END_DECLS