diff --git a/CMakeLists.txt b/CMakeLists.txt index 2519a8f..15f04ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/include/gint/image.h b/include/gint/image.h index e6aad53..e1f4e39 100644 --- a/include/gint/image.h +++ b/include/gint/image.h @@ -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 diff --git a/src/image/fixed.h b/src/image/fixed.h new file mode 100644 index 0000000..b4c4474 --- /dev/null +++ b/src/image/fixed.h @@ -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 */ diff --git a/src/image/image_copy.c b/src/image/image_copy.c index eef220f..ca7b700 100644 --- a/src/image/image_copy.c +++ b/src/image/image_copy.c @@ -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 { diff --git a/src/image/image_hflip.c b/src/image/image_hflip.c index 4a540c1..b4f97dc 100644 --- a/src/image/image_hflip.c +++ b/src/image/image_hflip.c @@ -1,14 +1,14 @@ #include -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)) { diff --git a/src/image/image_hflip_alloc.c b/src/image/image_hflip_alloc.c index 6d9f0fc..cfe1e9f 100644 --- a/src/image/image_hflip_alloc.c +++ b/src/image/image_hflip_alloc.c @@ -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; } diff --git a/src/image/image_rotate.c b/src/image/image_rotate.c new file mode 100644 index 0000000..3d72478 --- /dev/null +++ b/src/image/image_rotate.c @@ -0,0 +1,13 @@ +#include + +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, ¢er_x, ¢er_y, map); +} diff --git a/src/image/image_rotate_around.c b/src/image/image_rotate_around.c new file mode 100644 index 0000000..bf554e5 --- /dev/null +++ b/src/image/image_rotate_around.c @@ -0,0 +1,9 @@ +#include +#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); +} diff --git a/src/image/image_rotate_around_scale.c b/src/image/image_rotate_around_scale.c new file mode 100644 index 0000000..65faf97 --- /dev/null +++ b/src/image/image_rotate_around_scale.c @@ -0,0 +1,16 @@ +#include +#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 */ + ; +} diff --git a/src/image/image_scale.c b/src/image/image_scale.c new file mode 100644 index 0000000..f038c57 --- /dev/null +++ b/src/image/image_scale.c @@ -0,0 +1,24 @@ +#include +#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); +} diff --git a/src/image/image_vflip.c b/src/image/image_vflip.c index 1c4f324..e56de77 100644 --- a/src/image/image_vflip.c +++ b/src/image/image_vflip.c @@ -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); diff --git a/src/image/image_vflip_alloc.c b/src/image/image_vflip_alloc.c index ef26c91..21442e5 100644 --- a/src/image/image_vflip_alloc.c +++ b/src/image/image_vflip_alloc.c @@ -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; }