Firmware for MNT ZZ9000 graphics and ARM coprocessor card for Amiga computers.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gfx.c 28KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. /*
  2. * MNT ZZ9000 Amiga Graphics and Coprocessor Card Operating System (ZZ9000OS)
  3. *
  4. * Copyright (C) 2019, Lukas F. Hartmann <lukas@mntre.com>
  5. * MNT Research GmbH, Berlin
  6. * https://mntre.com
  7. *
  8. * More Info: https://mntre.com/zz9000
  9. *
  10. * SPDX-License-Identifier: GPL-3.0-or-later
  11. * GNU General Public License v3.0 or later
  12. *
  13. * https://spdx.org/licenses/GPL-3.0-or-later.html
  14. *
  15. */
  16. #include <stdint.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <math.h>
  21. #include "gfx.h"
  22. static uint32_t* fb=0;
  23. static uint32_t fb_pitch=0;
  24. void set_fb(uint32_t* fb_, uint32_t pitch) {
  25. fb=fb_;
  26. fb_pitch=pitch;
  27. }
  28. extern uint32_t* sprite_buf;
  29. void video_formatter_write(uint32_t data, uint16_t op);
  30. void update_hw_sprite(uint8_t *data, uint32_t *colors, uint16_t w, uint16_t h)
  31. {
  32. uint8_t cur_bit = 0x80;
  33. uint8_t cur_color = 0, out_pos = 0, iter_offset = 0;
  34. uint8_t line_pitch = (w / 8) * 2;
  35. uint8_t cur_bytes[8];
  36. for (uint8_t y_line = 0; y_line < h; y_line++) {
  37. if (w <= 16) {
  38. cur_bytes[0] = data[y_line * line_pitch];
  39. cur_bytes[1] = data[(y_line * line_pitch) + 2];
  40. cur_bytes[2] = data[(y_line * line_pitch) + 1];
  41. cur_bytes[3] = data[(y_line * line_pitch) + 3];
  42. }
  43. else {
  44. cur_bytes[0] = data[y_line * line_pitch];
  45. cur_bytes[1] = data[(y_line * line_pitch) + 4];
  46. cur_bytes[2] = data[(y_line * line_pitch) + 1];
  47. cur_bytes[3] = data[(y_line * line_pitch) + 5];
  48. cur_bytes[4] = data[(y_line * line_pitch) + 2];
  49. cur_bytes[5] = data[(y_line * line_pitch) + 6];
  50. cur_bytes[6] = data[(y_line * line_pitch) + 3];
  51. cur_bytes[7] = data[(y_line * line_pitch) + 7];
  52. }
  53. while(out_pos < 8) {
  54. for (uint8_t i = 0; i < line_pitch; i += 2) {
  55. cur_color = (cur_bytes[i] & cur_bit) ? 1 : 0;
  56. if (cur_bytes[i + 1] & cur_bit) cur_color += 2;
  57. sprite_buf[(y_line * 32) + out_pos + iter_offset] = colors[cur_color] & 0x00ffffff;
  58. video_formatter_write(((y_line * 32) + out_pos + iter_offset), 14);
  59. video_formatter_write(colors[cur_color] & 0x00ffffff, 15);
  60. iter_offset += 8;
  61. }
  62. out_pos++;
  63. cur_bit >>= 1;
  64. iter_offset = 0;
  65. }
  66. cur_bit = 0x80;
  67. out_pos = 0;
  68. }
  69. }
  70. void clip_hw_sprite(int16_t offset_x, int16_t offset_y)
  71. {
  72. uint16_t xo = 0, yo = 0;
  73. if (offset_x < 0)
  74. xo = -offset_x;
  75. if (offset_y < 0)
  76. yo = -offset_y;
  77. for (int y = 0; y < 48; y++) {
  78. for (int x = 0; x < 32; x++) {
  79. video_formatter_write((y * 32) + x, 14);
  80. if (x < 32 - xo && y < 48 - yo)
  81. video_formatter_write(sprite_buf[((y + yo) * 32) + (x + xo)] & 0x00ffffff, 15);
  82. else
  83. video_formatter_write(0x00ff00ff, 15);
  84. }
  85. }
  86. }
  87. void clear_hw_sprite()
  88. {
  89. for (uint16_t i = 0; i < 32 * 48; i++) {
  90. sprite_buf[i] = 0x00ff00ff;
  91. video_formatter_write(i, 14);
  92. video_formatter_write(0xff00ff, 15);
  93. }
  94. }
  95. void horizline(uint16_t x1, uint16_t x2, uint16_t y, uint32_t color) {
  96. uint32_t* p=fb+y*fb_pitch;
  97. uint16_t tmp;
  98. if (x2>x1) {
  99. tmp=x1; x1=x2; x2=tmp;
  100. }
  101. while (x1>x2) {
  102. p[x1--]=color;
  103. }
  104. }
  105. void fill_rect(uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h, uint32_t fg_color, uint32_t color_format, uint8_t mask)
  106. {
  107. uint32_t* dp = fb + (rect_y1 * fb_pitch);
  108. uint8_t u8_fg = fg_color >> 24;
  109. uint16_t rect_y2 = rect_y1 + h, rect_x2 = rect_x1 + w;
  110. uint16_t x;
  111. for (uint16_t cur_y = rect_y1; cur_y < rect_y2; cur_y++) {
  112. x = rect_x1;
  113. switch(color_format) {
  114. case MNTVA_COLOR_8BIT:
  115. while(x < rect_x2) {
  116. SET_FG_PIXEL8_MASK(0);
  117. x++;
  118. }
  119. break;
  120. case MNTVA_COLOR_32BIT:
  121. case MNTVA_COLOR_16BIT565:
  122. while(x < rect_x2) {
  123. // The mask isn't used at all for 16/32-bit
  124. SET_FG_PIXEL;
  125. x++;
  126. }
  127. break;
  128. default:
  129. // Unknown/unhandled color format.
  130. break;
  131. }
  132. dp += fb_pitch;
  133. }
  134. }
  135. void fill_rect_solid(uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h, uint32_t rect_rgb, uint32_t color_format)
  136. {
  137. uint32_t* p = fb + (rect_y1 * fb_pitch);
  138. uint16_t* p16;
  139. uint16_t rect_y2 = rect_y1 + h, rect_x2 = rect_x1 + w;
  140. uint16_t x;
  141. for (uint16_t cur_y = rect_y1; cur_y < rect_y2; cur_y++) {
  142. switch(color_format) {
  143. case MNTVA_COLOR_8BIT:
  144. memset((uint8_t *)p + rect_x1, (uint8_t)(rect_rgb >> 24), w);
  145. break;
  146. case MNTVA_COLOR_16BIT565:
  147. x = rect_x1;
  148. p16 = (uint16_t *)p;
  149. while(x < rect_x2) {
  150. p16[x++] = rect_rgb;
  151. }
  152. break;
  153. case MNTVA_COLOR_32BIT:
  154. x = rect_x1;
  155. while(x < rect_x2) {
  156. p[x++] = rect_rgb;
  157. }
  158. break;
  159. default:
  160. // Unknown/unhandled color format.
  161. break;
  162. }
  163. p += fb_pitch;
  164. }
  165. }
  166. void invert_rect(uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h, uint8_t mask, uint32_t color_format)
  167. {
  168. uint32_t* dp = fb + (rect_y1 * fb_pitch);
  169. uint16_t x;
  170. uint16_t rect_y2 = rect_y1 + h, rect_x2 = rect_x1 + w;
  171. for (uint16_t cur_y = rect_y1; cur_y < rect_y2; cur_y++) {
  172. x = rect_x1;
  173. while (x < rect_x2) {
  174. INVERT_PIXEL;
  175. x++;
  176. }
  177. dp += fb_pitch;
  178. }
  179. }
  180. void fill_rect32(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint32_t rect_rgb) {
  181. for (uint16_t y=rect_y1; y<=rect_y2; y++) {
  182. uint32_t* p=fb+y*fb_pitch;
  183. for (uint16_t x=rect_x1; x<=rect_x2; x++) {
  184. p[x]=rect_rgb;
  185. }
  186. }
  187. }
  188. void fill_rect8(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint8_t rect_rgb) {
  189. for (uint16_t y=rect_y1; y<=rect_y2; y++) {
  190. uint8_t* p=(uint8_t*)(fb+y*fb_pitch);
  191. //for (uint16_t x=rect_x1; x<=rect_x2; x++) {
  192. // p[x]=rect_rgb;
  193. //}
  194. memset(p+rect_x1,rect_rgb,rect_x2-rect_x1+1);
  195. }
  196. }
  197. void fill_rect16(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint16_t rect_rgb) {
  198. for (uint16_t y=rect_y1; y<=rect_y2; y++) {
  199. uint16_t* p=(uint16_t*)(fb+y*fb_pitch);
  200. for (uint16_t x=rect_x1; x<=rect_x2; x++) {
  201. p[x]=rect_rgb;
  202. }
  203. }
  204. }
  205. void copy_rect_nomask(uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h, uint16_t rect_sx, uint16_t rect_sy, uint32_t color_format, uint32_t* sp_src, uint32_t src_pitch, uint8_t draw_mode)
  206. {
  207. uint32_t* dp = fb + (rect_y1 * fb_pitch);
  208. uint32_t* sp = sp_src + (rect_sy * src_pitch);
  209. uint16_t rect_y2 = rect_y1 + h - 1;
  210. uint8_t mask = 0xFF; // Perform mask handling, just in case we get a FillRectComplete at some point.
  211. uint32_t color_mask = 0x00FFFFFF;
  212. uint8_t u8_fg = 0;
  213. uint32_t fg_color = 0;
  214. int32_t line_step_d = fb_pitch, line_step_s = src_pitch;
  215. int8_t x_reverse = 0;
  216. if (rect_sy < rect_y1) {
  217. line_step_d = -fb_pitch;
  218. dp = fb + (rect_y2 * fb_pitch);
  219. line_step_s = -src_pitch;
  220. sp = sp_src + ((rect_sy + h - 1) * src_pitch);
  221. }
  222. if (rect_sx < rect_x1) {
  223. x_reverse = 1;
  224. }
  225. if (draw_mode == MINTERM_SRC) {
  226. for (uint16_t y_line = 0; y_line < h; y_line++) {
  227. switch(color_format) {
  228. case MNTVA_COLOR_8BIT:
  229. if (!x_reverse)
  230. memcpy((uint8_t *)dp + rect_x1, (uint8_t *)sp + rect_sx, w);
  231. else
  232. memmove((uint8_t *)dp + rect_x1, (uint8_t *)sp + rect_sx, w);
  233. break;
  234. case MNTVA_COLOR_16BIT565:
  235. if (!x_reverse)
  236. memcpy((uint16_t *)dp + rect_x1, (uint16_t *)sp + rect_sx, w * 2);
  237. else
  238. memmove((uint16_t *)dp + rect_x1, (uint16_t *)sp + rect_sx, w * 2);
  239. break;
  240. case MNTVA_COLOR_32BIT:
  241. if (!x_reverse)
  242. memcpy(dp + rect_x1, sp + rect_sx, w * 4);
  243. else
  244. memmove(dp + rect_x1, sp + rect_sx, w * 4);
  245. break;
  246. }
  247. dp += line_step_d;
  248. sp += line_step_s;
  249. }
  250. }
  251. else {
  252. for (uint16_t y_line = 0; y_line < h; y_line++) {
  253. if (x_reverse) {
  254. for (int16_t x = w; x >= 0; x--) {
  255. if (color_format == MNTVA_COLOR_8BIT) {
  256. u8_fg = ((uint8_t *)sp)[rect_sx + x];
  257. HANDLE_MINTERM_PIXEL_8(u8_fg, ((uint8_t *)dp)[rect_x1 + x]);
  258. }
  259. else {
  260. if (color_format == MNTVA_COLOR_16BIT565)
  261. fg_color = ((uint16_t *)sp)[rect_sx + x];
  262. else
  263. fg_color = sp[rect_sx + x];
  264. HANDLE_MINTERM_PIXEL_16_32(fg_color, dp);
  265. }
  266. }
  267. }
  268. else {
  269. for (int16_t x = 0; x < w; x++) {
  270. if (color_format == MNTVA_COLOR_8BIT) {
  271. u8_fg = ((uint8_t *)sp)[rect_sx + x];
  272. HANDLE_MINTERM_PIXEL_8(u8_fg, ((uint8_t *)dp)[rect_x1 + x]);
  273. }
  274. else {
  275. if (color_format == MNTVA_COLOR_16BIT565)
  276. fg_color = ((uint16_t *)sp)[rect_sx + x];
  277. else
  278. fg_color = sp[rect_sx + x];
  279. HANDLE_MINTERM_PIXEL_16_32(fg_color, dp);
  280. }
  281. }
  282. }
  283. dp += line_step_d;
  284. sp += line_step_s;
  285. }
  286. }
  287. }
  288. void copy_rect(uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h, uint16_t rect_sx, uint16_t rect_sy, uint32_t color_format, uint32_t* sp_src, uint32_t src_pitch, uint8_t mask)
  289. {
  290. uint32_t* dp = fb + (rect_y1 * fb_pitch);
  291. uint32_t* sp = sp_src + (rect_sy * src_pitch);
  292. uint16_t rect_y2 = rect_y1 + h - 1;//, rect_x2 = rect_x1 + h - 1;
  293. int32_t line_step_d = fb_pitch, line_step_s = src_pitch;
  294. int8_t x_reverse = 0;
  295. if (rect_sy < rect_y1) {
  296. line_step_d = -fb_pitch;
  297. dp = fb + (rect_y2 * fb_pitch);
  298. line_step_s = -src_pitch;
  299. sp = sp_src + ((rect_sy + h - 1) * src_pitch);
  300. }
  301. if (rect_sx < rect_x1) {
  302. x_reverse = 1;
  303. }
  304. for (uint16_t y_line = 0; y_line < h; y_line++) {
  305. if (x_reverse) {
  306. for (int16_t x = w; x >= 0; x--) {
  307. ((uint8_t *)dp)[rect_x1 + x] = (((uint8_t *)dp)[rect_x1 + x] & (mask ^ 0xFF)) | (((uint8_t *)sp)[rect_sx + x] & mask);
  308. }
  309. }
  310. else {
  311. for (int16_t x = 0; x < w; x++) {
  312. ((uint8_t *)dp)[rect_x1 + x] = (((uint8_t *)dp)[rect_x1 + x] & (mask ^ 0xFF)) | (((uint8_t *)sp)[rect_sx + x] & mask);
  313. }
  314. }
  315. dp += line_step_d;
  316. sp += line_step_s;
  317. }
  318. }
  319. void copy_rect32(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint16_t rect_sx, uint16_t rect_sy) {
  320. int8_t ystep=1, xstep=1;
  321. uint16_t tmp;
  322. if (rect_sy < rect_y1) {
  323. uint16_t h = rect_y2-rect_y1;
  324. ystep=-1;
  325. tmp=rect_y2; rect_y2=rect_y1; rect_y1=tmp;
  326. rect_sy+=h;
  327. }
  328. if (rect_sx < rect_x1) {
  329. uint16_t w = rect_x2-rect_x1;
  330. xstep=-1;
  331. tmp=rect_x2; rect_x2=rect_x1; rect_x1=tmp;
  332. rect_sx+=w;
  333. }
  334. rect_y2+=ystep;
  335. rect_x2+=xstep;
  336. for (uint16_t sy=rect_sy, dy=rect_y1; dy!=rect_y2; sy+=ystep, dy+=ystep) {
  337. uint32_t* dp=(uint32_t*)(fb+dy*fb_pitch);
  338. uint32_t* sp=(uint32_t*)(fb+sy*fb_pitch);
  339. for (uint16_t sx=rect_sx, dx=rect_x1; dx!=rect_x2; sx+=xstep, dx+=xstep) {
  340. dp[dx]=sp[sx];
  341. }
  342. }
  343. }
  344. void copy_rect16(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint16_t rect_sx, uint16_t rect_sy) {
  345. int8_t ystep=1, xstep=1;
  346. uint16_t tmp;
  347. if (rect_sy < rect_y1) {
  348. uint16_t h = rect_y2-rect_y1;
  349. ystep=-1;
  350. tmp=rect_y2; rect_y2=rect_y1; rect_y1=tmp;
  351. rect_sy+=h;
  352. }
  353. if (rect_sx < rect_x1) {
  354. uint16_t w = rect_x2-rect_x1;
  355. xstep=-1;
  356. tmp=rect_x2; rect_x2=rect_x1; rect_x1=tmp;
  357. rect_sx+=w;
  358. }
  359. rect_y2+=ystep;
  360. rect_x2+=xstep;
  361. for (uint16_t sy=rect_sy, dy=rect_y1; dy!=rect_y2; sy+=ystep, dy+=ystep) {
  362. uint16_t* dp=(uint16_t*)(fb+dy*fb_pitch);
  363. uint16_t* sp=(uint16_t*)(fb+sy*fb_pitch);
  364. for (uint16_t sx=rect_sx, dx=rect_x1; dx!=rect_x2; sx+=xstep, dx+=xstep) {
  365. dp[dx]=sp[sx];
  366. }
  367. }
  368. }
  369. void copy_rect8(uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2, uint16_t rect_sx, uint16_t rect_sy) {
  370. int8_t ystep=1, xstep=1;
  371. uint16_t tmp;
  372. if (rect_sy < rect_y1) {
  373. uint16_t h = rect_y2-rect_y1;
  374. ystep=-1;
  375. tmp=rect_y2; rect_y2=rect_y1; rect_y1=tmp;
  376. rect_sy+=h;
  377. }
  378. if (rect_sx < rect_x1) {
  379. uint16_t w = rect_x2-rect_x1;
  380. xstep=-1;
  381. tmp=rect_x2; rect_x2=rect_x1; rect_x1=tmp;
  382. rect_sx+=w;
  383. }
  384. rect_y2+=ystep;
  385. rect_x2+=xstep;
  386. for (uint16_t sy=rect_sy, dy=rect_y1; dy!=rect_y2; sy+=ystep, dy+=ystep) {
  387. uint8_t* dp=(uint8_t*)(fb+dy*fb_pitch);
  388. uint8_t* sp=(uint8_t*)(fb+sy*fb_pitch);
  389. for (uint16_t sx=rect_sx, dx=rect_x1; dx!=rect_x2; sx+=xstep, dx+=xstep) {
  390. dp[dx]=sp[sx];
  391. }
  392. }
  393. }
  394. #define DRAW_LINE_PIXEL \
  395. if (draw_mode == JAM1) { \
  396. if(pattern & cur_bit) { \
  397. if (!inversion) { \
  398. if (mask == 0xFF || color_format == MNTVA_COLOR_16BIT565 || color_format == MNTVA_COLOR_32BIT) { SET_FG_PIXEL; } \
  399. else { SET_FG_PIXEL8_MASK(0) } \
  400. } \
  401. else { INVERT_PIXEL; } \
  402. } \
  403. } \
  404. else { \
  405. if(pattern & cur_bit) { \
  406. if (!inversion) { \
  407. if (mask == 0xFF || color_format == MNTVA_COLOR_16BIT565 || color_format == MNTVA_COLOR_32BIT) { SET_FG_PIXEL; } \
  408. else { SET_FG_PIXEL8_MASK(0); } \
  409. } \
  410. else { INVERT_PIXEL; } /* JAM2 and complement is kind of useless, as it ends up being the same visual result as JAM1 and a pattern of 0xFFFF */ \
  411. } \
  412. else { \
  413. if (!inversion) { \
  414. if (mask == 0xFF || color_format == MNTVA_COLOR_16BIT565 || color_format == MNTVA_COLOR_32BIT) { SET_BG_PIXEL; } \
  415. else { SET_BG_PIXEL8_MASK(0); } \
  416. } \
  417. else { INVERT_PIXEL; } \
  418. } \
  419. } \
  420. if ((cur_bit >>= 1) == 0) \
  421. cur_bit = 0x8000; \
  422. // Sneakily adapted version of the good old Bresenham algorithm
  423. void draw_line(int16_t rect_x1, int16_t rect_y1, int16_t rect_x2, int16_t rect_y2, uint16_t len,
  424. uint16_t pattern, uint16_t pattern_offset,
  425. uint32_t fg_color, uint32_t bg_color, uint32_t color_format,
  426. uint8_t mask, uint8_t draw_mode)
  427. {
  428. int16_t x1 = rect_x1, y1 = rect_y1;
  429. int16_t x2 = rect_x1 + rect_x2, y2 = rect_y1 + rect_y2;
  430. uint8_t u8_fg = fg_color >> 24;
  431. uint8_t u8_bg = bg_color >> 24;
  432. uint32_t* dp = fb + (y1 * fb_pitch);
  433. int32_t line_step = fb_pitch;
  434. int8_t x_reverse = 0, inversion = 0;
  435. uint16_t cur_bit = 0x8000;
  436. int16_t dx, dy, dx_abs, dy_abs, ix, iy, x = x1;
  437. if (x2 < x1)
  438. x_reverse = 1;
  439. if (y2 < y1)
  440. line_step = -fb_pitch;
  441. if (draw_mode & INVERSVID)
  442. pattern ^= 0xFFFF;
  443. if (draw_mode & COMPLEMENT) {
  444. inversion = 1;
  445. fg_color = 0xFFFF0000;
  446. }
  447. draw_mode &= 0x01;
  448. dx = x2 - x1;
  449. dy = y2 - y1;
  450. dx_abs = abs(dx);
  451. dy_abs = abs(dy);
  452. ix = dy_abs >> 1;
  453. iy = dx_abs >> 1;
  454. // This can't be used for now, as Flags from the current RastPort struct is not exposed by [ P96 2.4.2 ]
  455. /*if ((pattern_offset >> 8) & 0x01) { // Is FRST_DOT set?
  456. cur_bit = 0x8000;
  457. fg_color = 0xFFFF0000;
  458. }
  459. else {
  460. fg_color = 0xFF00FF00;
  461. cur_bit >>= ((pattern_offset & 0xFF) % 16);
  462. }
  463. if (cur_bit == 0)
  464. cur_bit = 0x8000;*/
  465. DRAW_LINE_PIXEL;
  466. if (dx_abs >= dy_abs) {
  467. if (!len) len = dx_abs;
  468. for (uint16_t i = 0; i < len; i++) {
  469. iy += dy_abs;
  470. if (iy >= dx_abs) {
  471. iy -= dx_abs;
  472. dp += line_step;
  473. }
  474. x += (x_reverse) ? -1 : 1;
  475. DRAW_LINE_PIXEL;
  476. }
  477. }
  478. else {
  479. if (!len) len = dy_abs;
  480. for(uint16_t i = 0; i < len; i++) {
  481. ix += dx_abs;
  482. if (ix >= dy_abs) {
  483. ix -= dy_abs;
  484. x += (x_reverse) ? -1 : 1;
  485. }
  486. dp += line_step;
  487. DRAW_LINE_PIXEL;
  488. }
  489. }
  490. }
  491. void draw_line_solid(int16_t rect_x1, int16_t rect_y1, int16_t rect_x2, int16_t rect_y2, uint16_t len,
  492. uint32_t fg_color, uint32_t color_format)
  493. {
  494. int16_t x1 = rect_x1, y1 = rect_y1;
  495. int16_t x2 = rect_x1 + rect_x2, y2 = rect_y1 + rect_y2;
  496. uint8_t u8_fg = fg_color >> 24;
  497. uint32_t* dp = fb + (y1 * fb_pitch);
  498. int32_t line_step = fb_pitch;
  499. int8_t x_reverse = 0;
  500. int16_t dx, dy, dx_abs, dy_abs, ix, iy, x = x1;
  501. if (x2 < x1)
  502. x_reverse = 1;
  503. if (y2 < y1)
  504. line_step = -fb_pitch;
  505. dx = x2 - x1;
  506. dy = y2 - y1;
  507. dx_abs = abs(dx);
  508. dy_abs = abs(dy);
  509. ix = dy_abs >> 1;
  510. iy = dx_abs >> 1;
  511. SET_FG_PIXEL;
  512. if (dx_abs >= dy_abs) {
  513. if (!len) len = dx_abs;
  514. for (uint16_t i = 0; i < len; i++) {
  515. iy += dy_abs;
  516. if (iy >= dx_abs) {
  517. iy -= dx_abs;
  518. dp += line_step;
  519. }
  520. x += (x_reverse) ? -1 : 1;
  521. SET_FG_PIXEL;
  522. }
  523. }
  524. else {
  525. if (!len) len = dy_abs;
  526. for (uint16_t i = 0; i < len; i++) {
  527. ix += dx_abs;
  528. if (ix >= dy_abs) {
  529. ix -= dy_abs;
  530. x += (x_reverse) ? -1 : 1;
  531. }
  532. dp += line_step;
  533. SET_FG_PIXEL;
  534. }
  535. }
  536. }
  537. #define DECODE_PLANAR_PIXEL(a) \
  538. switch (planes) { \
  539. case 8: if (layer_mask & 0x80 && bmp_data[(plane_size * 7) + cur_byte] & cur_bit) a |= 0x80; \
  540. case 7: if (layer_mask & 0x40 && bmp_data[(plane_size * 6) + cur_byte] & cur_bit) a |= 0x40; \
  541. case 6: if (layer_mask & 0x20 && bmp_data[(plane_size * 5) + cur_byte] & cur_bit) a |= 0x20; \
  542. case 5: if (layer_mask & 0x10 && bmp_data[(plane_size * 4) + cur_byte] & cur_bit) a |= 0x10; \
  543. case 4: if (layer_mask & 0x08 && bmp_data[(plane_size * 3) + cur_byte] & cur_bit) a |= 0x08; \
  544. case 3: if (layer_mask & 0x04 && bmp_data[(plane_size * 2) + cur_byte] & cur_bit) a |= 0x04; \
  545. case 2: if (layer_mask & 0x02 && bmp_data[plane_size + cur_byte] & cur_bit) a |= 0x02; \
  546. case 1: if (layer_mask & 0x01 && bmp_data[cur_byte] & cur_bit) a |= 0x01; \
  547. break; \
  548. }
  549. #define DECODE_INVERTED_PLANAR_PIXEL(a) \
  550. switch (planes) { \
  551. case 8: if (layer_mask & 0x80 && (bmp_data[(plane_size * 7) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x80; \
  552. case 7: if (layer_mask & 0x40 && (bmp_data[(plane_size * 6) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x40; \
  553. case 6: if (layer_mask & 0x20 && (bmp_data[(plane_size * 5) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x20; \
  554. case 5: if (layer_mask & 0x10 && (bmp_data[(plane_size * 4) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x10; \
  555. case 4: if (layer_mask & 0x08 && (bmp_data[(plane_size * 3) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x08; \
  556. case 3: if (layer_mask & 0x04 && (bmp_data[(plane_size * 2) + cur_byte] ^ 0xFF) & cur_bit) a |= 0x04; \
  557. case 2: if (layer_mask & 0x02 && (bmp_data[plane_size + cur_byte] ^ 0xFF) & cur_bit) a |= 0x02; \
  558. case 1: if (layer_mask & 0x01 && (bmp_data[cur_byte] ^ 0xFF) & cur_bit) a |= 0x01; \
  559. break; \
  560. }
  561. void p2c_rect(int16_t sx, int16_t sy, int16_t dx, int16_t dy, int16_t w, int16_t h, uint16_t sh, uint8_t draw_mode, uint8_t planes, uint8_t mask, uint8_t layer_mask, uint16_t src_line_pitch, uint8_t *bmp_data_src)
  562. {
  563. uint32_t *dp = fb + (dy * fb_pitch);
  564. uint8_t cur_bit, base_bit, base_byte;
  565. uint16_t cur_byte = 0, u8_fg = 0;
  566. uint32_t plane_size = src_line_pitch * h;
  567. uint8_t *bmp_data = bmp_data_src;
  568. cur_bit = base_bit = (0x80 >> (sx % 8));
  569. cur_byte = base_byte = ((sx / 8) % src_line_pitch);
  570. for (int16_t line_y = 0; line_y < h; line_y++) {
  571. for (int16_t x = dx; x < dx + w; x++) {
  572. u8_fg = 0;
  573. if (draw_mode & 0x01) // If bit 1 is set, the inverted planar data is always used.
  574. DECODE_INVERTED_PLANAR_PIXEL(u8_fg)
  575. else
  576. DECODE_PLANAR_PIXEL(u8_fg)
  577. if (mask == 0xFF && (draw_mode == MINTERM_SRC || draw_mode == MINTERM_NOTSRC)) {
  578. ((uint8_t *)dp)[x] = u8_fg;
  579. goto skip;
  580. }
  581. HANDLE_MINTERM_PIXEL_8(u8_fg, ((uint8_t *)dp)[x]);
  582. skip:;
  583. if ((cur_bit >>= 1) == 0) {
  584. cur_bit = 0x80;
  585. cur_byte++;
  586. cur_byte %= src_line_pitch;
  587. }
  588. }
  589. dp += fb_pitch;
  590. if ((line_y + sy + 1) % h)
  591. bmp_data += src_line_pitch;
  592. else
  593. bmp_data = bmp_data_src;
  594. cur_bit = base_bit;
  595. cur_byte = base_byte;
  596. }
  597. }
  598. void p2d_rect(int16_t sx, int16_t sy, int16_t dx, int16_t dy, int16_t w, int16_t h, uint16_t sh, uint8_t draw_mode, uint8_t planes, uint8_t mask, uint8_t layer_mask, uint32_t color_mask, uint16_t src_line_pitch, uint8_t *bmp_data_src, uint32_t color_format)
  599. {
  600. uint32_t *dp = fb + (dy * fb_pitch);
  601. uint8_t cur_bit, base_bit, base_byte;
  602. uint16_t cur_byte = 0, cur_pixel = 0;
  603. uint32_t fg_color = 0;
  604. uint32_t plane_size = src_line_pitch * h;
  605. uint32_t *bmp_pal = (uint32_t *)bmp_data_src;
  606. uint8_t *bmp_data = bmp_data_src + (256 * 4);
  607. cur_bit = base_bit = (0x80 >> (sx % 8));
  608. cur_byte = base_byte = ((sx / 8) % src_line_pitch);
  609. for (int16_t line_y = 0; line_y < h; line_y++) {
  610. for (int16_t x = dx; x < dx + w; x++) {
  611. cur_pixel = 0;
  612. if (draw_mode & 0x01)
  613. DECODE_INVERTED_PLANAR_PIXEL(cur_pixel)
  614. else
  615. DECODE_PLANAR_PIXEL(cur_pixel)
  616. fg_color = bmp_pal[cur_pixel];
  617. if (mask == 0xFF && (draw_mode == 0x0C || draw_mode == 0x03)) {
  618. switch (color_format) {
  619. case MNTVA_COLOR_16BIT565:
  620. ((uint16_t *)dp)[x] = fg_color;
  621. break;
  622. case MNTVA_COLOR_32BIT:
  623. dp[x] = fg_color;
  624. break;
  625. }
  626. goto skip;
  627. }
  628. HANDLE_MINTERM_PIXEL_16_32(fg_color, dp);
  629. skip:;
  630. if ((cur_bit >>= 1) == 0) {
  631. cur_bit = 0x80;
  632. cur_byte++;
  633. cur_byte %= src_line_pitch;
  634. }
  635. }
  636. dp += fb_pitch;
  637. if ((line_y + sy + 1) % h)
  638. bmp_data += src_line_pitch;
  639. else
  640. bmp_data = bmp_data_src;
  641. cur_bit = base_bit;
  642. cur_byte = base_byte;
  643. }
  644. }
  645. #define PATTERN_FILLRECT_LOOPX \
  646. tmpl_x ^= 0x01; \
  647. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  648. #define PATTERN_FILLRECT_LOOPY \
  649. tmpl_data += 2 ; \
  650. if ((y_line + y_offset + 1) % loop_rows == 0) \
  651. tmpl_data = tmpl_base; \
  652. tmpl_x = tmpl_x_base; \
  653. cur_bit = base_bit; \
  654. dp += fb_pitch / 4;
  655. void pattern_fill_rect(uint32_t color_format, uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h,
  656. uint8_t draw_mode, uint8_t mask, uint32_t fg_color, uint32_t bg_color,
  657. uint16_t x_offset, uint16_t y_offset,
  658. uint8_t *tmpl_data, uint16_t tmpl_pitch, uint16_t loop_rows)
  659. {
  660. uint32_t rect_x2 = rect_x1 + w;
  661. uint32_t *dp = fb + (rect_y1 * (fb_pitch / 4));
  662. uint8_t* tmpl_base = tmpl_data;
  663. uint16_t tmpl_x, tmpl_x_base;
  664. uint8_t cur_bit, base_bit, inversion = 0;
  665. uint8_t u8_fg = fg_color >> 24;
  666. uint8_t u8_bg = bg_color >> 24;
  667. uint8_t cur_byte = 0;
  668. tmpl_x = (x_offset / 8) % 2;
  669. tmpl_data += (y_offset % loop_rows) * 2;
  670. tmpl_x_base = tmpl_x;
  671. cur_bit = base_bit = (0x80 >> (x_offset % 8));
  672. if (draw_mode & INVERSVID) inversion = 1;
  673. draw_mode &= 0x03;
  674. if (draw_mode == JAM1) {
  675. for (uint16_t y_line = 0; y_line < h; y_line++) {
  676. uint16_t x = rect_x1;
  677. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  678. while (x < rect_x2) {
  679. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  680. if (mask == 0xFF) {
  681. SET_FG_PIXELS;
  682. }
  683. else {
  684. SET_FG_PIXELS_MASK;
  685. }
  686. x += 8;
  687. }
  688. else {
  689. while (cur_bit > 0 && x < rect_x2) {
  690. if (cur_byte & cur_bit) {
  691. SET_FG_PIXEL_MASK;
  692. }
  693. x++;
  694. cur_bit >>= 1;
  695. }
  696. cur_bit = 0x80;
  697. }
  698. PATTERN_FILLRECT_LOOPX;
  699. }
  700. PATTERN_FILLRECT_LOOPY;
  701. }
  702. return;
  703. }
  704. else if (draw_mode == JAM2) {
  705. for (uint16_t y_line = 0; y_line < h; y_line++) {
  706. uint16_t x = rect_x1;
  707. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  708. while (x < rect_x2) {
  709. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  710. if (mask == 0xFF) {
  711. SET_FG_OR_BG_PIXELS;
  712. }
  713. else {
  714. SET_FG_OR_BG_PIXELS_MASK;
  715. }
  716. x += 8;
  717. }
  718. else {
  719. while (cur_bit > 0 && x < rect_x2) {
  720. if (cur_byte & cur_bit) {
  721. SET_FG_PIXEL_MASK;
  722. }
  723. else {
  724. SET_BG_PIXEL_MASK;
  725. }
  726. x++;
  727. cur_bit >>= 1;
  728. }
  729. cur_bit = 0x80;
  730. }
  731. PATTERN_FILLRECT_LOOPX;
  732. }
  733. PATTERN_FILLRECT_LOOPY;
  734. }
  735. return;
  736. }
  737. else { // COMPLEMENT
  738. for (uint16_t y_line = 0; y_line < h; y_line++) {
  739. uint16_t x = rect_x1;
  740. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  741. while (x < rect_x2) {
  742. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  743. INVERT_PIXELS;
  744. x += 8;
  745. }
  746. else {
  747. while (cur_bit > 0 && x < rect_x2) {
  748. if (cur_byte & cur_bit) {
  749. INVERT_PIXEL;
  750. }
  751. x++;
  752. cur_bit >>= 1;
  753. }
  754. cur_bit = 0x80;
  755. }
  756. PATTERN_FILLRECT_LOOPX;
  757. }
  758. PATTERN_FILLRECT_LOOPY;
  759. }
  760. }
  761. }
  762. #define TEMPLATE_FILLRECT_LOOPX \
  763. tmpl_x++; \
  764. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  765. #define TEMPLATE_FILLRECT_LOOPY \
  766. tmpl_data += tmpl_pitch; \
  767. tmpl_x = tmpl_x_base; \
  768. cur_bit = base_bit; \
  769. dp += fb_pitch / 4;
  770. void template_fill_rect(uint32_t color_format, uint16_t rect_x1, uint16_t rect_y1, uint16_t w, uint16_t h,
  771. uint8_t draw_mode, uint8_t mask, uint32_t fg_color, uint32_t bg_color,
  772. uint16_t x_offset, uint16_t y_offset,
  773. uint8_t *tmpl_data, uint16_t tmpl_pitch)
  774. {
  775. uint32_t rect_x2 = rect_x1 + w;
  776. uint32_t *dp = fb + (rect_y1 * (fb_pitch / 4));
  777. uint16_t tmpl_x, tmpl_x_base;
  778. uint8_t cur_bit, base_bit, inversion = 0;
  779. uint8_t u8_fg = fg_color >> 24;
  780. uint8_t u8_bg = bg_color >> 24;
  781. uint8_t cur_byte = 0;
  782. tmpl_x = x_offset / 8;
  783. tmpl_x_base = tmpl_x;
  784. cur_bit = base_bit = (0x80 >> (x_offset % 8));
  785. if (draw_mode & INVERSVID) inversion = 1;
  786. draw_mode &= 0x03;
  787. if (draw_mode == JAM1) {
  788. for (uint16_t y_line = 0; y_line < h; y_line++) {
  789. uint16_t x = rect_x1;
  790. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  791. while (x < rect_x2) {
  792. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  793. if (mask == 0xFF) {
  794. SET_FG_PIXELS;
  795. }
  796. else {
  797. SET_FG_PIXELS_MASK;
  798. }
  799. x += 8;
  800. }
  801. else {
  802. while (cur_bit > 0 && x < rect_x2) {
  803. if (cur_byte & cur_bit) {
  804. SET_FG_PIXEL_MASK;
  805. }
  806. x++;
  807. cur_bit >>= 1;
  808. }
  809. cur_bit = 0x80;
  810. }
  811. TEMPLATE_FILLRECT_LOOPX;
  812. }
  813. TEMPLATE_FILLRECT_LOOPY;
  814. }
  815. return;
  816. }
  817. else if (draw_mode == JAM2) {
  818. for (uint16_t y_line = 0; y_line < h; y_line++) {
  819. uint16_t x = rect_x1;
  820. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  821. while (x < rect_x2) {
  822. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  823. if (mask == 0xFF) {
  824. SET_FG_OR_BG_PIXELS;
  825. }
  826. else {
  827. SET_FG_OR_BG_PIXELS_MASK;
  828. }
  829. x += 8;
  830. }
  831. else {
  832. while (cur_bit > 0 && x < rect_x2) {
  833. if (cur_byte & cur_bit) {
  834. SET_FG_PIXEL_MASK;
  835. }
  836. else {
  837. SET_BG_PIXEL_MASK;
  838. }
  839. x++;
  840. cur_bit >>= 1;
  841. }
  842. cur_bit = 0x80;
  843. }
  844. TEMPLATE_FILLRECT_LOOPX;
  845. }
  846. TEMPLATE_FILLRECT_LOOPY;
  847. }
  848. return;
  849. }
  850. else { // COMPLEMENT
  851. for (uint16_t y_line = 0; y_line < h; y_line++) {
  852. uint16_t x = rect_x1;
  853. cur_byte = (inversion) ? tmpl_data[tmpl_x] ^ 0xFF : tmpl_data[tmpl_x];
  854. while (x < rect_x2) {
  855. if (cur_bit == 0x80 && x < rect_x2 - 8) {
  856. INVERT_PIXELS;
  857. x += 8;
  858. }
  859. else {
  860. while (cur_bit > 0 && x < rect_x2) {
  861. if (cur_byte & cur_bit) {
  862. INVERT_PIXEL;
  863. }
  864. x++;
  865. cur_bit >>= 1;
  866. }
  867. cur_bit = 0x80;
  868. }
  869. TEMPLATE_FILLRECT_LOOPX;
  870. }
  871. TEMPLATE_FILLRECT_LOOPY;
  872. }
  873. }
  874. }
  875. void fill_template(uint32_t bpp, uint16_t rect_x1, uint16_t rect_y1, uint16_t rect_x2, uint16_t rect_y2,
  876. uint8_t draw_mode, uint8_t mask, uint32_t fg_color, uint32_t bg_color, uint16_t x_offset, uint16_t y_offset, uint8_t* tmpl_data, uint16_t tmpl_pitch, uint16_t loop_rows)
  877. {
  878. uint8_t inversion = 0;
  879. uint16_t rows;
  880. int bitoffset;
  881. uint8_t* dp=(uint8_t*)(fb);
  882. uint8_t* tmpl_base;
  883. uint16_t width = rect_x2-rect_x1+1;
  884. if (draw_mode & INVERSVID) inversion = 1;
  885. draw_mode &= 0x03;
  886. bitoffset = x_offset % 8;
  887. tmpl_base = tmpl_data + x_offset / 8;
  888. // starting position in destination
  889. dp += rect_y1*fb_pitch + rect_x1*bpp;
  890. // number of 8-bit blocks of source
  891. uint16_t loop_x = x_offset;
  892. uint16_t loop_y = y_offset;
  893. for (rows = rect_y1; rows <= rect_y2; rows++, dp += fb_pitch, tmpl_base += tmpl_pitch) {
  894. unsigned long cols;
  895. uint8_t* dp2 = dp;
  896. uint8_t* tmpl_mem;
  897. unsigned int data;
  898. tmpl_mem = tmpl_base;
  899. data = *tmpl_mem;
  900. for (cols = 0; cols < width; cols += 8, dp2 += bpp*8) {
  901. unsigned int byte;
  902. long bits;
  903. long max = width - cols;
  904. if (max > 8) max = 8;
  905. // loop through 16-bit horizontal pattern
  906. if (loop_rows>0) {
  907. tmpl_mem = tmpl_data+(loop_y%loop_rows)*2;
  908. byte = tmpl_mem[loop_x%2];
  909. loop_x++;
  910. } else {
  911. data <<= 8;
  912. data |= *++tmpl_mem;
  913. byte = data >> (8 - bitoffset);
  914. }
  915. switch (draw_mode)
  916. {
  917. case JAM1:
  918. {
  919. for (bits = 0; bits < max; bits++) {
  920. int bit_set = (byte & 0x80);
  921. byte <<= 1;
  922. if (inversion) bit_set = !bit_set;
  923. if (bit_set) {
  924. if (bpp == 1) {
  925. dp2[bits] = fg_color>>24;
  926. } else if (bpp == 2) {
  927. ((uint16_t*)dp2)[bits] = fg_color;
  928. } else if (bpp == 4) {
  929. ((uint32_t*)dp2)[bits] = fg_color;
  930. }
  931. // TODO mask
  932. //dp2[bits] = (fg_color & mask) | (dp2[bits] & ~mask);
  933. }
  934. }
  935. break;
  936. }
  937. case JAM2:
  938. {
  939. for (bits = 0; bits < max; bits++) {
  940. char bit_set = (byte & 0x80);
  941. byte <<= 1;
  942. if (inversion) bit_set = !bit_set;
  943. uint32_t color = bit_set ? fg_color : bg_color;
  944. //if (bit_set) printf("#");
  945. //else printf(".");
  946. if (bpp == 1) {
  947. dp2[bits] = color>>24;
  948. } else if (bpp == 2) {
  949. ((uint16_t*)dp2)[bits] = color;
  950. } else if (bpp == 4) {
  951. ((uint32_t*)dp2)[bits] = color;
  952. }
  953. // NYI
  954. // dp2[bits] = (color & mask) | (dp2[bits] & ~mask);
  955. }
  956. break;
  957. }
  958. case COMPLEMENT:
  959. {
  960. for (bits = 0; bits < max; bits++) {
  961. int bit_set = (byte & 0x80);
  962. byte <<= 1;
  963. if (bit_set) {
  964. if (bpp == 1) {
  965. dp2[bits] ^= 0xff;
  966. } else if (bpp == 2) {
  967. ((uint16_t*)dp2)[bits] ^= 0xffff;
  968. } else if (bpp == 4) {
  969. ((uint32_t*)dp2)[bits] ^= 0xffffffff;
  970. }
  971. }
  972. // TODO mask
  973. }
  974. break;
  975. }
  976. }
  977. }
  978. loop_y++;
  979. }
  980. }