gtk2-ng/gdk/gdkrgb16.c
Daemonratte 73183e1a30 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.
2026-08-05 12:15:23 +02:00

742 lines
26 KiB
C

/* GTK - The GIMP Toolkit
* High-precision GdkRGB image conversion
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
#include "config.h"
#include <string.h>
#include "gdkinternals.h"
#include "gdkrgb.h"
#include "gdkscreen.h"
#include "gdkalias.h"
typedef void (*GdkRgb16ConvFunc) (GdkImage *image,
gint x0,
gint y0,
gint width,
gint height,
const guint16 *buf,
gint rowstride);
#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 (GdkImage *image, \
gint x0, \
gint y0, \
gint width, \
gint height, \
const guint16 *buf, \
gint rowstride) \
{ \
const guchar *row; \
const guint16 *src; \
guchar *dst_row; \
guchar *dst; \
guint32 r, g, b; \
guint32 alpha_mask; \
gint x, y; \
\
row = (const guchar *) buf; \
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 GdkRgb16ConvFunc
gdk_rgb16_select_converter (GdkDrawable *drawable,
gboolean is_rgbx,
GdkRgbDither dith)
{
GdkVisual *visual;
GdkByteOrder byte_order;
GdkScreen *screen;
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
/*
* Preserve the existing GdkRGB dither path for maximum dithering
* and for unknown future values.
*/
if (dith != GDK_RGB_DITHER_NONE &&
dith != GDK_RGB_DITHER_NORMAL)
return NULL;
visual = gdk_drawable_get_visual (drawable);
if (visual == NULL)
return NULL;
if (gdk_visual_get_visual_type (visual) != GDK_VISUAL_TRUE_COLOR)
return NULL;
screen = gdk_visual_get_screen (visual);
depth = gdk_visual_get_depth (visual);
byte_order = gdk_visual_get_byte_order (visual);
gdk_visual_get_red_pixel_details (visual, &red_mask, NULL, NULL);
gdk_visual_get_green_pixel_details (visual, &green_mask, NULL, NULL);
gdk_visual_get_blue_pixel_details (visual, &blue_mask, NULL, NULL);
bits_per_pixel = _gdk_windowing_get_bits_for_depth
(gdk_screen_get_display (screen), depth);
#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_rgb16_draw_direct (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
const guint16 *buf,
gint pixel_words,
gint rowstride,
GdkRgb16ConvFunc converter)
{
GdkScreen *screen;
GdkImage *image;
const guint16 *tile;
gint depth;
gint x0, y0;
gint xs0, ys0;
gint width1, height1;
screen = gdk_drawable_get_screen (drawable);
depth = gdk_drawable_get_depth (drawable);
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);
tile = (const guint16 *)
((const guchar *) buf +
y0 * rowstride +
x0 * pixel_words * sizeof (guint16));
image = _gdk_image_get_scratch (screen,
width1,
height1,
depth,
&xs0,
&ys0);
converter (image,
xs0,
ys0,
width1,
height1,
tile,
rowstride);
gdk_draw_image (drawable,
gc,
image,
xs0,
ys0,
x + x0,
y + y0,
width1,
height1);
}
}
}
static void
gdk_rgb16_draw_fallback (GdkDrawable *drawable,
GdkGC *gc,
gint x,
gint y,
gint width,
gint height,
GdkRgbDither dith,
const guint16 *buf,
gint pixel_words,
gint rowstride,
gint xdith,
gint ydith)
{
const guchar *input_row;
const guint16 *input;
guchar *stage;
guchar *output;
gint stage_rowstride;
gint x0, y0;
gint x1, y1;
gint width1, height1;
stage = g_malloc (MIN (width, GDK_SCRATCH_IMAGE_WIDTH) *
MIN (height, GDK_SCRATCH_IMAGE_HEIGHT) * 3);
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);
stage_rowstride = width1 * 3;
input_row = (const guchar *) buf +
y0 * rowstride +
x0 * pixel_words * sizeof (guint16);
output = stage;
for (y1 = 0; y1 < height1; y1++)
{
input = (const guint16 *) input_row;
for (x1 = 0; x1 < width1; x1++)
{
*output++ = input[0] >> 8;
*output++ = input[1] >> 8;
*output++ = input[2] >> 8;
input += pixel_words;
}
input_row += rowstride;
}
gdk_draw_rgb_image_dithalign (drawable,
gc,
x + x0,
y + y0,
width1,
height1,
dith,
stage,
stage_rowstride,
xdith,
ydith);
}
}
g_free (stage);
}
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)
{
GdkRgb16ConvFunc converter;
gint pixel_words;
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;
pixel_words = is_rgbx ? 4 : 3;
converter = gdk_rgb16_select_converter (drawable, is_rgbx, dith);
if (converter != NULL)
gdk_rgb16_draw_direct (drawable,
gc,
x,
y,
width,
height,
buf,
pixel_words,
rowstride,
converter);
else
gdk_rgb16_draw_fallback (drawable,
gc,
x,
y,
width,
height,
dith,
buf,
pixel_words,
rowstride,
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);
}
#define __GDK_RGB16_C__
#include "gdkaliasdef.c"