Skip to content

Commit

Permalink
rdpq_tex_blit: fix various bugs with flipping and source rects
Browse files Browse the repository at this point in the history
When specifying a source rectangle (subset of the whole surface),
flipping was totally broken. The previous formulas did not account
for this case correctly, in none of the various code paths.

This was true for both flipping via the flip_x/flip_y variables,
and flipping via negative scale.

This patch reworks all the various coordinate formulas to correctly
fix these cases.
  • Loading branch information
rasky committed Nov 16, 2024
1 parent f49c816 commit a170948
Showing 1 changed file with 39 additions and 29 deletions.
68 changes: 39 additions & 29 deletions src/rdpq/rdpq_tex.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "rdpq_tex.h"
#include "rdpq_tex_internal.h"
#include "utils.h"
#include "fmath.h"
#include <math.h>

/** @brief Non-zero if we are doing a multi-texture upload */
Expand Down Expand Up @@ -469,7 +470,7 @@ static void ltd_texloader(rdpq_tile_t tile, const surface_t *tex, int s0, int t0
tex_loader_t tload = tex_loader_init(tile, tex);

// Calculate the optimal height for a strip, based on strips of maximum length.
int tile_h = tex_loader_calc_max_height(&tload, tex->width);
int tile_h = tex_loader_calc_max_height(&tload, s1 - s0);

// Go through the surface
while (t0 < t1)
Expand All @@ -496,24 +497,26 @@ static void tex_xblit_norotate_noscale(const surface_t *surf, float x0, float y0
rdpq_tile_t tile = parms->tile;
int src_width = parms->width ? parms->width : surf->width;
int src_height = parms->height ? parms->height : surf->height;
int s0 = parms->s0;
int t0 = parms->t0;
int cx = parms->cx + s0;
int cy = parms->cy + t0;
int os0 = parms->s0;
int ot0 = parms->t0;
int os1 = os0 + src_width;
int ot1 = ot0 + src_height;
bool flip_x = parms->flip_x;
bool flip_y = parms->flip_y;
x0 -= os0 + parms->cx;
y0 -= ot0 + parms->cy;

void draw_cb(rdpq_tile_t tile, int s0, int t0, int s1, int t1)
{
int ks0 = s0, kt0 = t0, ks1 = s1, kt1 = t1;

if (flip_x) { ks0 = src_width - s0 - 1; ks1 = src_width - s1 - 1; }
if (flip_y) { kt0 = src_height - t0 - 1; kt1 = src_height - t1 - 1; }
if (flip_x) { ks0 = os1 - s0 + os0 - 1; ks1 = os1 - s1 + os0 - 1; }
if (flip_y) { kt0 = ot1 - t0 + ot0 - 1; kt1 = ot1 - t1 + ot0 - 1; }

rdpq_texture_rectangle(tile, x0 + ks0 - cx, y0 + kt0 - cy, x0 + ks1 - cx, y0 + kt1 - cy, s0, t0);
rdpq_texture_rectangle(tile, x0 + ks0, y0 + kt0, x0 + ks1, y0 + kt1, s0, t0);
}

(*ltd)(tile, surf, s0, t0, s0 + src_width, t0 + src_height, draw_cb, parms->filtering);
(*ltd)(tile, surf, os0, ot0, os1, ot1, draw_cb, parms->filtering);
}

__attribute__((noinline))
Expand All @@ -522,14 +525,14 @@ static void tex_xblit_norotate(const surface_t *surf, float x0, float y0, const
rdpq_tile_t tile = parms->tile;
int src_width = parms->width ? parms->width : surf->width;
int src_height = parms->height ? parms->height : surf->height;
int s0 = parms->s0;
int t0 = parms->t0;
int cx = parms->cx + s0;
int cy = parms->cy + t0;
int os0 = parms->s0;
int ot0 = parms->t0;
int os1 = os0 + src_width;
int ot1 = ot0 + src_height;
int cx = parms->cx + os0;
int cy = parms->cy + ot0;
float scalex = parms->scale_x == 0 ? 1.0f : parms->scale_x;
float scaley = parms->scale_y == 0 ? 1.0f : parms->scale_y;
bool flip_x = (scalex < 0) ^ parms->flip_x;
bool flip_y = (scaley < 0) ^ parms->flip_y;

float mtx[3][2] = {
{ scalex, 0 },
Expand All @@ -542,8 +545,8 @@ static void tex_xblit_norotate(const surface_t *surf, float x0, float y0, const
{
int ks0 = s0, kt0 = t0, ks1 = s1, kt1 = t1;

if (flip_x) { ks0 = src_width - s0 - 1; ks1 = src_width - s1 - 1; }
if (flip_y) { kt0 = src_height - t0 - 1; kt1 = src_height - t1 - 1; }
if (parms->flip_x) { ks0 = os1 - s0 + os0 - 1; ks1 = os1 - s1 + os0 - 1; }
if (parms->flip_y) { kt0 = ot1 - t0 + ot0 - 1; kt1 = ot1 - t1 + ot0 - 1; }

float k0x = mtx[0][0] * ks0 + mtx[1][0] * kt0 + mtx[2][0];
float k0y = mtx[0][1] * ks0 + mtx[1][1] * kt0 + mtx[2][1];
Expand All @@ -553,7 +556,7 @@ static void tex_xblit_norotate(const surface_t *surf, float x0, float y0, const
rdpq_texture_rectangle_scaled(tile, k0x, k0y, k2x, k2y, s0, t0, s1, t1);
}

(*ltd)(tile, surf, s0, t0, s0 + src_width, t0 + src_height, draw_cb, parms->filtering);
(*ltd)(tile, surf, os0, ot0, os1, ot1, draw_cb, parms->filtering);
}

__attribute__((noinline))
Expand All @@ -562,17 +565,24 @@ static void tex_xblit(const surface_t *surf, float x0, float y0, const rdpq_blit
rdpq_tile_t tile = parms->tile;
int src_width = parms->width ? parms->width : surf->width;
int src_height = parms->height ? parms->height : surf->height;
int s0 = parms->s0;
int t0 = parms->t0;
int cx = parms->cx + s0;
int cy = parms->cy + t0;
int os0 = parms->s0;
int ot0 = parms->t0;
int os1 = os0 + src_width;
int ot1 = ot0 + src_height;
int cx = parms->cx + os0;
int cy = parms->cy + ot0;
int nx = parms->nx;
int ny = parms->ny;
float scalex = parms->scale_x == 0 ? 1.0f : parms->scale_x;
float scaley = parms->scale_y == 0 ? 1.0f : parms->scale_y;
bool flip_x = parms->flip_x;
bool flip_y = parms->flip_y;

if (scalex < 0) { flip_x = !flip_x; scalex = -scalex; }
if (scaley < 0) { flip_y = !flip_y; scaley = -scaley; }

float sin_theta, cos_theta;
sincosf(parms->theta, &sin_theta, &cos_theta);
fm_sincosf(parms->theta, &sin_theta, &cos_theta);

float mtx[3][2] = {
{ cos_theta * scalex, -sin_theta * scaley },
Expand All @@ -585,8 +595,8 @@ static void tex_xblit(const surface_t *surf, float x0, float y0, const rdpq_blit
{
int ks0 = s0, kt0 = t0, ks1 = s1, kt1 = t1;

if (parms->flip_x) { ks0 = src_width - ks0; ks1 = src_width - ks1; }
if (parms->flip_y) { kt0 = src_height - kt0; kt1 = src_height - kt1; }
if (flip_x) { ks0 = os1 - ks0 + os0; ks1 = os1 - ks1 + os0; }
if (flip_y) { kt0 = ot1 - kt0 + ot0; kt1 = ot1 - kt1 + ot0; }

float k0x = mtx[0][0] * ks0 + mtx[1][0] * kt0 + mtx[2][0];
float k0y = mtx[0][1] * ks0 + mtx[1][1] * kt0 + mtx[2][1];
Expand All @@ -608,8 +618,8 @@ static void tex_xblit(const surface_t *surf, float x0, float y0, const rdpq_blit
void draw_cb_multi_rot(rdpq_tile_t tile, int s0, int t0, int s1, int t1)
{
int ks0 = s0, kt0 = t0, ks1 = s1, kt1 = t1;
if (parms->flip_x) { ks0 = src_width - ks0; ks1 = src_width - ks1; }
if (parms->flip_y) { kt0 = src_height - kt0; kt1 = src_height - kt1; }
if (flip_x) { ks0 = os1 - ks0 + os0; ks1 = os1 - ks1 + os0; }
if (flip_y) { kt0 = ot1 - kt0 + ot0; kt1 = ot1 - kt1 + ot0; }

assert(s1-s0 == src_width);

Expand Down Expand Up @@ -647,9 +657,9 @@ static void tex_xblit(const surface_t *surf, float x0, float y0, const rdpq_blit
}

if (nx || ny) {
(*ltd)(tile, surf, s0, t0, s0 + src_width, t0 + src_height, draw_cb_multi_rot, parms->filtering);
(*ltd)(tile, surf, os0, ot0, os1, ot1, draw_cb_multi_rot, parms->filtering);
} else {
(*ltd)(tile, surf, s0, t0, s0 + src_width, t0 + src_height, draw_cb, parms->filtering);
(*ltd)(tile, surf, os0, ot0, os1, ot1, draw_cb, parms->filtering);
}
}

Expand Down

0 comments on commit a170948

Please sign in to comment.