sprite.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #include "sprite.h"
  2. struct Sprite {
  3. uint8_t width;
  4. uint8_t height;
  5. uint8_t width_in_bytes;
  6. uint8_t* data;
  7. };
  8. #define TAG "Sprite"
  9. static void sprite_set_pixel(Sprite* sprite, uint8_t x, uint8_t y, bool color) {
  10. size_t p = y * sprite->width_in_bytes + x / 8;
  11. size_t b = x % 8;
  12. if(color) {
  13. sprite->data[p] |= 1 << b;
  14. } else {
  15. sprite->data[p] &= ~(1 << b);
  16. }
  17. }
  18. static bool sprite_load(Storage* storage, const char* bmp_path, Sprite* sprite) {
  19. bool result = false;
  20. File* file = storage_file_alloc(storage);
  21. do {
  22. if(!storage_file_open(file, bmp_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  23. FURI_LOG_E(TAG, "Failed to open file: %s", bmp_path);
  24. break;
  25. }
  26. // actually header is 54 bytes, but we only need first 38
  27. uint8_t header[38];
  28. if(!storage_file_read(file, header, sizeof(header))) {
  29. FURI_LOG_E(TAG, "Failed to read file header");
  30. break;
  31. }
  32. // check if it's a bmp file
  33. if(header[0] != 'B' || header[1] != 'M') {
  34. FURI_LOG_E(TAG, "Invalid BMP header");
  35. break;
  36. }
  37. // load bmp info
  38. const uint32_t data_offset = *(uint32_t*)&header[10];
  39. const uint32_t width = *(uint32_t*)&header[18];
  40. const uint32_t height = *(uint32_t*)&header[22];
  41. const uint16_t bpp = *(uint16_t*)&header[28];
  42. const uint32_t size = *(uint32_t*)&header[34];
  43. uint8_t* data = malloc(size);
  44. storage_file_seek(file, data_offset, true);
  45. if(!storage_file_read(file, data, size)) {
  46. FURI_LOG_E(TAG, "Failed to read file data");
  47. free(data);
  48. break;
  49. }
  50. sprite->width = width;
  51. sprite->height = height;
  52. sprite->width_in_bytes = width / 8 + (width % 8 ? 1 : 0);
  53. sprite->data = malloc(sprite->width_in_bytes * sprite->height);
  54. // convert bmp data to 1-bit xbm
  55. size_t p = 0;
  56. size_t x = 0;
  57. size_t y = 0;
  58. const size_t bpp_in_bytes = bpp / 8;
  59. while(p < size) {
  60. // sum all bytes in pixel
  61. uint32_t bmp_px = 0;
  62. for(size_t i = 0; i < bpp_in_bytes; i++) {
  63. bmp_px += data[p + i];
  64. }
  65. p += bpp_in_bytes;
  66. // if sum divided by bytes per pixel is less than 128, it's a black pixel
  67. bool color = bmp_px / (bpp_in_bytes) < 128;
  68. // set pixel
  69. sprite_set_pixel(sprite, x, height - y - 1, color);
  70. // advance to next pixel
  71. x++;
  72. // advance to next row
  73. if(x >= width) {
  74. // consider that bmp data is padded to 4 bytes
  75. size_t row_reminder = p % 4;
  76. if(row_reminder) {
  77. p += 4 - row_reminder;
  78. }
  79. x = 0;
  80. y++;
  81. }
  82. }
  83. free(data);
  84. result = true;
  85. } while(false);
  86. storage_file_free(file);
  87. return result;
  88. }
  89. Sprite* sprite_alloc() {
  90. Sprite* sprite = malloc(sizeof(Sprite));
  91. sprite->width = 0;
  92. sprite->height = 0;
  93. sprite->data = NULL;
  94. return sprite;
  95. }
  96. bool sprite_load_from_bmp(Sprite* sprite, Storage* storage, const char* bmp_path) {
  97. return sprite_load(storage, bmp_path, sprite);
  98. }
  99. void sprite_free(Sprite* sprite) {
  100. free(sprite->data);
  101. free(sprite);
  102. }
  103. size_t sprite_get_width(Sprite* sprite) {
  104. return sprite->width;
  105. }
  106. size_t sprite_get_height(Sprite* sprite) {
  107. return sprite->height;
  108. }
  109. void canvas_draw_sprite(Canvas* canvas, Sprite* sprite, int32_t x, int32_t y) {
  110. canvas_draw_xbm(canvas, x, y, sprite->width, sprite->height, sprite->data);
  111. }