mirror of
https://github.com/godotengine/godot.git
synced 2024-11-28 15:12:33 +00:00
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) {
|
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(!_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(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
|
||||||
ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
|
ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels.");
|
||||||
|
|
||||||
int saved_width = height;
|
|
||||||
int saved_height = width;
|
|
||||||
|
|
||||||
if (width != height) {
|
|
||||||
int n = MAX(width, height);
|
|
||||||
resize(n, n, INTERPOLATE_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool used_mipmaps = has_mipmaps();
|
bool used_mipmaps = has_mipmaps();
|
||||||
if (used_mipmaps) {
|
if (used_mipmaps) {
|
||||||
clear_mipmaps();
|
clear_mipmaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In-place 90 degrees rotation by following the permutation cycles.
|
||||||
{
|
{
|
||||||
uint8_t *w = data.ptrw();
|
// Explanation by example (clockwise):
|
||||||
uint8_t src[16];
|
//
|
||||||
uint8_t dst[16];
|
// 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);
|
uint32_t pixel_size = get_format_pixel_size(format);
|
||||||
|
|
||||||
// Flip.
|
uint8_t single_pixel_buffer[16];
|
||||||
|
|
||||||
if (p_direction == CLOCKWISE) {
|
#define PREV_INDEX_IN_CYCLE(index) (p_direction == CLOCKWISE) ? ((h - 1 - (index % h)) * w + (index / h)) : ((index % h) * w + (w - 1 - (index / h)))
|
||||||
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);
|
|
||||||
|
|
||||||
_put_pixelb(x, height - y - 1, pixel_size, w, src);
|
if (w == h) { // Square case, 4-length cycles only (plus irrelevant thus skipped 1-length cycle in the middle for odd-sized squares).
|
||||||
_put_pixelb(x, y, pixel_size, w, dst);
|
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 {
|
} else { // Rectangular case (w != h), kinda unpredictable cycles.
|
||||||
for (int y = 0; y < height; y++) {
|
int permuted_pixels_count = 0;
|
||||||
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);
|
|
||||||
|
|
||||||
_put_pixelb(width - x - 1, y, pixel_size, w, src);
|
for (int i = 0; i < size; i++) {
|
||||||
_put_pixelb(x, y, pixel_size, w, dst);
|
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.
|
#undef PREV_INDEX_IN_CYCLE
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saved_width != saved_height) {
|
if (used_mipmaps) {
|
||||||
resize(saved_width, saved_height, INTERPOLATE_NEAREST);
|
|
||||||
} else if (used_mipmaps) {
|
|
||||||
generate_mipmaps();
|
generate_mipmaps();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::rotate_180() {
|
void Image::rotate_180() {
|
||||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
|
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(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
|
||||||
ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 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();
|
bool used_mipmaps = has_mipmaps();
|
||||||
if (used_mipmaps) {
|
if (used_mipmaps) {
|
||||||
@ -1420,19 +1468,21 @@ void Image::rotate_180() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
uint8_t *w = data.ptrw();
|
uint8_t *data_ptr = data.ptrw();
|
||||||
uint8_t src[16];
|
|
||||||
uint8_t dst[16];
|
|
||||||
uint32_t pixel_size = get_format_pixel_size(format);
|
uint32_t pixel_size = get_format_pixel_size(format);
|
||||||
|
|
||||||
for (int y = 0; y < height / 2; y++) {
|
uint8_t single_pixel_buffer[16];
|
||||||
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);
|
|
||||||
|
|
||||||
_put_pixelb(width - x - 1, height - y - 1, pixel_size, w, src);
|
uint8_t *from_begin_ptr = data_ptr;
|
||||||
_put_pixelb(x, y, pixel_size, w, dst);
|
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
Block a user