`Image` Fix `rotate_90`/`rotate_180` methods
This commit is contained in:
parent
11abffbf12
commit
100b83971f
|
@ -1341,78 +1341,126 @@ void Image::crop(int p_width, int p_height) {
|
|||
|
||||
void Image::rotate_90(ClockDirection p_direction) {
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
|
||||
ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
|
||||
ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
|
||||
|
||||
int saved_width = height;
|
||||
int saved_height = width;
|
||||
|
||||
if (width != height) {
|
||||
int n = MAX(width, height);
|
||||
resize(n, n, INTERPOLATE_NEAREST);
|
||||
}
|
||||
ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
|
||||
ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels.");
|
||||
|
||||
bool used_mipmaps = has_mipmaps();
|
||||
if (used_mipmaps) {
|
||||
clear_mipmaps();
|
||||
}
|
||||
|
||||
// In-place 90 degrees rotation by following the permutation cycles.
|
||||
{
|
||||
uint8_t *w = data.ptrw();
|
||||
uint8_t src[16];
|
||||
uint8_t dst[16];
|
||||
// Explanation by example (clockwise):
|
||||
//
|
||||
// abc da
|
||||
// def -> eb
|
||||
// fc
|
||||
//
|
||||
// In memory:
|
||||
// 012345 012345
|
||||
// abcdef -> daebfc
|
||||
//
|
||||
// Permutation cycles:
|
||||
// (0 --a--> 1 --b--> 3 --d--> 0)
|
||||
// (2 --c--> 5 --f--> 4 --e--> 2)
|
||||
//
|
||||
// Applying cycles (backwards):
|
||||
// 0->s s=a (store)
|
||||
// 3->0 abcdef -> dbcdef
|
||||
// 1->3 dbcdef -> dbcbef
|
||||
// s->1 dbcbef -> dacbef
|
||||
//
|
||||
// 2->s s=c
|
||||
// 4->2 dacbef -> daebef
|
||||
// 5->4 daebef -> daebff
|
||||
// s->5 daebff -> daebfc
|
||||
|
||||
const int w = width;
|
||||
const int h = height;
|
||||
const int size = w * h;
|
||||
|
||||
uint8_t *data_ptr = data.ptrw();
|
||||
uint32_t pixel_size = get_format_pixel_size(format);
|
||||
|
||||
// Flip.
|
||||
uint8_t single_pixel_buffer[16];
|
||||
|
||||
if (p_direction == CLOCKWISE) {
|
||||
for (int y = 0; y < height / 2; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
_get_pixelb(x, y, pixel_size, w, src);
|
||||
_get_pixelb(x, height - y - 1, pixel_size, w, dst);
|
||||
#define PREV_INDEX_IN_CYCLE(index) (p_direction == CLOCKWISE) ? ((h - 1 - (index % h)) * w + (index / h)) : ((index % h) * w + (w - 1 - (index / h)))
|
||||
|
||||
_put_pixelb(x, height - y - 1, pixel_size, w, src);
|
||||
_put_pixelb(x, y, pixel_size, w, dst);
|
||||
if (w == h) { // Square case, 4-length cycles only (plus irrelevant thus skipped 1-length cycle in the middle for odd-sized squares).
|
||||
for (int y = 0; y < h / 2; y++) {
|
||||
for (int x = 0; x < (w + 1) / 2; x++) {
|
||||
int current = y * w + x;
|
||||
memcpy(single_pixel_buffer, data_ptr + current * pixel_size, pixel_size);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int prev = PREV_INDEX_IN_CYCLE(current);
|
||||
memcpy(data_ptr + current * pixel_size, data_ptr + prev * pixel_size, pixel_size);
|
||||
current = prev;
|
||||
}
|
||||
memcpy(data_ptr + current * pixel_size, single_pixel_buffer, pixel_size);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width / 2; x++) {
|
||||
_get_pixelb(x, y, pixel_size, w, src);
|
||||
_get_pixelb(width - x - 1, y, pixel_size, w, dst);
|
||||
} else { // Rectangular case (w != h), kinda unpredictable cycles.
|
||||
int permuted_pixels_count = 0;
|
||||
|
||||
_put_pixelb(width - x - 1, y, pixel_size, w, src);
|
||||
_put_pixelb(x, y, pixel_size, w, dst);
|
||||
for (int i = 0; i < size; i++) {
|
||||
int prev = PREV_INDEX_IN_CYCLE(i);
|
||||
if (prev == i) {
|
||||
// 1-length cycle, pixel remains at the same index.
|
||||
permuted_pixels_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check whether we already processed this cycle.
|
||||
// We iterate over it and if we'll find an index smaller than `i` then we already
|
||||
// processed this cycle because we always start at the smallest index in the cycle.
|
||||
// TODO: Improve this naive approach, can be done better.
|
||||
while (prev > i) {
|
||||
prev = PREV_INDEX_IN_CYCLE(prev);
|
||||
}
|
||||
if (prev < i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save the in-cycle pixel with the smallest index (`i`).
|
||||
memcpy(single_pixel_buffer, data_ptr + i * pixel_size, pixel_size);
|
||||
|
||||
// Overwrite pixels one by one by the preceding pixel in the cycle.
|
||||
int current = i;
|
||||
prev = PREV_INDEX_IN_CYCLE(current);
|
||||
while (prev != i) {
|
||||
memcpy(data_ptr + current * pixel_size, data_ptr + prev * pixel_size, pixel_size);
|
||||
permuted_pixels_count++;
|
||||
|
||||
current = prev;
|
||||
prev = PREV_INDEX_IN_CYCLE(current);
|
||||
};
|
||||
|
||||
// Overwrite the remaining pixel in the cycle by the saved pixel with the smallest index.
|
||||
memcpy(data_ptr + current * pixel_size, single_pixel_buffer, pixel_size);
|
||||
permuted_pixels_count++;
|
||||
|
||||
if (permuted_pixels_count == size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
width = h;
|
||||
height = w;
|
||||
}
|
||||
|
||||
// Transpose.
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (x < y) {
|
||||
_get_pixelb(x, y, pixel_size, w, src);
|
||||
_get_pixelb(y, x, pixel_size, w, dst);
|
||||
|
||||
_put_pixelb(y, x, pixel_size, w, src);
|
||||
_put_pixelb(x, y, pixel_size, w, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef PREV_INDEX_IN_CYCLE
|
||||
}
|
||||
|
||||
if (saved_width != saved_height) {
|
||||
resize(saved_width, saved_height, INTERPOLATE_NEAREST);
|
||||
} else if (used_mipmaps) {
|
||||
if (used_mipmaps) {
|
||||
generate_mipmaps();
|
||||
}
|
||||
}
|
||||
|
||||
void Image::rotate_180() {
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
|
||||
ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
|
||||
ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
|
||||
ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
|
||||
ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels.");
|
||||
|
||||
bool used_mipmaps = has_mipmaps();
|
||||
if (used_mipmaps) {
|
||||
|
@ -1420,19 +1468,21 @@ void Image::rotate_180() {
|
|||
}
|
||||
|
||||
{
|
||||
uint8_t *w = data.ptrw();
|
||||
uint8_t src[16];
|
||||
uint8_t dst[16];
|
||||
uint8_t *data_ptr = data.ptrw();
|
||||
uint32_t pixel_size = get_format_pixel_size(format);
|
||||
|
||||
for (int y = 0; y < height / 2; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
_get_pixelb(x, y, pixel_size, w, src);
|
||||
_get_pixelb(width - x - 1, height - y - 1, pixel_size, w, dst);
|
||||
uint8_t single_pixel_buffer[16];
|
||||
|
||||
_put_pixelb(width - x - 1, height - y - 1, pixel_size, w, src);
|
||||
_put_pixelb(x, y, pixel_size, w, dst);
|
||||
}
|
||||
uint8_t *from_begin_ptr = data_ptr;
|
||||
uint8_t *from_end_ptr = data_ptr + (width * height - 1) * pixel_size;
|
||||
|
||||
while (from_begin_ptr < from_end_ptr) {
|
||||
memcpy(single_pixel_buffer, from_begin_ptr, pixel_size);
|
||||
memcpy(from_begin_ptr, from_end_ptr, pixel_size);
|
||||
memcpy(from_end_ptr, single_pixel_buffer, pixel_size);
|
||||
|
||||
from_begin_ptr += pixel_size;
|
||||
from_end_ptr -= pixel_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue