image: arbitrary linear transforms [WIP]

This commit is contained in:
Lephe 2022-05-14 22:32:59 +01:00
parent 818f950fff
commit 780acb3fc9
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
12 changed files with 200 additions and 17 deletions

View File

@ -174,6 +174,10 @@ set(SOURCES_CG
src/image/image_get_pixel.c
src/image/image_hflip.c
src/image/image_hflip_alloc.c
src/image/image_rotate.c
src/image/image_rotate_around.c
src/image/image_rotate_around_scale.c
src/image/image_scale.c
src/image/image_set_palette.c
src/image/image_set_pixel.c
src/image/image_sub.c

View File

@ -448,15 +448,101 @@ image_t *image_sub(image_t const *src, int x, int y, int w, int h,
// while transforming, which is especially useful on the VRAM.
//---
/* image_hflip(): Flip horizontally (supports in-place) */
void image_hflip(image_t const *src, image_t *dst);
/* image_hflip(): Flip horizontally
Formats: RGB16, P8
Size requirement: destination at least as large as source (no clipping)
Supports in-place: Yes */
void image_hflip(image_t const *src, image_t *dst, bool copy_alpha);
image_t *image_hflip_alloc(image_t const *src);
/* image_vflip(): Flip vertically (supports in-place) */
void image_vflip(image_t const *src, image_t *dst);
/* image_vflip(): Flip vertically
Formats: RGB16, P8
Size requirement: destination at least as large as source (no clipping)
Supports in-place: Yes */
void image_vflip(image_t const *src, image_t *dst, bool copy_alpha);
image_t *image_vflip_alloc(image_t const *src);
/* TODO: Geometric transforms */
/* image_linear(): Linear transformation
This function implements a generic linear transformation. This is a powerful
function that can perform any combination of rotation, mirroring and scaling
with nearest-neighbor sampling.
The [image_linear_opt] structure defines the settings for the transform.
Users familiar with linear algebra might want to use it directly, but they
are most conveniently generated with the rotation and scaling functions
listed below.
Formats: RGB16, P8
Size requirement: none (clipping through image_linear_opt settings)
Supports in-place: No */
struct image_linear_map {
/* The following parameters define the linear transformation as a mapping
from coordinates in the destination image (x and y) into coordinates in
the source image (u and v).
- (u, v) indicate where the top-left corner of the destination lands in
the source image.
- (dx_u, dx_v) indicate the source-image movement for each movement of
x += 1 in the destination.
- (dy_u, dy_v) indicate the source-image movement for each movement of
y += 1 in the destination.
All of these values are specified as 16:16 fixed-point, ie. they encode
decimal values by multiplying them by 65536. */
int u, v, dx_u, dx_v, dy_u, dy_v;
/* Dimensions of the source and destination */
int src_w, src_h, dst_w, dst_h;
};
void image_linear(image_t const *src, image_t *dst,
struct image_linear_map const *map);
image_t *image_linear_alloc(image_t const *src,
struct image_linear_map const *map);
/* image_scale(): Upscale or downscale an image
This function generates a linear map to be used in image_linear() to scale
the input image. The scaling factor gamma can be specified independently for
the x and y dimensions. It is expressed as 16:16 fixed-point; you can set
any decimal value multiplied by 65536, for instance 1.5*65536 to increase
the width and height by 50%. */
void image_scale(image_t const *src, int gamma_x, int gamma_y,
struct image_linear_map *map);
/* image_rotate(): Rotate an image around its center
This function generates a linear map to be used in image_linear() to perform
a rotation around the center of an image. If [resize=true], the target is
enlarged to make sure all the rotated pixels can be represented. This can
increase the final surface by a factor of up to 2. If the original image
doesn't extend to its corners, it is recommended to leave [resize=false] as
it noticeably affects performance. */
void image_rotate(image_t const *src, float angle, bool resize,
struct image_linear_map *map);
/* image_rotate_around(): Rotate an image around any point
This function generalizes image_rotate() by allowing rotations around any
center, even a point not within the image. The center is specified through
two coordinates (*center_x, *center_y). If the center is near the side of
the image, a normal rotation would move most of the pixels out of frame;
this function moves the frame to make sure the whole image remains visible.
*center_x and *center_y are updated to indicate the position of the center
of rotation within the new frame (the target image). */
void image_rotate_around(image_t const *src, float angle, bool resize,
int *center_x, int *center_y, struct image_linear_map *map);
/* image_rotate_around_scale(): Rotate an image around any point and scale it
This function generalizes image_rotate_around() by adding a scaling factor
to the transformation. The scaling factor gamma is expressed as 16:16
fixed-point. If [resize=true] the image is further extended to make sure no
parts are cut out, as in other rotation functions. */
void image_rotate_around_scale(
image_t const *src, float angle, int gamma,
bool resize, int *center_x, int *center_y,
struct image_linear_map *map);
//---
// Color transforms
@ -733,6 +819,10 @@ bool image_target(image_t const *src, image_t *dst, ...);
#define image_target_arg6(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_too_many_args(__VA_ARGS__))
/* image_alpha_2(): Conditional alpha */
#define image_alpha_2(fmt, copy_alpha) \
((copy_alpha) ? 0x10000 : image_alpha(fmt))
#endif /* FXCG50 */
#ifdef __cplusplus

29
src/image/fixed.h Normal file
View File

@ -0,0 +1,29 @@
//---
// gint:image:fixed - Minimal fixed-point interface for linear transformations
//---
#ifndef GINT_IMAGE_FIXED
#define GINT_IMAGE_FIXED
/* Constants */
#define fconst(x) ((x) * 65536)
/* Multiplication */
static inline int fmul(int x, int y)
{
return ((int64_t)x * (int64_t)y) >> 32;
}
/* Multiplication with a scalar */
static inline int fmuls(int x, int s)
{
return ((int64_t)x * (int64_t)s) >> 16;
}
/* Division */
static inline int fdiv(int x, int y)
{
return ((int64_t)x << 16) / y;
}
#endif /* GINT_IMAGE_FIXED */

View File

@ -16,8 +16,8 @@ void image_copy(image_t const *src, image_t *dst, bool copy_alpha)
void *src_px = src->data;
void *dst_px = dst->data;
int src_alpha = copy_alpha ? 0x10000 : image_alpha(src->format);
int dst_alpha = copy_alpha ? 0x10000 : image_alpha(dst->format);
int src_alpha = image_alpha_2(src->format, copy_alpha);
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
if(IMAGE_IS_RGB16(src->format) && IMAGE_IS_RGB16(dst->format)) {
do {

View File

@ -1,14 +1,14 @@
#include <gint/image.h>
void image_hflip(image_t const *src, image_t *dst)
void image_hflip(image_t const *src, image_t *dst, bool copy_alpha)
{
if(!image_target(src, dst, DATA_RW, SAME_DEPTH, SAME_SIZE))
return;
void *src_px = src->data;
void *dst_px = dst->data;
int src_alpha = image_alpha(src->format);
int dst_alpha = image_alpha(dst->format);
int src_alpha = image_alpha_2(src->format, copy_alpha);
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
int h = src->height;
if(IMAGE_IS_RGB16(src->format)) {

View File

@ -11,7 +11,6 @@ image_t *image_hflip_alloc(image_t const *src)
return NULL;
}
image_clear(dst);
image_hflip(src, dst);
image_hflip(src, dst, true);
return dst;
}

13
src/image/image_rotate.c Normal file
View File

@ -0,0 +1,13 @@
#include <gint/image.h>
void image_rotate(image_t const *src, float angle, bool resize,
struct image_linear_map *map)
{
if(!image_valid(src))
return;
int center_x = src->width / 2;
int center_y = src->height / 2;
image_rotate_around(src, angle, resize, &center_x, &center_y, map);
}

View File

@ -0,0 +1,9 @@
#include <gint/image.h>
#include "fixed.h"
void image_rotate_around(image_t const *src, float angle, bool resize,
int *center_x, int *center_y, struct image_linear_map *map)
{
image_rotate_around_scale(src, angle, fconst(1.0), resize, center_x,
center_y, map);
}

View File

@ -0,0 +1,16 @@
#include <gint/image.h>
#include "fixed.h"
void image_rotate_around_scale(image_t const *src, float angle, int gamma,
bool resize, int *center_x, int *center_y, struct image_linear_map *map)
{
if(!image_valid(src))
return;
map->src_w = src->width;
map->src_h = src->height;
/* Don't try to resize cleanly; just add a √2 factor in both dimensions if
[resize=true] to make sure everything fits */
;
}

24
src/image/image_scale.c Normal file
View File

@ -0,0 +1,24 @@
#include <gint/image.h>
#include "fixed.h"
void image_scale(image_t const *src, int gamma_x, int gamma_y,
struct image_linear_map *map)
{
if(!image_valid(src))
return;
int inv_gamma_x = fdiv(fconst(1), gamma_x);
int inv_gamma_y = fdiv(fconst(1), gamma_y);
map->u = fconst(0);
map->v = fconst(0);
map->dx_u = inv_gamma_x;
map->dx_v = 0;
map->dy_u = 0;
map->dy_v = inv_gamma_y;
map->src_w = src->width;
map->src_h = src->height;
map->dst_w = fmuls(src->width, gamma_x);
map->dst_h = fmuls(src->height, gamma_y);
}

View File

@ -21,7 +21,7 @@ static void copy_row_p8(int8_t *src, int8_t *dst, int src_alpha, int width)
}
}
void image_vflip(image_t const *src, image_t *dst)
void image_vflip(image_t const *src, image_t *dst, bool copy_alpha)
{
if(!image_target(src, dst, DATA_RW, SAME_DEPTH, SAME_SIZE))
return;
@ -32,8 +32,8 @@ void image_vflip(image_t const *src, image_t *dst)
void *dst_top = dst->data;
void *dst_bot = dst->data + (h - 1) * dst->stride;
int src_alpha = image_alpha(src->format);
int dst_alpha = image_alpha(dst->format);
int src_alpha = image_alpha_2(src->format, copy_alpha);
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
int row_length = src->stride;
void *row = malloc(row_length);

View File

@ -11,7 +11,6 @@ image_t *image_vflip_alloc(image_t const *src)
return NULL;
}
image_clear(dst);
image_vflip(src, dst);
image_vflip(src, dst, true);
return dst;
}