display.h 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include <gui/gui.h>
  2. #include <furi_hal.h>
  3. #include "constants.h"
  4. #include <doom_icons.h>
  5. #include "assets.h"
  6. #define CHECK_BIT(var, pos) ((var) & (1 << (pos)))
  7. static const uint8_t bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1};
  8. #define pgm_read_byte(addr) (*(const unsigned char*)(addr))
  9. #define read_bit(b, n) b& pgm_read_byte(bit_mask + n) ? 1 : 0
  10. //#define read_bit(byte, index) (((unsigned)(byte) >> (index)) & 1)
  11. void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas);
  12. void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas);
  13. void drawSprite(
  14. int8_t x,
  15. int8_t y,
  16. const uint8_t* bitmap,
  17. const uint8_t* bitmap_mask,
  18. int16_t w,
  19. int16_t h,
  20. uint8_t sprite,
  21. double distance,
  22. Canvas* const canvas);
  23. void drawBitmap(
  24. int16_t x,
  25. int16_t y,
  26. const Icon* i,
  27. int16_t w,
  28. int16_t h,
  29. uint16_t color,
  30. Canvas* const canvas);
  31. void drawTextSpace(int8_t x, int8_t y, char* txt, uint8_t space, Canvas* const canvas);
  32. void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas);
  33. void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas);
  34. void drawGun(
  35. int16_t x,
  36. int16_t y,
  37. const uint8_t* bitmap,
  38. int16_t w,
  39. int16_t h,
  40. uint16_t color,
  41. Canvas* const canvas);
  42. void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas);
  43. void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas);
  44. void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas);
  45. bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i);
  46. double getActualFps();
  47. void fps();
  48. uint8_t reverse_bits(uint8_t num);
  49. // FPS control
  50. double delta = 1;
  51. uint32_t lastFrameTime = 0;
  52. uint8_t zbuffer[128]; /// 128 = screen width & REMOVE WHEN DISPLAY.H IMPLEMENTED
  53. void drawGun(
  54. int16_t x,
  55. int16_t y,
  56. const uint8_t* bitmap,
  57. int16_t w,
  58. int16_t h,
  59. uint16_t color,
  60. Canvas* const canvas) {
  61. int16_t byteWidth = (w + 7) / 8;
  62. uint8_t byte = 0;
  63. for(int16_t j = 0; j < h; j++, y++) {
  64. for(int16_t i = 0; i < w; i++) {
  65. if(i & 7)
  66. byte <<= 1;
  67. else
  68. byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
  69. if(byte & 0x80) drawPixel(x + i, y, color, false, canvas);
  70. }
  71. }
  72. }
  73. void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas) {
  74. UNUSED(intensity);
  75. uint8_t dots = end_y - start_y;
  76. for(int i = 0; i < dots; i++) {
  77. canvas_draw_dot(canvas, x, start_y + i);
  78. }
  79. }
  80. void drawBitmap(
  81. int16_t x,
  82. int16_t y,
  83. const Icon* i,
  84. int16_t w,
  85. int16_t h,
  86. uint16_t color,
  87. Canvas* const canvas) {
  88. UNUSED(w);
  89. UNUSED(h);
  90. if(!color) {
  91. canvas_invert_color(canvas);
  92. }
  93. canvas_draw_icon(canvas, x, y, i);
  94. if(!color) {
  95. canvas_invert_color(canvas);
  96. }
  97. }
  98. void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas) {
  99. char buf[4];
  100. snprintf(buf, 4, "%d", num);
  101. drawTextSpace(x, y, buf, 1, canvas);
  102. }
  103. void drawTextSpace(int8_t x, int8_t y, char* txt, uint8_t space, Canvas* const canvas) {
  104. uint8_t pos = x;
  105. uint8_t i = 0;
  106. char ch;
  107. while((ch = txt[i]) != '\0') {
  108. drawChar(pos, y, ch, canvas);
  109. i++;
  110. pos += UICHAR_WIDTH + space;
  111. // shortcut on end of screen
  112. if(pos > SCREEN_WIDTH) return;
  113. }
  114. }
  115. // Custom drawBitmap method with scale support, mask, zindex and pattern filling
  116. void drawSprite(
  117. int8_t x,
  118. int8_t y,
  119. const uint8_t* bitmap,
  120. const uint8_t* bitmap_mask,
  121. int16_t w,
  122. int16_t h,
  123. uint8_t sprite,
  124. double distance,
  125. Canvas* const canvas) {
  126. uint8_t tw = (double)w / distance;
  127. uint8_t th = (double)h / distance;
  128. uint8_t byte_width = w / 8;
  129. uint8_t pixel_size = fmax(1, (double)1.0 / (double)distance);
  130. uint16_t sprite_offset = byte_width * h * sprite;
  131. bool pixel;
  132. bool maskPixel;
  133. // Don't draw the whole sprite if the anchor is hidden by z buffer
  134. // Not checked per pixel for performance reasons
  135. if(zbuffer[(int)(fmin(fmax(x, 0), ZBUFFER_SIZE - 1) / Z_RES_DIVIDER)] <
  136. distance * DISTANCE_MULTIPLIER) {
  137. return;
  138. }
  139. for(uint8_t ty = 0; ty < th; ty += pixel_size) {
  140. // Don't draw out of screen
  141. if(y + ty < 0 || y + ty >= RENDER_HEIGHT) {
  142. continue;
  143. }
  144. uint8_t sy = ty * distance; // The y from the sprite
  145. for(uint8_t tx = 0; tx < tw; tx += pixel_size) {
  146. uint8_t sx = tx * distance; // The x from the sprite
  147. uint16_t byte_offset = sprite_offset + sy * byte_width + sx / 8;
  148. // Don't draw out of screen
  149. if(x + tx < 0 || x + tx >= SCREEN_WIDTH) {
  150. continue;
  151. }
  152. maskPixel = read_bit(pgm_read_byte(bitmap_mask + byte_offset), sx % 8);
  153. if(maskPixel) {
  154. pixel = read_bit(pgm_read_byte(bitmap + byte_offset), sx % 8);
  155. for(uint8_t ox = 0; ox < pixel_size; ox++) {
  156. for(uint8_t oy = 0; oy < pixel_size; oy++) {
  157. if(bitmap == imp_inv)
  158. drawPixel(x + tx + ox, y + ty + oy, 1, true, canvas);
  159. else
  160. drawPixel(x + tx + ox, y + ty + oy, pixel, true, canvas);
  161. }
  162. }
  163. }
  164. }
  165. }
  166. }
  167. void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas) {
  168. if(x < 0 || x >= SCREEN_WIDTH || y < 0 ||
  169. y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) {
  170. return;
  171. }
  172. if(color)
  173. canvas_draw_dot(canvas, x, y);
  174. else {
  175. canvas_invert_color(canvas);
  176. canvas_draw_dot(canvas, x, y);
  177. canvas_invert_color(canvas);
  178. }
  179. }
  180. void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas) {
  181. uint8_t lsb;
  182. uint8_t c = 0;
  183. while(CHAR_MAP[c] != ch && CHAR_MAP[c] != '\0') c++;
  184. for(uint8_t i = 0; i < 6; i++) {
  185. //lsb = (char_arr[c][i] >> 4);
  186. lsb = reverse_bits(char_arr[c][i]);
  187. for(uint8_t n = 0; n < 4; n++) {
  188. if(CHECK_BIT(lsb, n)) {
  189. drawPixel(x + n, y + i, true, false, canvas);
  190. }
  191. }
  192. }
  193. }
  194. void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas) {
  195. canvas_invert_color(canvas);
  196. for(int i = 0; i < w; i++) {
  197. for(int j = 0; j < h; j++) {
  198. canvas_draw_dot(canvas, x + i, y + j);
  199. }
  200. }
  201. canvas_invert_color(canvas);
  202. }
  203. void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas) {
  204. for(int i = 0; i < w; i++) {
  205. for(int j = 0; j < h; j++) {
  206. canvas_draw_dot(canvas, x + i, y + j);
  207. }
  208. }
  209. }
  210. bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i) {
  211. if(i == 0) return 0;
  212. if(i >= GRADIENT_COUNT - 1) return 1;
  213. uint8_t index =
  214. fmax(0, fmin(GRADIENT_COUNT - 1, i)) * GRADIENT_WIDTH * GRADIENT_HEIGHT // gradient index
  215. + y * GRADIENT_WIDTH % (GRADIENT_WIDTH * GRADIENT_HEIGHT) // y byte offset
  216. + x / GRADIENT_HEIGHT % GRADIENT_WIDTH; // x byte offset
  217. //uint8_t *gradient_data = NULL;
  218. //furi_hal_compress_icon_decode(icon_get_data(&I_gradient_inv), &gradient_data);
  219. // return the bit based on x
  220. return read_bit(pgm_read_byte(gradient + index), x % 8);
  221. }
  222. void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas) {
  223. for(uint8_t x = 0; x < SCREEN_WIDTH; x++) {
  224. for(uint8_t y = 0; y < SCREEN_HEIGHT; y++) {
  225. if(getGradientPixel(x, y, intensity)) drawPixel(x, y, color, false, canvas);
  226. }
  227. }
  228. }
  229. // Adds a delay to limit play to specified fps
  230. // Calculates also delta to keep movement consistent in lower framerates
  231. void fps() {
  232. while(furi_get_tick() - lastFrameTime < FRAME_TIME)
  233. ;
  234. delta = (double)(furi_get_tick() - lastFrameTime) / (double)FRAME_TIME;
  235. lastFrameTime = furi_get_tick();
  236. }
  237. double getActualFps() {
  238. return 1000 / ((double)FRAME_TIME * (double)delta);
  239. }
  240. uint8_t reverse_bits(uint8_t num) {
  241. unsigned int NO_OF_BITS = sizeof(num) * 8;
  242. uint8_t reverse_num = 0;
  243. uint8_t i;
  244. for(i = 0; i < NO_OF_BITS; i++) {
  245. if((num & (1 << i))) reverse_num |= 1 << ((NO_OF_BITS - 1) - i);
  246. }
  247. return reverse_num;
  248. }