summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
Diffstat (limited to 'core/io')
-rw-r--r--core/io/image.cpp156
-rw-r--r--core/io/pck_packer.cpp2
-rw-r--r--core/io/xml_parser.cpp69
-rw-r--r--core/io/xml_parser.h8
4 files changed, 157 insertions, 78 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 65addaf964..21146dd80c 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -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;
+
+ 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;
+ }
- _put_pixelb(width - x - 1, y, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
+ // 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;
}
- }
- }
- // Transpose.
+ // Save the in-cycle pixel with the smallest index (`i`).
+ memcpy(single_pixel_buffer, data_ptr + i * pixel_size, pixel_size);
- 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);
+ // 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++;
- _put_pixelb(y, x, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
+ 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;
}
+
+#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;
}
}
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index aa1b323db2..0118b4c6af 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -107,6 +107,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
}
Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) {
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
+
Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);
if (f.is_null()) {
return ERR_FILE_CANT_OPEN;
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index abae48fdd8..16b7f3a1f6 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -41,11 +41,11 @@ static inline bool _is_white_space(char c) {
}
//! sets the state that text was found. Returns true if set should be set
-bool XMLParser::_set_text(char *start, char *end) {
+bool XMLParser::_set_text(const char *start, const char *end) {
// check if text is more than 2 characters, and if not, check if there is
// only white space, so that this text won't be reported
if (end - start < 3) {
- char *p = start;
+ const char *p = start;
for (; p != end; ++p) {
if (!_is_white_space(*p)) {
break;
@@ -92,7 +92,7 @@ void XMLParser::_parse_closing_xml_element() {
void XMLParser::_ignore_definition() {
node_type = NODE_UNKNOWN;
- char *F = P;
+ const char *F = P;
// move until end marked with '>' reached
while (*P && *P != '>') {
next_char();
@@ -123,8 +123,8 @@ bool XMLParser::_parse_cdata() {
return true;
}
- char *cDataBegin = P;
- char *cDataEnd = nullptr;
+ const char *cDataBegin = P;
+ const char *cDataEnd = nullptr;
// find end of CDATA
while (*P && !cDataEnd) {
@@ -152,9 +152,9 @@ void XMLParser::_parse_comment() {
node_type = NODE_COMMENT;
P += 1;
- char *pEndOfInput = data + length;
- char *pCommentBegin;
- char *pCommentEnd;
+ const char *pEndOfInput = data + length;
+ const char *pCommentBegin;
+ const char *pCommentEnd;
if (P + 1 < pEndOfInput && P[0] == '-' && P[1] == '-') {
// Comment, use '-->' as end.
@@ -293,7 +293,7 @@ void XMLParser::_parse_opening_xml_element() {
}
void XMLParser::_parse_current_node() {
- char *start = P;
+ const char *start = P;
node_offset = P - data;
// more forward until '<' found
@@ -458,15 +458,36 @@ bool XMLParser::is_empty() const {
Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA);
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
length = p_buffer.size();
- data = memnew_arr(char, length + 1);
- memcpy(data, p_buffer.ptr(), length);
- data[length] = 0;
+ data_copy = memnew_arr(char, length + 1);
+ memcpy(data_copy, p_buffer.ptr(), length);
+ data_copy[length] = 0;
+ data = data_copy;
+ P = data;
+ current_line = 0;
+
+ return OK;
+}
+
+Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) {
+ ERR_FAIL_COND_V(p_size == 0, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!p_buffer, ERR_INVALID_DATA);
+
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
+ }
+
+ length = p_size;
+ data = (const char *)p_buffer;
P = data;
+ current_line = 0;
+
return OK;
}
@@ -479,13 +500,15 @@ Error XMLParser::open(const String &p_path) {
length = file->get_length();
ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT);
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
- data = memnew_arr(char, length + 1);
- file->get_buffer((uint8_t *)data, length);
- data[length] = 0;
+ data_copy = memnew_arr(char, length + 1);
+ file->get_buffer((uint8_t *)data_copy, length);
+ data_copy[length] = 0;
+ data = data_copy;
P = data;
current_line = 0;
@@ -512,8 +535,9 @@ void XMLParser::skip_section() {
}
void XMLParser::close() {
- if (data) {
+ if (data_copy) {
memdelete_arr(data);
+ data_copy = nullptr;
}
data = nullptr;
length = 0;
@@ -528,7 +552,8 @@ int XMLParser::get_current_line() const {
}
XMLParser::~XMLParser() {
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
}
diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h
index aea252ddc7..be44f771dc 100644
--- a/core/io/xml_parser.h
+++ b/core/io/xml_parser.h
@@ -65,8 +65,9 @@ public:
};
private:
- char *data = nullptr;
- char *P = nullptr;
+ char *data_copy = nullptr;
+ const char *data = nullptr;
+ const char *P = nullptr;
uint64_t length = 0;
uint64_t current_line = 0;
String node_name;
@@ -81,7 +82,7 @@ private:
Vector<Attribute> attributes;
- bool _set_text(char *start, char *end);
+ bool _set_text(const char *start, const char *end);
void _parse_closing_xml_element();
void _ignore_definition();
bool _parse_cdata();
@@ -118,6 +119,7 @@ public:
Error open(const String &p_path);
Error open_buffer(const Vector<uint8_t> &p_buffer);
+ Error _open_buffer(const uint8_t *p_buffer, size_t p_size);
void close();