|
|
@@ -1,129 +1,55 @@
|
|
|
#include "sprite.h"
|
|
|
+#include <storage/storage.h>
|
|
|
+
|
|
|
+#ifdef SPRITE_DEBUG
|
|
|
+#define SPRITE_D(...) FURI_LOG_D("Sprite", __VA_ARGS__)
|
|
|
+#define SPRITE_E(...) FURI_LOG_E("Sprite", __VA_ARGS__)
|
|
|
+#else
|
|
|
+#define SPRITE_D(...)
|
|
|
+#define SPRITE_E(...)
|
|
|
+#endif
|
|
|
|
|
|
struct Sprite {
|
|
|
- uint8_t width;
|
|
|
- uint8_t height;
|
|
|
- uint8_t width_in_bytes;
|
|
|
- uint8_t* data;
|
|
|
+ uint32_t width;
|
|
|
+ uint32_t height;
|
|
|
+ uint8_t data[];
|
|
|
};
|
|
|
|
|
|
-#define TAG "Sprite"
|
|
|
-
|
|
|
-static void sprite_set_pixel(Sprite* sprite, uint8_t x, uint8_t y, bool color) {
|
|
|
- size_t p = y * sprite->width_in_bytes + x / 8;
|
|
|
- size_t b = x % 8;
|
|
|
-
|
|
|
- if(color) {
|
|
|
- sprite->data[p] |= 1 << b;
|
|
|
- } else {
|
|
|
- sprite->data[p] &= ~(1 << b);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static bool sprite_load(Storage* storage, const char* bmp_path, Sprite* sprite) {
|
|
|
- bool result = false;
|
|
|
+Sprite* sprite_alloc(const char* path) {
|
|
|
+ Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
File* file = storage_file_alloc(storage);
|
|
|
+ Sprite* sprite = NULL;
|
|
|
|
|
|
do {
|
|
|
- if(!storage_file_open(file, bmp_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
|
- FURI_LOG_E(TAG, "Failed to open file: %s", bmp_path);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- // actually header is 54 bytes, but we only need first 38
|
|
|
- uint8_t header[38];
|
|
|
-
|
|
|
- if(!storage_file_read(file, header, sizeof(header))) {
|
|
|
- FURI_LOG_E(TAG, "Failed to read file header");
|
|
|
+ if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
|
|
+ SPRITE_E("Failed to open file: %s", path);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // check if it's a bmp file
|
|
|
- if(header[0] != 'B' || header[1] != 'M') {
|
|
|
- FURI_LOG_E(TAG, "Invalid BMP header");
|
|
|
+ uint32_t size = 0;
|
|
|
+ if(storage_file_read(file, &size, sizeof(size)) != sizeof(size)) {
|
|
|
+ SPRITE_E("Failed to read file size: %s", path);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // load bmp info
|
|
|
- const uint32_t data_offset = *(uint32_t*)&header[10];
|
|
|
- const uint32_t width = *(uint32_t*)&header[18];
|
|
|
- const uint32_t height = *(uint32_t*)&header[22];
|
|
|
- const uint16_t bpp = *(uint16_t*)&header[28];
|
|
|
- const uint32_t size = *(uint32_t*)&header[34];
|
|
|
-
|
|
|
- uint8_t* data = malloc(size);
|
|
|
- storage_file_seek(file, data_offset, true);
|
|
|
- if(!storage_file_read(file, data, size)) {
|
|
|
- FURI_LOG_E(TAG, "Failed to read file data");
|
|
|
- free(data);
|
|
|
+ sprite = malloc(size);
|
|
|
+ if(storage_file_read(file, sprite, size) != size) {
|
|
|
+ SPRITE_E("Failed to read file: %s", path);
|
|
|
+ free(sprite);
|
|
|
+ sprite = NULL;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- sprite->width = width;
|
|
|
- sprite->height = height;
|
|
|
- sprite->width_in_bytes = width / 8 + (width % 8 ? 1 : 0);
|
|
|
-
|
|
|
- sprite->data = malloc(sprite->width_in_bytes * sprite->height);
|
|
|
-
|
|
|
- // convert bmp data to 1-bit xbm
|
|
|
- size_t p = 0;
|
|
|
- size_t x = 0;
|
|
|
- size_t y = 0;
|
|
|
- const size_t bpp_in_bytes = bpp / 8;
|
|
|
-
|
|
|
- while(p < size) {
|
|
|
- // sum all bytes in pixel
|
|
|
- uint32_t bmp_px = 0;
|
|
|
- for(size_t i = 0; i < bpp_in_bytes; i++) {
|
|
|
- bmp_px += data[p + i];
|
|
|
- }
|
|
|
- p += bpp_in_bytes;
|
|
|
-
|
|
|
- // if sum divided by bytes per pixel is less than 128, it's a black pixel
|
|
|
- bool color = bmp_px / (bpp_in_bytes) < 128;
|
|
|
-
|
|
|
- // set pixel
|
|
|
- sprite_set_pixel(sprite, x, height - y - 1, color);
|
|
|
-
|
|
|
- // advance to next pixel
|
|
|
- x++;
|
|
|
-
|
|
|
- // advance to next row
|
|
|
- if(x >= width) {
|
|
|
- // consider that bmp data is padded to 4 bytes
|
|
|
- size_t row_reminder = p % 4;
|
|
|
- if(row_reminder) {
|
|
|
- p += 4 - row_reminder;
|
|
|
- }
|
|
|
-
|
|
|
- x = 0;
|
|
|
- y++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- free(data);
|
|
|
-
|
|
|
- result = true;
|
|
|
+ SPRITE_D(
|
|
|
+ "Loaded sprite: %s, width: %lu, height: %lu", path, sprite->width, sprite->height);
|
|
|
} while(false);
|
|
|
|
|
|
storage_file_free(file);
|
|
|
- return result;
|
|
|
-}
|
|
|
|
|
|
-Sprite* sprite_alloc() {
|
|
|
- Sprite* sprite = malloc(sizeof(Sprite));
|
|
|
- sprite->width = 0;
|
|
|
- sprite->height = 0;
|
|
|
- sprite->data = NULL;
|
|
|
return sprite;
|
|
|
}
|
|
|
|
|
|
-bool sprite_load_from_bmp(Sprite* sprite, Storage* storage, const char* bmp_path) {
|
|
|
- return sprite_load(storage, bmp_path, sprite);
|
|
|
-}
|
|
|
-
|
|
|
void sprite_free(Sprite* sprite) {
|
|
|
- free(sprite->data);
|
|
|
free(sprite);
|
|
|
}
|
|
|
|