lv_draw.c 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157
  1. /**
  2. * @file lv_draw.c
  3. *
  4. */
  5. /*********************
  6. * INCLUDES
  7. *********************/
  8. #include "../../lv_conf.h"
  9. #include <stdio.h>
  10. #include <stdbool.h>
  11. #include "lv_draw.h"
  12. #include "lv_draw_rbasic.h"
  13. #include "lv_draw_vbasic.h"
  14. #include "../lv_misc/lv_circ.h"
  15. #include "../lv_misc/lv_fs.h"
  16. #include "../lv_misc/lv_math.h"
  17. #include "../lv_misc/lv_ufs.h"
  18. #include "../lv_objx/lv_img.h"
  19. /*********************
  20. * DEFINES
  21. *********************/
  22. #define CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD 5 /*Circle segment greater then this value will be anti-aliased by a non-linear (cos) opacity mapping*/
  23. #define LINE_WIDTH_CORR_BASE 64
  24. #define LINE_WIDTH_CORR_SHIFT 6
  25. #define LABEL_RECOLOR_PAR_LENGTH 6
  26. #define SHADOW_OPA_EXTRA_PRECISION 8 /*Calculate with 2^x bigger shadow opacity values to avoid rounding errors*/
  27. #define SHADOW_BOTTOM_AA_EXTRA_RADIUS 3 /*Add extra radius with LV_SHADOW_BOTTOM to cover anti-aliased corners*/
  28. /**********************
  29. * TYPEDEFS
  30. **********************/
  31. typedef enum
  32. {
  33. CMD_STATE_WAIT,
  34. CMD_STATE_PAR,
  35. CMD_STATE_IN,
  36. }cmd_state_t;
  37. /**********************
  38. * STATIC PROTOTYPES
  39. **********************/
  40. static void lv_draw_rect_main_mid(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  41. static void lv_draw_rect_main_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  42. static void lv_draw_rect_border_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  43. static void lv_draw_rect_border_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  44. #if USE_LV_SHADOW && LV_VDB_SIZE
  45. static void lv_draw_shadow(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  46. static void lv_draw_shadow_full(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  47. static void lv_draw_shadow_bottom(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style);
  48. static void lv_draw_shadow_full_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, const lv_opa_t * map);
  49. #endif
  50. static uint16_t lv_draw_cont_radius_corr(uint16_t r, lv_coord_t w, lv_coord_t h);
  51. #if LV_ANTIALIAS != 0
  52. static lv_opa_t antialias_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t line_opa);
  53. static lv_opa_t antialias_get_opa_circ(lv_coord_t seg, lv_coord_t px_id, lv_opa_t opa);
  54. #endif
  55. static uint8_t hex_char_to_num(char hex);
  56. #if USE_LV_TRIANGLE != 0
  57. static void point_swap(lv_point_t * p1, lv_point_t * p2);
  58. #endif
  59. /**********************
  60. * STATIC VARIABLES
  61. **********************/
  62. #if LV_VDB_SIZE != 0
  63. static void (*px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_vpx;
  64. static void (*fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_vfill;
  65. static void (*letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa) = lv_vletter;
  66. # if USE_LV_IMG
  67. static void (*map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
  68. const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
  69. lv_color_t recolor, lv_opa_t recolor_opa) = lv_vmap;
  70. # endif /*USE_LV_IMG*/
  71. #elif USE_LV_REAL_DRAW != 0
  72. /* px_fp used only by shadow drawing and anti aliasing
  73. * static void (*px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_rpx;
  74. */
  75. static void (*fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_rfill;
  76. static void (*letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa) = lv_rletter;
  77. # if USE_LV_IMG
  78. static void (*map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
  79. const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
  80. lv_color_t recolor, lv_opa_t recolor_opa) = lv_rmap;
  81. # endif /*USE_LV_IMG*/
  82. #else
  83. /*Invalid settings. Compiler error will be thrown*/
  84. static void (*px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = NULL;
  85. static void (*fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = NULL;
  86. static void (*letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa) = NULL;
  87. static void (*map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
  88. const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
  89. lv_color_t recolor, lv_opa_t recolor_opa) = NULL;
  90. #endif
  91. /**********************
  92. * MACROS
  93. **********************/
  94. /**********************
  95. * GLOBAL FUNCTIONS
  96. **********************/
  97. /**
  98. * Draw a rectangle
  99. * @param coords the coordinates of the rectangle
  100. * @param mask the rectangle will be drawn only in this mask
  101. * @param style pointer to a style
  102. */
  103. void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  104. {
  105. if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
  106. #if USE_LV_SHADOW && LV_VDB_SIZE
  107. if(style->body.shadow.width != 0) {
  108. lv_draw_shadow(coords, mask, style);
  109. }
  110. #endif
  111. if(style->body.empty == 0){
  112. lv_draw_rect_main_mid(coords, mask, style);
  113. if(style->body.radius != 0) {
  114. lv_draw_rect_main_corner(coords, mask, style);
  115. }
  116. }
  117. if(style->body.border.width != 0 && style->body.border.part != LV_BORDER_NONE) {
  118. lv_draw_rect_border_straight(coords, mask, style);
  119. if(style->body.radius != 0) {
  120. lv_draw_rect_border_corner(coords, mask, style);
  121. }
  122. }
  123. }
  124. #if USE_LV_TRIANGLE != 0
  125. /**
  126. *
  127. * @param points pointer to an array with 3 points
  128. * @param mask the triangle will be drawn only in this mask
  129. * @param color color of the triangle
  130. */
  131. void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, lv_color_t color)
  132. {
  133. lv_point_t tri[3];
  134. memcpy(tri, points, sizeof(tri));
  135. /*Sort the vertices according to their y coordinate (0: y max, 1: y mid, 2:y min)*/
  136. if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
  137. if(tri[2].y < tri[1].y) point_swap(&tri[2], &tri[1]);
  138. if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
  139. /*Return is the triangle is degenerated*/
  140. if(tri[0].x == tri[1].x && tri[0].y == tri[1].y) return;
  141. if(tri[1].x == tri[2].x && tri[1].y == tri[2].y) return;
  142. if(tri[0].x == tri[2].x && tri[0].y == tri[2].y) return;
  143. if(tri[0].x == tri[1].x && tri[1].x == tri[2].x) return;
  144. if(tri[0].y == tri[1].y && tri[1].y == tri[2].y) return;
  145. /*Draw the triangle*/
  146. lv_point_t edge1;
  147. lv_coord_t dx1 = LV_MATH_ABS(tri[0].x - tri[1].x);
  148. lv_coord_t sx1 = tri[0].x < tri[1].x ? 1 : -1;
  149. lv_coord_t dy1 = LV_MATH_ABS(tri[0].y - tri[1].y);
  150. lv_coord_t sy1 = tri[0].y < tri[1].y ? 1 : -1;
  151. lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
  152. lv_coord_t err_tmp1;
  153. lv_point_t edge2;
  154. lv_coord_t dx2 = LV_MATH_ABS(tri[0].x - tri[2].x);
  155. lv_coord_t sx2 = tri[0].x < tri[2].x ? 1 : -1;
  156. lv_coord_t dy2 = LV_MATH_ABS(tri[0].y - tri[2].y);
  157. lv_coord_t sy2 = tri[0].y < tri[2].y ? 1 : -1;
  158. lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
  159. lv_coord_t err_tmp2;
  160. lv_coord_t y1_tmp;
  161. lv_coord_t y2_tmp;
  162. edge1.x = tri[0].x;
  163. edge1.y = tri[0].y;
  164. edge2.x = tri[0].x;
  165. edge2.y = tri[0].y;
  166. lv_area_t act_area;
  167. lv_area_t draw_area;
  168. while(1) {
  169. act_area.x1 = edge1.x;
  170. act_area.x2 = edge2.x ;
  171. act_area.y1 = edge1.y;
  172. act_area.y2 = edge2.y ;
  173. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  174. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  175. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  176. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  177. draw_area.x2--; /*Do not draw most right pixel because it will be drawn by the adjacent triangle*/
  178. fill_fp(&draw_area, mask, color, LV_OPA_50);
  179. /*Calc. the next point of edge1*/
  180. y1_tmp = edge1.y;
  181. do {
  182. if (edge1.x == tri[1].x && edge1.y == tri[1].y) {
  183. dx1 = LV_MATH_ABS(tri[1].x - tri[2].x);
  184. sx1 = tri[1].x < tri[2].x ? 1 : -1;
  185. dy1 = LV_MATH_ABS(tri[1].y - tri[2].y);
  186. sy1 = tri[1].y < tri[2].y ? 1 : -1;
  187. err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
  188. }
  189. else if (edge1.x == tri[2].x && edge1.y == tri[2].y) return;
  190. err_tmp1 = err1;
  191. if (err_tmp1 >-dx1) {
  192. err1 -= dy1;
  193. edge1.x += sx1;
  194. }
  195. if (err_tmp1 < dy1) {
  196. err1 += dx1;
  197. edge1.y += sy1;
  198. }
  199. } while(edge1.y == y1_tmp);
  200. /*Calc. the next point of edge2*/
  201. y2_tmp = edge2.y;
  202. do {
  203. if (edge2.x == tri[2].x && edge2.y == tri[2].y) return;
  204. err_tmp2 = err2;
  205. if (err_tmp2 > -dx2) {
  206. err2 -= dy2;
  207. edge2.x += sx2;
  208. }
  209. if (err_tmp2 < dy2) {
  210. err2 += dx2;
  211. edge2.y += sy2;
  212. }
  213. } while(edge2.y == y2_tmp);
  214. }
  215. }
  216. #endif
  217. /**
  218. * Write a text
  219. * @param coords coordinates of the label
  220. * @param mask the label will be drawn only in this area
  221. * @param style pointer to a style
  222. * @param txt 0 terminated text to write
  223. * @param flag settings for the text from 'txt_flag_t' enum
  224. * @param offset text offset in x and y direction (NULL if unused)
  225. *
  226. */
  227. void lv_draw_label(const lv_area_t * coords,const lv_area_t * mask, const lv_style_t * style,
  228. const char * txt, lv_txt_flag_t flag, lv_point_t * offset)
  229. {
  230. const lv_font_t * font = style->text.font;
  231. lv_coord_t w;
  232. if((flag & LV_TXT_FLAG_EXPAND) == 0) {
  233. w = lv_area_get_width(coords);
  234. } else {
  235. lv_point_t p;
  236. lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag);
  237. w = p.x;
  238. }
  239. /*Init variables for the first line*/
  240. lv_coord_t line_length = 0;
  241. uint32_t line_start = 0;
  242. uint32_t line_end = lv_txt_get_next_line(txt, font, style->text.letter_space, w, flag);
  243. lv_point_t pos;
  244. pos.x = coords->x1;
  245. pos.y = coords->y1;
  246. /*Align the line to middle if enabled*/
  247. if(flag & LV_TXT_FLAG_CENTER) {
  248. line_length = lv_txt_get_width(&txt[line_start], line_end - line_start,
  249. font, style->text.letter_space, flag);
  250. pos.x += (w - line_length) / 2;
  251. }
  252. cmd_state_t cmd_state = CMD_STATE_WAIT;
  253. uint32_t i;
  254. uint16_t par_start = 0;
  255. lv_color_t recolor;
  256. lv_coord_t letter_w;
  257. lv_coord_t x_ofs = 0;
  258. lv_coord_t y_ofs = 0;
  259. if(offset != NULL) {
  260. x_ofs = offset->x;
  261. y_ofs = offset->y;
  262. pos.y += y_ofs;
  263. }
  264. /*Real draw need a background color for higher bpp letter*/
  265. #if LV_VDB_SIZE == 0
  266. lv_rletter_set_background(style->body.main_color);
  267. #endif
  268. /*Write out all lines*/
  269. while(txt[line_start] != '\0') {
  270. if(offset != NULL) {
  271. pos.x += x_ofs;
  272. }
  273. /*Write all letter of a line*/
  274. cmd_state = CMD_STATE_WAIT;
  275. i = line_start;
  276. uint32_t letter;
  277. while(i < line_end) {
  278. letter = lv_txt_utf8_next(txt, &i);
  279. /*Handle the re-color command*/
  280. if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
  281. if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
  282. if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
  283. par_start = i;// + lv_txt_utf8_size(txt[i]);
  284. cmd_state = CMD_STATE_PAR;
  285. continue;
  286. } else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char */
  287. cmd_state = CMD_STATE_WAIT;
  288. }else if(cmd_state == CMD_STATE_IN) { /*Command end */
  289. cmd_state = CMD_STATE_WAIT;
  290. continue;
  291. }
  292. }
  293. /*Skip the color parameter and wait the space after it*/
  294. if(cmd_state == CMD_STATE_PAR) {
  295. if(letter == ' ') {
  296. /*Get the parameter*/
  297. if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
  298. char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
  299. memcpy(buf, &txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
  300. buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
  301. int r,g,b;
  302. r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
  303. g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
  304. b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
  305. recolor = LV_COLOR_MAKE(r, g, b);
  306. } else {
  307. recolor.full = style->text.color.full;
  308. }
  309. cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
  310. }
  311. continue;
  312. }
  313. }
  314. lv_color_t color = style->text.color;
  315. if(cmd_state == CMD_STATE_IN) color = recolor;
  316. letter_fp(&pos, mask, font, letter, color, style->text.opa);
  317. letter_w = lv_font_get_width(font, letter);
  318. pos.x += letter_w + style->text.letter_space;
  319. }
  320. /*Go to next line*/
  321. line_start = line_end;
  322. line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
  323. pos.x = coords->x1;
  324. /*Align to middle*/
  325. if(flag & LV_TXT_FLAG_CENTER) {
  326. line_length = lv_txt_get_width(&txt[line_start], line_end - line_start,
  327. font, style->text.letter_space, flag);
  328. pos.x += (w - line_length) / 2;
  329. }
  330. /*Go the next line position*/
  331. pos.y += lv_font_get_height(font);
  332. pos.y += style->text.line_space;
  333. }
  334. }
  335. #if USE_LV_IMG
  336. /**
  337. * Draw an image
  338. * @param coords the coordinates of the image
  339. * @param mask the image will be drawn only in this area
  340. * @param map_p pointer to a lv_color_t array which contains the pixels of the image
  341. * @param opa opacity of the image (0..255)
  342. */
  343. void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask,
  344. const lv_style_t * style, const void * src)
  345. {
  346. if(src == NULL) {
  347. lv_draw_rect(coords, mask, &lv_style_plain);
  348. lv_draw_label(coords, mask, &lv_style_plain, "No\ndata", LV_TXT_FLAG_NONE, NULL);
  349. return;
  350. }
  351. const uint8_t * u8_p = (uint8_t*) src;
  352. if(u8_p[0] >= 'A' && u8_p[0] <= 'Z') { /*It will be a path of a file*/
  353. #if USE_LV_FILESYSTEM
  354. lv_fs_file_t file;
  355. lv_fs_res_t res = lv_fs_open(&file, src, LV_FS_MODE_RD);
  356. if(res == LV_FS_RES_OK) {
  357. lv_img_t img_data;
  358. uint32_t br;
  359. res = lv_fs_read(&file, &img_data, sizeof(lv_img_t), &br);
  360. lv_area_t mask_com; /*Common area of mask and cords*/
  361. bool union_ok;
  362. union_ok = lv_area_union(&mask_com, mask, coords);
  363. if(union_ok == false) {
  364. lv_fs_close(&file);
  365. return;
  366. }
  367. uint8_t px_size = 0;
  368. switch(img_data.header.format) {
  369. case LV_IMG_FORMAT_FILE_RAW_RGB332: px_size = 1; break;
  370. case LV_IMG_FORMAT_FILE_RAW_RGB565: px_size = 2; break;
  371. case LV_IMG_FORMAT_FILE_RAW_RGB888: px_size = 4; break;
  372. default: return;
  373. }
  374. if(img_data.header.alpha_byte) { /*Correction with the alpha byte*/
  375. px_size++;
  376. if(img_data.header.format == LV_IMG_FORMAT_FILE_RAW_RGB888) px_size--; /*Stored in the 4 byte anyway*/
  377. }
  378. /* Move the file pointer to the start address according to mask*/
  379. uint32_t start_offset = sizeof(img_data.header);
  380. start_offset += (lv_area_get_width(coords)) * (mask_com.y1 - coords->y1) * px_size; /*First row*/
  381. start_offset += (mask_com.x1 - coords->x1) * px_size; /*First col*/
  382. lv_fs_seek(&file, start_offset);
  383. uint32_t useful_data = lv_area_get_width(&mask_com) * px_size;
  384. uint32_t next_row = lv_area_get_width(coords) * px_size - useful_data;
  385. lv_area_t line;
  386. lv_area_copy(&line, &mask_com);
  387. lv_area_set_height(&line, 1);
  388. lv_coord_t row;
  389. uint32_t act_pos;
  390. #if LV_COMPILER_VLA_SUPPORTED
  391. uint8_t buf[lv_area_get_width(&mask_com) * px_size];
  392. #else
  393. # if LV_HOR_RES > LV_VER_RES
  394. uint8_t buf[LV_HOR_RES * px_size];
  395. # else
  396. uint8_t buf[LV_VER_RES * px_size];
  397. # endif
  398. #endif
  399. for(row = mask_com.y1; row <= mask_com.y2; row ++) {
  400. res = lv_fs_read(&file, buf, useful_data, &br);
  401. map_fp(&line, &mask_com, (uint8_t *)buf, style->image.opa, img_data.header.chroma_keyed, img_data.header.alpha_byte,
  402. style->image.color, style->image.intense);
  403. lv_fs_tell(&file, &act_pos);
  404. lv_fs_seek(&file, act_pos + next_row);
  405. line.y1++; /*Go down a line*/
  406. line.y2++;
  407. }
  408. lv_fs_close(&file);
  409. if(res != LV_FS_RES_OK) {
  410. lv_draw_rect(coords, mask, &lv_style_plain);
  411. lv_draw_label(coords, mask, &lv_style_plain, "No data", LV_TXT_FLAG_NONE, NULL);
  412. }
  413. }
  414. #endif
  415. }
  416. else {
  417. const lv_img_t * img_var = src;
  418. lv_area_t mask_com; /*Common area of mask and coords*/
  419. bool union_ok;
  420. union_ok = lv_area_union(&mask_com, mask, coords);
  421. if(union_ok == false) {
  422. return; /*Out of mask*/
  423. }
  424. map_fp(coords, mask, img_var->pixel_map, style->image.opa, img_var->header.chroma_keyed, img_var->header.alpha_byte, style->image.color, style->image.intense);
  425. }
  426. }
  427. #endif
  428. /**
  429. * Draw a line
  430. * @param p1 first point of the line
  431. * @param p2 second point of the line
  432. * @param maskthe line will be drawn only on this area
  433. * @param lines_p pointer to a line style
  434. */
  435. void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
  436. const lv_style_t * style)
  437. {
  438. if(style->line.width == 0) return;
  439. if(point1->x == point2->x && point1->y == point2->y) return;
  440. lv_point_t p1;
  441. lv_point_t p2;
  442. /*Be sure always x1 < x2*/
  443. if(point1->x < point2->x) {
  444. p1.x = point1->x;
  445. p1.y = point1->y;
  446. p2.x = point2->x;
  447. p2.y = point2->y;
  448. } else {
  449. p1.x = point2->x;
  450. p1.y = point2->y;
  451. p2.x = point1->x;
  452. p2.y = point1->y;
  453. }
  454. lv_coord_t dx = LV_MATH_ABS(p2.x - p1.x);
  455. lv_coord_t sx = p1.x < p2.x ? 1 : -1;
  456. lv_coord_t dy = LV_MATH_ABS(p2.y - p1.y);
  457. lv_coord_t sy = p1.y < p2.y ? 1 : -1;
  458. lv_coord_t err = (dx > dy ? dx : -dy) / 2;
  459. lv_coord_t e2;
  460. bool hor = dx > dy ? true : false; /*Rather horizontal or vertical*/
  461. lv_coord_t last_x = p1.x;
  462. lv_coord_t last_y = p1.y;
  463. lv_point_t act_point;
  464. act_point.x = p1.x;
  465. act_point.y = p1.y;
  466. lv_coord_t width;
  467. uint16_t wcor;
  468. uint16_t width_half = 0;
  469. uint16_t width_1 = 0;
  470. static const uint8_t width_corr_array[] = {
  471. 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66,
  472. 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, 72, 72, 73, 73, 74,
  473. 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85,
  474. 86, 86, 87, 88, 88, 89, 90, 91,
  475. };
  476. if(hor == false) {
  477. wcor = (dx * LINE_WIDTH_CORR_BASE) / dy;
  478. } else {
  479. wcor = (dy * LINE_WIDTH_CORR_BASE) / dx;
  480. }
  481. width = style->line.width - 1;
  482. #if LV_ANTIALIAS != 0
  483. bool aa_invert = false;
  484. aa_invert = p1.y < p2.y ? false : true; /*Direction of opacity increase on the edges*/
  485. if(p1.x != p2.x && p1.y != p2.y) width--; /*Because of anti aliasing (no anti aliasing on hor. and ver. lines)*/
  486. #endif
  487. /*Make the correction on lie width*/
  488. if(width > 0) {
  489. width = (width * width_corr_array[wcor]);
  490. width = width >> LINE_WIDTH_CORR_SHIFT;
  491. width_half = width >> 1;
  492. width_1 = width & 0x1 ? 1 : 0;
  493. }
  494. /*Special case draw a horizontal line*/
  495. if(p1.y == p2.y ) {
  496. lv_area_t act_area;
  497. act_area.x1 = p1.x;
  498. act_area.x2 = p2.x;
  499. act_area.y1 = p1.y - width_half - width_1;
  500. act_area.y2 = p2.y + width_half ;
  501. lv_area_t draw_area;
  502. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  503. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  504. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  505. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  506. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  507. return;
  508. }
  509. /*Special case draw a vertical line*/
  510. if(p1.x == p2.x ) {
  511. lv_area_t act_area;
  512. act_area.x1 = p1.x - width_half;
  513. act_area.x2 = p2.x + width_half + width_1;
  514. act_area.y1 = p1.y;
  515. act_area.y2 = p2.y;
  516. lv_area_t draw_area;
  517. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  518. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  519. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  520. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  521. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  522. return;
  523. }
  524. while(1){
  525. if(hor == true && last_y != act_point.y) {
  526. lv_area_t act_area;
  527. lv_area_t draw_area;
  528. act_area.x1 = last_x;
  529. act_area.x2 = act_point.x - sx;
  530. act_area.y1 = last_y - width_half ;
  531. act_area.y2 = act_point.y - sy + width_half + width_1;
  532. last_y = act_point.y;
  533. last_x = act_point.x;
  534. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  535. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  536. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  537. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  538. if(width >= 0) {
  539. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  540. }
  541. #if LV_ANTIALIAS != 0
  542. lv_coord_t seg_w = lv_area_get_width(&draw_area); /*Segment width*/
  543. lv_point_t aa_p1;
  544. lv_point_t aa_p2;
  545. aa_p1.x = draw_area.x1;
  546. aa_p1.y = draw_area.y1 - 1;
  547. aa_p2.x = draw_area.x1;
  548. aa_p2.y = draw_area.y1 + width + 1;
  549. lv_coord_t i;
  550. for(i = 0; i < seg_w; i++) {
  551. lv_opa_t aa_opa = antialias_get_opa(seg_w, i, style->line.opa);
  552. px_fp(aa_p1.x + i, aa_p1.y, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa);
  553. px_fp(aa_p2.x + i, aa_p2.y, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa);
  554. }
  555. #endif
  556. }
  557. if (hor == false && last_x != act_point.x) {
  558. lv_area_t act_area;
  559. lv_area_t draw_area;
  560. act_area.x1 = last_x - width_half;
  561. act_area.x2 = act_point.x - sx + width_half + width_1;
  562. act_area.y1 = last_y ;
  563. act_area.y2 = act_point.y - sy;
  564. last_y = act_point.y;
  565. last_x = act_point.x;
  566. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  567. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  568. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  569. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  570. if(width >= 0) {
  571. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  572. }
  573. #if LV_ANTIALIAS != 0
  574. lv_coord_t seg_h = lv_area_get_height(&draw_area); /*Segment height*/
  575. lv_point_t aa_p1;
  576. lv_point_t aa_p2;
  577. aa_p1.x = draw_area.x1 - 1;
  578. aa_p1.y = draw_area.y1;
  579. aa_p2.x = draw_area.x1 + width + 1;
  580. aa_p2.y = draw_area.y1;
  581. lv_coord_t i;
  582. for(i = 0; i < seg_h; i++) {
  583. lv_opa_t aa_opa = antialias_get_opa(seg_h, i, style->line.opa);
  584. px_fp(aa_p1.x, aa_p1.y + i, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa);
  585. px_fp(aa_p2.x, aa_p2.y + i, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa);
  586. }
  587. #endif
  588. }
  589. /*Calc. the next point of the line*/
  590. if (act_point.x == p2.x && act_point.y == p2.y) break;
  591. e2 = err;
  592. if (e2 >-dx) {
  593. err -= dy;
  594. act_point.x += sx;
  595. }
  596. if (e2 < dy) {
  597. err += dx;
  598. act_point.y += sy;
  599. }
  600. }
  601. /*Draw the last part of the line*/
  602. if(hor == true) {
  603. lv_area_t act_area;
  604. lv_area_t draw_area;
  605. act_area.x1 = last_x;
  606. act_area.x2 = act_point.x;
  607. act_area.y1 = last_y - width_half ;
  608. act_area.y2 = act_point.y + width_half + width_1;
  609. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  610. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  611. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  612. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  613. if(width >= 0) {
  614. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  615. }
  616. #if LV_ANTIALIAS != 0
  617. lv_coord_t seg_w = lv_area_get_width(&draw_area); /*Segment width*/
  618. lv_point_t aa_p1;
  619. lv_point_t aa_p2;
  620. aa_p1.x = draw_area.x1;
  621. aa_p1.y = draw_area.y1 - 1;
  622. aa_p2.x = draw_area.x1;
  623. aa_p2.y = draw_area.y1 + width + 1;
  624. lv_coord_t i;
  625. for(i = 0; i < seg_w; i++) {
  626. lv_opa_t aa_opa = antialias_get_opa(seg_w, i, style->line.opa);
  627. px_fp(aa_p1.x + i, aa_p1.y, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa);
  628. px_fp(aa_p2.x + i, aa_p2.y, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa);
  629. }
  630. #endif
  631. }
  632. if (hor == false) {
  633. lv_area_t act_area;
  634. lv_area_t draw_area;
  635. act_area.x1 = last_x - width_half;
  636. act_area.x2 = act_point.x + width_half + width_1;
  637. act_area.y1 = last_y;
  638. act_area.y2 = act_point.y;
  639. draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
  640. draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
  641. draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
  642. draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
  643. if(width >= 0) {
  644. fill_fp(&draw_area, mask, style->line.color, style->line.opa);
  645. }
  646. #if LV_ANTIALIAS != 0
  647. lv_coord_t seg_h = lv_area_get_height(&draw_area); /*Segment height*/
  648. lv_point_t aa_p1;
  649. lv_point_t aa_p2;
  650. aa_p1.x = draw_area.x1 - 1;
  651. aa_p1.y = draw_area.y1;
  652. aa_p2.x = draw_area.x1 + width + 1;
  653. aa_p2.y = draw_area.y1;
  654. lv_coord_t i;
  655. for(i = 0; i < seg_h; i++) {
  656. lv_opa_t aa_opa = antialias_get_opa(seg_h, i, style->line.opa);
  657. px_fp(aa_p1.x, aa_p1.y + i, mask, style->line.color, aa_invert ? aa_opa : style->line.opa - aa_opa);
  658. px_fp(aa_p2.x, aa_p2.y + i, mask, style->line.color, aa_invert ? style->line.opa - aa_opa : aa_opa);
  659. }
  660. #endif
  661. }
  662. }
  663. /**********************
  664. * STATIC FUNCTIONS
  665. **********************/
  666. /**
  667. * Draw the middle part (rectangular) of a rectangle
  668. * @param coords the coordinates of the original rectangle
  669. * @param mask the rectangle will be drawn only on this area
  670. * @param rects_p pointer to a rectangle style
  671. */
  672. static void lv_draw_rect_main_mid(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  673. {
  674. uint16_t radius = style->body.radius;
  675. lv_color_t mcolor = style->body.main_color;
  676. lv_color_t gcolor = style->body.grad_color;
  677. uint8_t mix;
  678. lv_opa_t opa = style->body.opa;
  679. lv_coord_t height = lv_area_get_height(coords);
  680. lv_coord_t width = lv_area_get_width(coords);
  681. radius = lv_draw_cont_radius_corr(radius, width, height);
  682. /*If the radius is too big then there is no body*/
  683. if(radius > height / 2) return;
  684. lv_area_t work_area;
  685. work_area.x1 = coords->x1;
  686. work_area.x2 = coords->x2;
  687. if(mcolor.full == gcolor.full) {
  688. work_area.y1 = coords->y1 + radius;
  689. work_area.y2 = coords->y2 - radius;
  690. if(style->body.radius != 0) {
  691. #if LV_ANTIALIAS
  692. work_area.y1 += 2;
  693. work_area.y2 -= 2;
  694. #else
  695. work_area.y1 += 1;
  696. work_area.y2 -= 1;
  697. #endif
  698. }
  699. fill_fp(&work_area, mask, mcolor, opa);
  700. } else {
  701. lv_coord_t row;
  702. lv_coord_t row_start = coords->y1 + radius;
  703. lv_coord_t row_end = coords->y2 - radius;
  704. lv_color_t act_color;
  705. if(style->body.radius != 0) {
  706. #if LV_ANTIALIAS
  707. row_start += 2;
  708. row_end -= 2;
  709. #else
  710. row_start += 1;
  711. row_end -= 1;
  712. #endif
  713. }
  714. if(row_start < 0) row_start = 0;
  715. for(row = row_start; row <= row_end; row ++)
  716. {
  717. work_area.y1 = row;
  718. work_area.y2 = row;
  719. mix = (uint32_t)((uint32_t)(coords->y2 - work_area.y1) * 255) / height;
  720. act_color = lv_color_mix(mcolor, gcolor, mix);
  721. fill_fp(&work_area, mask, act_color, opa);
  722. }
  723. }
  724. }
  725. /**
  726. * Draw the top and bottom parts (corners) of a rectangle
  727. * @param coords the coordinates of the original rectangle
  728. * @param mask the rectangle will be drawn only on this area
  729. * @param rects_p pointer to a rectangle style
  730. */
  731. static void lv_draw_rect_main_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  732. {
  733. uint16_t radius = style->body.radius;
  734. lv_color_t mcolor = style->body.main_color;
  735. lv_color_t gcolor = style->body.grad_color;
  736. lv_color_t act_color;
  737. lv_opa_t opa = style->body.opa;
  738. uint8_t mix;
  739. lv_coord_t height = lv_area_get_height(coords);
  740. lv_coord_t width = lv_area_get_width(coords);
  741. radius = lv_draw_cont_radius_corr(radius, width, height);
  742. lv_point_t lt_origo; /*Left Top origo*/
  743. lv_point_t lb_origo; /*Left Bottom origo*/
  744. lv_point_t rt_origo; /*Right Top origo*/
  745. lv_point_t rb_origo; /*Left Bottom origo*/
  746. lt_origo.x = coords->x1 + radius + LV_ANTIALIAS;
  747. lt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
  748. lb_origo.x = coords->x1 + radius + LV_ANTIALIAS;
  749. lb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
  750. rt_origo.x = coords->x2 - radius - LV_ANTIALIAS;
  751. rt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
  752. rb_origo.x = coords->x2 - radius - LV_ANTIALIAS;
  753. rb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
  754. lv_area_t edge_top_area;
  755. lv_area_t mid_top_area;
  756. lv_area_t mid_bot_area;
  757. lv_area_t edge_bot_area;
  758. lv_point_t cir;
  759. lv_coord_t cir_tmp;
  760. lv_circ_init(&cir, &cir_tmp, radius);
  761. /*Init the areas*/
  762. lv_area_set(&mid_bot_area, lb_origo.x + LV_CIRC_OCT4_X(cir),
  763. lb_origo.y + LV_CIRC_OCT4_Y(cir),
  764. rb_origo.x + LV_CIRC_OCT1_X(cir),
  765. rb_origo.y + LV_CIRC_OCT1_Y(cir));
  766. lv_area_set(&edge_bot_area, lb_origo.x + LV_CIRC_OCT3_X(cir),
  767. lb_origo.y + LV_CIRC_OCT3_Y(cir),
  768. rb_origo.x + LV_CIRC_OCT2_X(cir),
  769. rb_origo.y + LV_CIRC_OCT2_Y(cir));
  770. lv_area_set(&mid_top_area, lt_origo.x + LV_CIRC_OCT5_X(cir),
  771. lt_origo.y + LV_CIRC_OCT5_Y(cir),
  772. rt_origo.x + LV_CIRC_OCT8_X(cir),
  773. rt_origo.y + LV_CIRC_OCT8_Y(cir));
  774. lv_area_set(&edge_top_area, lt_origo.x + LV_CIRC_OCT6_X(cir),
  775. lt_origo.y + LV_CIRC_OCT6_Y(cir),
  776. rt_origo.x + LV_CIRC_OCT7_X(cir),
  777. rt_origo.y + LV_CIRC_OCT7_Y(cir));
  778. #if LV_ANTIALIAS
  779. /*Store some internal states for anti-aliasing*/
  780. lv_coord_t out_y_seg_start = 0;
  781. lv_coord_t out_y_seg_end = 0;
  782. lv_coord_t out_x_last = radius;
  783. lv_color_t aa_color_hor_top;
  784. lv_color_t aa_color_hor_bottom;
  785. lv_color_t aa_color_ver;
  786. #endif
  787. while(lv_circ_cont(&cir)) {
  788. #if LV_ANTIALIAS != 0
  789. /*New step in y on the outter circle*/
  790. if(out_x_last != cir.x) {
  791. out_y_seg_end = cir.y;
  792. lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
  793. lv_point_t aa_p;
  794. aa_p.x = out_x_last;
  795. aa_p.y = out_y_seg_start;
  796. mix = (uint32_t)((uint32_t)(radius - out_x_last) * 255) / height;
  797. aa_color_hor_top = lv_color_mix(gcolor, mcolor, mix);
  798. aa_color_hor_bottom = lv_color_mix(mcolor, gcolor, mix);
  799. lv_coord_t i;
  800. for(i = 0; i < seg_size; i++) {
  801. lv_opa_t aa_opa;
  802. if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
  803. aa_opa = antialias_get_opa_circ(seg_size, i, style->body.opa);
  804. } else {
  805. aa_opa = opa - antialias_get_opa(seg_size, i, style->body.opa);
  806. }
  807. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, aa_color_hor_bottom, aa_opa);
  808. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, aa_color_hor_bottom, aa_opa);
  809. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, aa_color_hor_top, aa_opa);
  810. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, aa_color_hor_top, aa_opa);
  811. mix = (uint32_t)((uint32_t)(radius - out_y_seg_start + i) * 255) / height;
  812. aa_color_ver = lv_color_mix(mcolor, gcolor, mix);
  813. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
  814. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
  815. aa_color_ver = lv_color_mix(gcolor, mcolor, mix);
  816. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
  817. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
  818. }
  819. out_x_last = cir.x;
  820. out_y_seg_start = out_y_seg_end;
  821. }
  822. #endif
  823. uint8_t edge_top_refr = 0;
  824. uint8_t mid_top_refr = 0;
  825. uint8_t mid_bot_refr = 0;
  826. uint8_t edge_bot_refr = 0;
  827. /* If a new row coming draw the previous
  828. * The y coordinate can remain the same so wait for a new*/
  829. if(mid_bot_area.y1 != LV_CIRC_OCT4_Y(cir) + lb_origo.y ) mid_bot_refr = 1;
  830. if(edge_bot_area.y1 != LV_CIRC_OCT2_Y(cir) + lb_origo.y) edge_bot_refr = 1;
  831. if(mid_top_area.y1 != LV_CIRC_OCT8_Y(cir) + lt_origo.y) mid_top_refr = 1;
  832. if(edge_top_area.y1 != LV_CIRC_OCT7_Y(cir) + lt_origo.y) edge_top_refr = 1;
  833. /*Draw the areas which are not disabled*/
  834. if(edge_top_refr != 0){
  835. if(mcolor.full == gcolor.full) act_color = mcolor;
  836. else {
  837. mix = (uint32_t)((uint32_t)(coords->y2 - edge_top_area.y1) * 255) / height;
  838. act_color = lv_color_mix(mcolor, gcolor, mix);
  839. }
  840. fill_fp(&edge_top_area, mask, act_color, opa);
  841. }
  842. if(mid_top_refr != 0) {
  843. if(mcolor.full == gcolor.full) act_color = mcolor;
  844. else {
  845. mix = (uint32_t)((uint32_t)(coords->y2 - mid_top_area.y1) * 255) / height;
  846. act_color = lv_color_mix(mcolor, gcolor, mix);
  847. }
  848. fill_fp(&mid_top_area, mask, act_color, opa);
  849. }
  850. if(mid_bot_refr != 0) {
  851. if(mcolor.full == gcolor.full) act_color = mcolor;
  852. else {
  853. mix = (uint32_t)((uint32_t)(coords->y2 - mid_bot_area.y1) * 255) / height;
  854. act_color = lv_color_mix(mcolor, gcolor, mix);
  855. }
  856. fill_fp(&mid_bot_area, mask, act_color, opa);
  857. }
  858. if(edge_bot_refr != 0) {
  859. if(mcolor.full == gcolor.full) act_color = mcolor;
  860. else {
  861. mix = (uint32_t)((uint32_t)(coords->y2 - edge_bot_area.y1) * 255) / height;
  862. act_color = lv_color_mix(mcolor, gcolor, mix);
  863. }
  864. fill_fp(&edge_bot_area, mask, act_color, opa);
  865. }
  866. /*Save the current coordinates*/
  867. lv_area_set(&mid_bot_area, lb_origo.x + LV_CIRC_OCT4_X(cir),
  868. lb_origo.y + LV_CIRC_OCT4_Y(cir),
  869. rb_origo.x + LV_CIRC_OCT1_X(cir),
  870. rb_origo.y + LV_CIRC_OCT1_Y(cir));
  871. lv_area_set(&edge_bot_area, lb_origo.x + LV_CIRC_OCT3_X(cir),
  872. lb_origo.y + LV_CIRC_OCT3_Y(cir),
  873. rb_origo.x + LV_CIRC_OCT2_X(cir),
  874. rb_origo.y + LV_CIRC_OCT2_Y(cir));
  875. lv_area_set(&mid_top_area, lt_origo.x + LV_CIRC_OCT5_X(cir),
  876. lt_origo.y + LV_CIRC_OCT5_Y(cir),
  877. rt_origo.x + LV_CIRC_OCT8_X(cir),
  878. rt_origo.y + LV_CIRC_OCT8_Y(cir));
  879. lv_area_set(&edge_top_area, lt_origo.x + LV_CIRC_OCT6_X(cir),
  880. lt_origo.y + LV_CIRC_OCT6_Y(cir),
  881. rt_origo.x + LV_CIRC_OCT7_X(cir),
  882. rt_origo.y + LV_CIRC_OCT7_Y(cir));
  883. lv_circ_next(&cir, &cir_tmp);
  884. }
  885. if(mcolor.full == gcolor.full) act_color = mcolor;
  886. else {
  887. mix = (uint32_t)((uint32_t)(coords->y2 - edge_top_area.y1) * 255) / height;
  888. act_color = lv_color_mix(mcolor, gcolor, mix);
  889. }
  890. fill_fp(&edge_top_area, mask, act_color, opa);
  891. if(edge_top_area.y1 != mid_top_area.y1) {
  892. if(mcolor.full == gcolor.full) act_color = mcolor;
  893. else {
  894. mix = (uint32_t)((uint32_t)(coords->y2 - mid_top_area.y1) * 255) / height;
  895. act_color = lv_color_mix(mcolor, gcolor, mix);
  896. }
  897. fill_fp(&mid_top_area, mask, act_color, opa);
  898. }
  899. if(mcolor.full == gcolor.full) act_color = mcolor;
  900. else {
  901. mix = (uint32_t)((uint32_t)(coords->y2 - mid_bot_area.y1) * 255) / height;
  902. act_color = lv_color_mix(mcolor, gcolor, mix);
  903. }
  904. fill_fp(&mid_bot_area, mask, act_color, opa);
  905. if(edge_bot_area.y1 != mid_bot_area.y1) {
  906. if(mcolor.full == gcolor.full) act_color = mcolor;
  907. else {
  908. mix = (uint32_t)((uint32_t)(coords->y2 - edge_bot_area.y1) * 255) / height;
  909. act_color = lv_color_mix(mcolor, gcolor, mix);
  910. }
  911. fill_fp(&edge_bot_area, mask, act_color, opa);
  912. }
  913. #if LV_ANTIALIAS
  914. /*The first and the last line is not drawn*/
  915. edge_top_area.x1 = coords->x1 + radius + 2;
  916. edge_top_area.x2 = coords->x2 - radius - 2;
  917. edge_top_area.y1 = coords->y1;
  918. edge_top_area.y2 = coords->y1;
  919. fill_fp(&edge_top_area, mask, style->body.main_color, opa);
  920. edge_top_area.y1 = coords->y2;
  921. edge_top_area.y2 = coords->y2;
  922. fill_fp(&edge_top_area, mask, style->body.grad_color, opa);
  923. /*Last parts of the anti-alias*/
  924. out_y_seg_end = cir.y;
  925. lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
  926. lv_point_t aa_p;
  927. aa_p.x = out_x_last;
  928. aa_p.y = out_y_seg_start;
  929. mix = (uint32_t)((uint32_t)(radius - out_x_last) * 255) / height;
  930. aa_color_hor_bottom = lv_color_mix(gcolor, mcolor, mix);
  931. aa_color_hor_top = lv_color_mix(mcolor, gcolor, mix);
  932. lv_coord_t i;
  933. for(i = 0; i < seg_size; i++) {
  934. lv_opa_t aa_opa = opa - antialias_get_opa(seg_size, i, opa);
  935. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, aa_color_hor_top, aa_opa);
  936. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, aa_color_hor_top, aa_opa);
  937. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, aa_color_hor_bottom, aa_opa);
  938. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, aa_color_hor_bottom, aa_opa);
  939. mix = (uint32_t)((uint32_t)(radius - out_y_seg_start + i) * 255) / height;
  940. aa_color_ver = lv_color_mix(mcolor, gcolor, mix);
  941. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
  942. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
  943. aa_color_ver = lv_color_mix(gcolor, mcolor, mix);
  944. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
  945. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
  946. }
  947. /*In some cases the last pixel is not drawn*/
  948. if(LV_MATH_ABS(aa_p.x - aa_p.y) == seg_size) {
  949. aa_p.x = out_x_last;
  950. aa_p.y = out_x_last;
  951. mix = (uint32_t)((uint32_t)(out_x_last) * 255) / height;
  952. aa_color_hor_top = lv_color_mix(gcolor, mcolor, mix);
  953. aa_color_hor_bottom = lv_color_mix(mcolor, gcolor, mix);
  954. lv_opa_t aa_opa = style->body.opa >> 1;
  955. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p), rb_origo.y + LV_CIRC_OCT2_Y(aa_p), mask, aa_color_hor_bottom, aa_opa);
  956. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p), lb_origo.y + LV_CIRC_OCT4_Y(aa_p), mask, aa_color_hor_bottom, aa_opa);
  957. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p), lt_origo.y + LV_CIRC_OCT6_Y(aa_p), mask, aa_color_hor_top, aa_opa);
  958. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p), rt_origo.y + LV_CIRC_OCT8_Y(aa_p), mask, aa_color_hor_top, aa_opa);
  959. }
  960. #endif
  961. }
  962. /**
  963. * Draw the straight parts of a rectangle border
  964. * @param coords the coordinates of the original rectangle
  965. * @param mask_ the rectangle will be drawn only on this area
  966. * @param rstyle pointer to a rectangle style
  967. */
  968. static void lv_draw_rect_border_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  969. {
  970. uint16_t radius = style->body.radius;
  971. lv_coord_t width = lv_area_get_width(coords);
  972. lv_coord_t height = lv_area_get_height(coords);
  973. uint16_t bwidth = style->body.border.width;
  974. lv_opa_t opa = style->body.border.opa;
  975. lv_border_part_t part = style->body.border.part;
  976. lv_color_t color = style->body.border.color;
  977. lv_area_t work_area;
  978. lv_coord_t length_corr = 0;
  979. lv_coord_t corner_size = 0;
  980. /*the 0 px border width drawn as 1 px, so decrement the b_width*/
  981. bwidth--;
  982. radius = lv_draw_cont_radius_corr(radius, width, height);
  983. if(radius < bwidth) {
  984. length_corr = bwidth - radius - LV_ANTIALIAS;
  985. corner_size = bwidth;
  986. } else {
  987. corner_size = radius + LV_ANTIALIAS;
  988. }
  989. /* Modify the corner_size if corner is drawn */
  990. corner_size ++;
  991. /*Depending one which part's are drawn modify the area lengths */
  992. if(part & LV_BORDER_TOP) work_area.y1 = coords->y1 + corner_size;
  993. else work_area.y1 = coords->y1 + radius;
  994. if(part & LV_BORDER_BOTTOM) work_area.y2 = coords->y2 - corner_size;
  995. else work_area.y2 = coords->y2 - radius;
  996. /*Left border*/
  997. if(part & LV_BORDER_LEFT) {
  998. work_area.x1 = coords->x1;
  999. work_area.x2 = work_area.x1 + bwidth;
  1000. fill_fp(&work_area, mask, color, opa);
  1001. }
  1002. /*Right border*/
  1003. if(part & LV_BORDER_RIGHT) {
  1004. work_area.x2 = coords->x2;
  1005. work_area.x1 = work_area.x2 - bwidth;
  1006. fill_fp(&work_area, mask, color, opa);
  1007. }
  1008. work_area.x1 = coords->x1 + corner_size - length_corr;
  1009. work_area.x2 = coords->x2 - corner_size + length_corr;
  1010. /*Upper border*/
  1011. if(part & LV_BORDER_TOP) {
  1012. work_area.y1 = coords->y1;
  1013. work_area.y2 = coords->y1 + bwidth;
  1014. fill_fp(&work_area, mask, color, opa);
  1015. }
  1016. /*Lower border*/
  1017. if(part & LV_BORDER_BOTTOM) {
  1018. work_area.y2 = coords->y2;
  1019. work_area.y1 = work_area.y2 - bwidth;
  1020. fill_fp(&work_area, mask, color, opa);
  1021. }
  1022. /*Draw the a remaining rectangles if the radius is smaller then bwidth */
  1023. if(length_corr != 0) {
  1024. /*Left top correction*/
  1025. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1026. work_area.x1 = coords->x1;
  1027. work_area.x2 = coords->x1 + radius + LV_ANTIALIAS;
  1028. work_area.y1 = coords->y1 + radius + 1 + LV_ANTIALIAS;
  1029. work_area.y2 = coords->y1 + bwidth;
  1030. fill_fp(&work_area, mask, color, opa);
  1031. }
  1032. /*Right top correction*/
  1033. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1034. work_area.x1 = coords->x2 - radius - LV_ANTIALIAS;
  1035. work_area.x2 = coords->x2;
  1036. work_area.y1 = coords->y1 + radius + 1 + LV_ANTIALIAS;
  1037. work_area.y2 = coords->y1 + bwidth;
  1038. fill_fp(&work_area, mask, color, opa);
  1039. }
  1040. /*Left bottom correction*/
  1041. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1042. work_area.x1 = coords->x1;
  1043. work_area.x2 = coords->x1 + radius + LV_ANTIALIAS;
  1044. work_area.y1 = coords->y2 - bwidth;
  1045. work_area.y2 = coords->y2 - radius - 1 - LV_ANTIALIAS;
  1046. fill_fp(&work_area, mask, color, opa);
  1047. }
  1048. /*Right bottom correction*/
  1049. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1050. work_area.x1 = coords->x2 - radius - LV_ANTIALIAS;
  1051. work_area.x2 = coords->x2;
  1052. work_area.y1 = coords->y2 - bwidth;
  1053. work_area.y2 = coords->y2 - radius - 1 - LV_ANTIALIAS;
  1054. fill_fp(&work_area, mask, color, opa);
  1055. }
  1056. }
  1057. /*If radius == 0 one px on the corners are not drawn by main drawer*/
  1058. if(style->body.radius == 0) {
  1059. /*Left top corner*/
  1060. if(part & (LV_BORDER_TOP | LV_BORDER_LEFT)) {
  1061. work_area.x1 = coords->x1;
  1062. work_area.x2 = coords->x1 + LV_ANTIALIAS;
  1063. work_area.y1 = coords->y1;
  1064. work_area.y2 = coords->y1 + LV_ANTIALIAS;
  1065. fill_fp(&work_area, mask, color, opa);
  1066. }
  1067. /*Right top corner*/
  1068. if(part & (LV_BORDER_TOP | LV_BORDER_RIGHT)) {
  1069. work_area.x1 = coords->x2 - LV_ANTIALIAS;
  1070. work_area.x2 = coords->x2;
  1071. work_area.y1 = coords->y1;
  1072. work_area.y2 = coords->y1 + LV_ANTIALIAS;
  1073. fill_fp(&work_area, mask, color, opa);
  1074. }
  1075. /*Left bottom corner*/
  1076. if(part & (LV_BORDER_BOTTOM | LV_BORDER_LEFT)) {
  1077. work_area.x1 = coords->x1;
  1078. work_area.x2 = coords->x1 + LV_ANTIALIAS;
  1079. work_area.y1 = coords->y2 - LV_ANTIALIAS;
  1080. work_area.y2 = coords->y2;
  1081. fill_fp(&work_area, mask, color, opa);
  1082. }
  1083. /*Right bottom corner*/
  1084. if(part & (LV_BORDER_BOTTOM | LV_BORDER_RIGHT)) {
  1085. work_area.x1 = coords->x2 - LV_ANTIALIAS;
  1086. work_area.x2 = coords->x2;
  1087. work_area.y1 = coords->y2 - LV_ANTIALIAS;
  1088. work_area.y2 = coords->y2;
  1089. fill_fp(&work_area, mask, color, opa);
  1090. }
  1091. }
  1092. }
  1093. /**
  1094. * Draw the corners of a rectangle border
  1095. * @param coords the coordinates of the original rectangle
  1096. * @param mask the rectangle will be drawn only on this area
  1097. * @param rects_p pointer to a rectangle style
  1098. * @param opa opacity of the rectangle (0..255)
  1099. */
  1100. static void lv_draw_rect_border_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  1101. {
  1102. uint16_t radius = style->body.radius ;
  1103. uint16_t bwidth = style->body.border.width;
  1104. lv_color_t color = style->body.border.color;
  1105. lv_opa_t opa = style->body.border.opa;
  1106. lv_border_part_t part = style->body.border.part;
  1107. /*0 px border width drawn as 1 px, so decrement the bwidth*/
  1108. bwidth--;
  1109. #if LV_ANTIALIAS
  1110. bwidth--; /*Because of anti-aliasing the border seems one pixel ticker*/
  1111. #endif
  1112. lv_coord_t width = lv_area_get_width(coords);
  1113. lv_coord_t height = lv_area_get_height(coords);
  1114. radius = lv_draw_cont_radius_corr(radius, width, height);
  1115. lv_point_t lt_origo; /*Left Top origo*/
  1116. lv_point_t lb_origo; /*Left Bottom origo*/
  1117. lv_point_t rt_origo; /*Right Top origo*/
  1118. lv_point_t rb_origo; /*Left Bottom origo*/
  1119. lt_origo.x = coords->x1 + radius + LV_ANTIALIAS;
  1120. lt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
  1121. lb_origo.x = coords->x1 + radius + LV_ANTIALIAS;
  1122. lb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
  1123. rt_origo.x = coords->x2 - radius - LV_ANTIALIAS;
  1124. rt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
  1125. rb_origo.x = coords->x2 - radius - LV_ANTIALIAS;
  1126. rb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
  1127. lv_point_t cir_out;
  1128. lv_coord_t tmp_out;
  1129. lv_circ_init(&cir_out, &tmp_out, radius);
  1130. lv_point_t cir_in;
  1131. lv_coord_t tmp_in;
  1132. lv_coord_t radius_in = radius - bwidth;
  1133. if(radius_in < 0){
  1134. radius_in = 0;
  1135. }
  1136. lv_circ_init(&cir_in, &tmp_in, radius_in);
  1137. lv_area_t circ_area;
  1138. lv_coord_t act_w1;
  1139. lv_coord_t act_w2;
  1140. #if LV_ANTIALIAS
  1141. /*Store some internal states for anti-aliasing*/
  1142. lv_coord_t out_y_seg_start = 0;
  1143. lv_coord_t out_y_seg_end = 0;
  1144. lv_coord_t out_x_last = radius;
  1145. lv_coord_t in_y_seg_start = 0;
  1146. lv_coord_t in_y_seg_end = 0;
  1147. lv_coord_t in_x_last = radius - bwidth;
  1148. #endif
  1149. while( cir_out.y <= cir_out.x) {
  1150. /*Calculate the actual width to avoid overwriting pixels*/
  1151. if(cir_in.y < cir_in.x) {
  1152. act_w1 = cir_out.x - cir_in.x;
  1153. act_w2 = act_w1;
  1154. } else {
  1155. act_w1 = cir_out.x - cir_out.y;
  1156. act_w2 = act_w1 - 1;
  1157. }
  1158. #if LV_ANTIALIAS != 0
  1159. /*New step in y on the outter circle*/
  1160. if(out_x_last != cir_out.x) {
  1161. out_y_seg_end = cir_out.y;
  1162. lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
  1163. lv_point_t aa_p;
  1164. aa_p.x = out_x_last;
  1165. aa_p.y = out_y_seg_start;
  1166. lv_coord_t i;
  1167. for(i = 0; i < seg_size; i++) {
  1168. lv_opa_t aa_opa;
  1169. if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
  1170. aa_opa = antialias_get_opa_circ(seg_size, i, style->body.border.opa);
  1171. } else {
  1172. aa_opa = style->body.border.opa - antialias_get_opa(seg_size, i, style->body.border.opa);
  1173. }
  1174. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1175. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1176. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1177. }
  1178. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1179. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1180. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1181. }
  1182. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1183. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1184. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1185. }
  1186. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1187. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1188. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1189. }
  1190. }
  1191. out_x_last = cir_out.x;
  1192. out_y_seg_start = out_y_seg_end;
  1193. }
  1194. /*New step in y on the inner circle*/
  1195. if(in_x_last != cir_in.x) {
  1196. in_y_seg_end = cir_out.y;
  1197. lv_coord_t seg_size = in_y_seg_end - in_y_seg_start;
  1198. lv_point_t aa_p;
  1199. aa_p.x = in_x_last;
  1200. aa_p.y = in_y_seg_start;
  1201. lv_coord_t i;
  1202. for(i = 0; i < seg_size; i++) {
  1203. lv_opa_t aa_opa;
  1204. if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
  1205. aa_opa = style->body.border.opa - antialias_get_opa_circ(seg_size, i, style->body.border.opa);
  1206. } else {
  1207. aa_opa = antialias_get_opa(seg_size, i, style->body.border.opa);
  1208. }
  1209. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1210. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) - 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1211. }
  1212. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1213. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1214. }
  1215. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1216. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) + 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1217. }
  1218. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1219. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1220. }
  1221. /*Be sure the pixels on the middle are not drawn twice*/
  1222. if(LV_CIRC_OCT1_X(aa_p) - 1 != LV_CIRC_OCT2_X(aa_p) + i) {
  1223. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1224. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1225. }
  1226. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1227. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) + 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1228. }
  1229. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1230. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1231. }
  1232. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1233. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) - 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1234. }
  1235. }
  1236. }
  1237. in_x_last = cir_in.x;
  1238. in_y_seg_start = in_y_seg_end;
  1239. }
  1240. #endif
  1241. /*Draw the octets to the right bottom corner*/
  1242. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1243. circ_area.x1 = rb_origo.x + LV_CIRC_OCT1_X(cir_out) - act_w2;
  1244. circ_area.x2 = rb_origo.x + LV_CIRC_OCT1_X(cir_out);
  1245. circ_area.y1 = rb_origo.y + LV_CIRC_OCT1_Y(cir_out);
  1246. circ_area.y2 = rb_origo.y + LV_CIRC_OCT1_Y(cir_out);
  1247. fill_fp(&circ_area, mask, color, opa);
  1248. circ_area.x1 = rb_origo.x + LV_CIRC_OCT2_X(cir_out);
  1249. circ_area.x2 = rb_origo.x + LV_CIRC_OCT2_X(cir_out);
  1250. circ_area.y1 = rb_origo.y + LV_CIRC_OCT2_Y(cir_out)- act_w1;
  1251. circ_area.y2 = rb_origo.y + LV_CIRC_OCT2_Y(cir_out);
  1252. fill_fp(&circ_area, mask, color, opa);
  1253. }
  1254. /*Draw the octets to the left bottom corner*/
  1255. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1256. circ_area.x1 = lb_origo.x + LV_CIRC_OCT3_X(cir_out);
  1257. circ_area.x2 = lb_origo.x + LV_CIRC_OCT3_X(cir_out);
  1258. circ_area.y1 = lb_origo.y + LV_CIRC_OCT3_Y(cir_out) - act_w2;
  1259. circ_area.y2 = lb_origo.y + LV_CIRC_OCT3_Y(cir_out);
  1260. fill_fp(&circ_area, mask, color, opa);
  1261. circ_area.x1 = lb_origo.x + LV_CIRC_OCT4_X(cir_out);
  1262. circ_area.x2 = lb_origo.x + LV_CIRC_OCT4_X(cir_out) + act_w1;
  1263. circ_area.y1 = lb_origo.y + LV_CIRC_OCT4_Y(cir_out);
  1264. circ_area.y2 = lb_origo.y + LV_CIRC_OCT4_Y(cir_out);
  1265. fill_fp(&circ_area, mask, color, opa);
  1266. }
  1267. /*Draw the octets to the left top corner*/
  1268. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1269. if(lb_origo.y + LV_CIRC_OCT4_Y(cir_out) > lt_origo.y + LV_CIRC_OCT5_Y(cir_out)) {
  1270. /*Don't draw if the lines are common in the middle*/
  1271. circ_area.x1 = lt_origo.x + LV_CIRC_OCT5_X(cir_out);
  1272. circ_area.x2 = lt_origo.x + LV_CIRC_OCT5_X(cir_out) + act_w2;
  1273. circ_area.y1 = lt_origo.y + LV_CIRC_OCT5_Y(cir_out);
  1274. circ_area.y2 = lt_origo.y + LV_CIRC_OCT5_Y(cir_out);
  1275. fill_fp(&circ_area, mask, color, opa);
  1276. }
  1277. circ_area.x1 = lt_origo.x + LV_CIRC_OCT6_X(cir_out);
  1278. circ_area.x2 = lt_origo.x + LV_CIRC_OCT6_X(cir_out);
  1279. circ_area.y1 = lt_origo.y + LV_CIRC_OCT6_Y(cir_out);
  1280. circ_area.y2 = lt_origo.y + LV_CIRC_OCT6_Y(cir_out) + act_w1;
  1281. fill_fp(&circ_area, mask, color, opa);
  1282. }
  1283. /*Draw the octets to the right top corner*/
  1284. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1285. circ_area.x1 = rt_origo.x + LV_CIRC_OCT7_X(cir_out);
  1286. circ_area.x2 = rt_origo.x + LV_CIRC_OCT7_X(cir_out);
  1287. circ_area.y1 = rt_origo.y + LV_CIRC_OCT7_Y(cir_out);
  1288. circ_area.y2 = rt_origo.y + LV_CIRC_OCT7_Y(cir_out) + act_w2;
  1289. fill_fp(&circ_area, mask, color, opa);
  1290. /*Don't draw if the lines are common in the middle*/
  1291. if(rb_origo.y + LV_CIRC_OCT1_Y(cir_out) > rt_origo.y + LV_CIRC_OCT8_Y(cir_out)) {
  1292. circ_area.x1 = rt_origo.x + LV_CIRC_OCT8_X(cir_out) - act_w1;
  1293. circ_area.x2 = rt_origo.x + LV_CIRC_OCT8_X(cir_out);
  1294. circ_area.y1 = rt_origo.y + LV_CIRC_OCT8_Y(cir_out);
  1295. circ_area.y2 = rt_origo.y + LV_CIRC_OCT8_Y(cir_out);
  1296. fill_fp(&circ_area, mask, color, opa);
  1297. }
  1298. }
  1299. lv_circ_next(&cir_out, &tmp_out);
  1300. /*The internal circle will be ready faster
  1301. * so check it! */
  1302. if(cir_in.y < cir_in.x) {
  1303. lv_circ_next(&cir_in, &tmp_in);
  1304. }
  1305. }
  1306. #if LV_ANTIALIAS != 0
  1307. /*Last parts of the outer anti-alias*/
  1308. out_y_seg_end = cir_out.y;
  1309. lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
  1310. lv_point_t aa_p;
  1311. aa_p.x = out_x_last;
  1312. aa_p.y = out_y_seg_start;
  1313. lv_coord_t i;
  1314. for(i = 0; i < seg_size; i++) {
  1315. lv_opa_t aa_opa = style->body.border.opa - antialias_get_opa(seg_size, i, style->body.border.opa);
  1316. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1317. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1318. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1319. }
  1320. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1321. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1322. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1323. }
  1324. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1325. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1326. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1327. }
  1328. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1329. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1330. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1331. }
  1332. }
  1333. /*In some cases the last pixel in the outer middle is not drawn*/
  1334. if(LV_MATH_ABS(aa_p.x - aa_p.y) == seg_size) {
  1335. aa_p.x = out_x_last;
  1336. aa_p.y = out_x_last;
  1337. lv_opa_t aa_opa = style->body.border.opa >> 1;
  1338. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1339. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p), rb_origo.y + LV_CIRC_OCT2_Y(aa_p), mask, style->body.border.color, aa_opa);
  1340. }
  1341. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1342. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p), lb_origo.y + LV_CIRC_OCT4_Y(aa_p), mask, style->body.border.color, aa_opa);
  1343. }
  1344. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1345. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p), lt_origo.y + LV_CIRC_OCT6_Y(aa_p), mask, style->body.border.color, aa_opa);
  1346. }
  1347. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1348. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p), rt_origo.y + LV_CIRC_OCT8_Y(aa_p), mask, style->body.border.color, aa_opa);
  1349. }
  1350. }
  1351. /*Last parts of the inner anti-alias*/
  1352. in_y_seg_end = cir_in.y;
  1353. aa_p.x = in_x_last;
  1354. aa_p.y = in_y_seg_start;
  1355. seg_size = in_y_seg_end - in_y_seg_start;
  1356. for(i = 0; i < seg_size; i++) {
  1357. lv_opa_t aa_opa = antialias_get_opa(seg_size, i, style->body.border.opa);
  1358. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1359. px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) - 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1360. }
  1361. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1362. px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1363. }
  1364. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1365. px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) + 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1366. }
  1367. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1368. px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1369. }
  1370. if(LV_CIRC_OCT1_X(aa_p) - 1 != LV_CIRC_OCT2_X(aa_p) + i) {
  1371. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
  1372. px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
  1373. }
  1374. if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
  1375. px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) + 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
  1376. }
  1377. if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
  1378. px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
  1379. }
  1380. if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
  1381. px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) - 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
  1382. }
  1383. }
  1384. }
  1385. #endif
  1386. }
  1387. #if USE_LV_SHADOW && LV_VDB_SIZE
  1388. /**
  1389. * Draw a shadow
  1390. * @param rect pointer to rectangle object
  1391. * @param mask pointer to a mask area (from the design functions)
  1392. */
  1393. static void lv_draw_shadow(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  1394. {
  1395. /* If mask is in the middle of cords do not draw shadow*/
  1396. lv_coord_t radius = style->body.radius;
  1397. lv_coord_t width = lv_area_get_width(coords);
  1398. lv_coord_t height = lv_area_get_height(coords);
  1399. radius = lv_draw_cont_radius_corr(radius, width, height);
  1400. lv_area_t area_tmp;
  1401. /*Check horizontally without radius*/
  1402. lv_area_copy(&area_tmp, coords);
  1403. area_tmp.x1 += radius;
  1404. area_tmp.x2 -= radius;
  1405. if(lv_area_is_in(mask, &area_tmp) != false) return;
  1406. /*Check vertically without radius*/
  1407. lv_area_copy(&area_tmp, coords);
  1408. area_tmp.y1 += radius;
  1409. area_tmp.y2 -= radius;
  1410. if(lv_area_is_in(mask, &area_tmp) != false) return;
  1411. if(style->body.shadow.type == LV_SHADOW_FULL) {
  1412. lv_draw_shadow_full(coords, mask, style);
  1413. } else if(style->body.shadow.type == LV_SHADOW_BOTTOM) {
  1414. lv_draw_shadow_bottom(coords, mask, style);
  1415. }
  1416. }
  1417. static void lv_draw_shadow_full(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  1418. {
  1419. lv_coord_t radius = style->body.radius;
  1420. lv_coord_t swidth = style->body.shadow.width;
  1421. lv_coord_t width = lv_area_get_width(coords);
  1422. lv_coord_t height = lv_area_get_height(coords);
  1423. radius = lv_draw_cont_radius_corr(radius, width, height);
  1424. if(radius != 0) radius -= LV_ANTIALIAS;
  1425. swidth += LV_ANTIALIAS;
  1426. #if LV_COMPILER_VLA_SUPPORTED
  1427. lv_coord_t curve_x[radius + swidth + 1]; /*Stores the 'x' coordinates of a quarter circle.*/
  1428. #else
  1429. # if LV_HOR_RES > LV_VER_RES
  1430. lv_coord_t curve_x[LV_HOR_RES];
  1431. # else
  1432. lv_coord_t curve_x[LV_VER_RES];
  1433. # endif
  1434. #endif
  1435. memset(curve_x, 0, sizeof(curve_x));
  1436. lv_point_t circ;
  1437. lv_coord_t circ_tmp;
  1438. lv_circ_init(&circ, &circ_tmp, radius);
  1439. while(lv_circ_cont(&circ)) {
  1440. curve_x[LV_CIRC_OCT1_Y(circ)] = LV_CIRC_OCT1_X(circ);
  1441. curve_x[LV_CIRC_OCT2_Y(circ)] = LV_CIRC_OCT2_X(circ);
  1442. lv_circ_next(&circ, &circ_tmp);
  1443. }
  1444. int16_t line;
  1445. int16_t filter_width = 2 * swidth + 1;
  1446. #if LV_COMPILER_VLA_SUPPORTED
  1447. uint32_t line_1d_blur[filter_width];
  1448. #else
  1449. # if LV_HOR_RES > LV_VER_RES
  1450. uint32_t line_1d_blur[LV_HOR_RES];
  1451. # else
  1452. uint32_t line_1d_blur[LV_VER_RES];
  1453. # endif
  1454. #endif
  1455. /*1D Blur horizontally*/
  1456. for(line = 0; line < filter_width; line++) {
  1457. line_1d_blur[line] = (uint32_t)((uint32_t)(filter_width - line) * (style->body.opa * 2) << SHADOW_OPA_EXTRA_PRECISION) / (filter_width * filter_width);
  1458. }
  1459. uint16_t col;
  1460. #if LV_COMPILER_VLA_SUPPORTED
  1461. lv_opa_t line_2d_blur[radius + swidth];
  1462. #else
  1463. # if LV_HOR_RES > LV_VER_RES
  1464. lv_opa_t line_2d_blur[LV_HOR_RES];
  1465. # else
  1466. lv_opa_t line_2d_blur[LV_VER_RES];
  1467. # endif
  1468. #endif
  1469. lv_point_t point_rt;
  1470. lv_point_t point_rb;
  1471. lv_point_t point_lt;
  1472. lv_point_t point_lb;
  1473. lv_point_t ofs_rb;
  1474. lv_point_t ofs_rt;
  1475. lv_point_t ofs_lb;
  1476. lv_point_t ofs_lt;
  1477. ofs_rb.x = coords->x2 - radius - LV_ANTIALIAS;
  1478. ofs_rb.y = coords->y2 - radius - LV_ANTIALIAS;
  1479. ofs_rt.x = coords->x2 - radius - LV_ANTIALIAS;
  1480. ofs_rt.y = coords->y1 + radius + LV_ANTIALIAS;
  1481. ofs_lb.x = coords->x1 + radius + LV_ANTIALIAS;
  1482. ofs_lb.y = coords->y2 - radius - LV_ANTIALIAS;
  1483. ofs_lt.x = coords->x1 + radius + LV_ANTIALIAS;
  1484. ofs_lt.y = coords->y1 + radius + LV_ANTIALIAS;
  1485. bool line_ready;
  1486. for(line = 1; line <= radius + swidth; line++) { /*Check all rows and make the 1D blur to 2D*/
  1487. line_ready = false;
  1488. for(col = 1; col < radius + swidth; col++) { /*Check all pixels in a 1D blur line (from the origo to last shadow pixel (radius + swidth))*/
  1489. /*Sum the opacities from the lines above and below this 'row'*/
  1490. int16_t line_rel;
  1491. uint32_t px_opa_sum = 0;
  1492. for(line_rel = -swidth; line_rel <= swidth; line_rel ++) {
  1493. /*Get the relative x position of the 'line_rel' to 'line'*/
  1494. int16_t col_rel;
  1495. if(line + line_rel < 0) { /*Below the radius, here is the blur of the edge */
  1496. col_rel = radius - curve_x[line] - col;
  1497. } else if(line + line_rel > radius) { /*Above the radius, here won't be more 1D blur*/
  1498. break;
  1499. } else { /*Blur from the curve*/
  1500. col_rel = curve_x[line + line_rel] - curve_x[line] - col;
  1501. }
  1502. /*Add the value of the 1D blur on 'col_rel' position*/
  1503. if(col_rel < -swidth) { /*Outside of the blurred area. */
  1504. if(line_rel == -swidth) line_ready = true; /*If no data even on the very first line then it wont't be anything else in this line*/
  1505. break; /*Break anyway because only smaller 'col_rel' values will come */
  1506. }
  1507. else if (col_rel > swidth) px_opa_sum += line_1d_blur[0]; /*Inside the not blurred area*/
  1508. else px_opa_sum += line_1d_blur[swidth - col_rel]; /*On the 1D blur (+ swidth to align to the center)*/
  1509. }
  1510. line_2d_blur[col] = px_opa_sum >> SHADOW_OPA_EXTRA_PRECISION;
  1511. if(line_ready) {
  1512. col++; /*To make this line to the last one ( drawing will go to '< col')*/
  1513. break;
  1514. }
  1515. }
  1516. /*Flush the line*/
  1517. point_rt.x = curve_x[line] + ofs_rt.x + 1;
  1518. point_rt.y = ofs_rt.y - line;
  1519. point_rb.x = curve_x[line] + ofs_rb.x + 1;
  1520. point_rb.y = ofs_rb.y + line;
  1521. point_lt.x = ofs_lt.x - curve_x[line] - 1;
  1522. point_lt.y = ofs_lt.y - line;
  1523. point_lb.x = ofs_lb.x - curve_x[line] - 1;
  1524. point_lb.y = ofs_lb.y + line;
  1525. uint16_t d;
  1526. for(d = 1; d < col; d++) {
  1527. if(point_rt.x != point_lt.x) {
  1528. px_fp(point_lt.x,point_lt.y , mask, style->body.shadow.color, line_2d_blur[d]);
  1529. }
  1530. if(point_rb.x != point_lb.x && point_lt.y != point_lb.y) {
  1531. px_fp(point_lb.x,point_lb.y , mask, style->body.shadow.color, line_2d_blur[d]);
  1532. }
  1533. if(point_lt.y != point_lb.y) {
  1534. px_fp(point_rb.x,point_rb.y , mask, style->body.shadow.color, line_2d_blur[d]);
  1535. }
  1536. px_fp(point_rt.x,point_rt.y , mask, style->body.shadow.color, line_2d_blur[d]);
  1537. point_rb.x++;
  1538. point_lb.x--;
  1539. point_rt.x++;
  1540. point_lt.x--;
  1541. }
  1542. /* Put the first line to the edges too.
  1543. * It is not correct because blur should be done below the corner too
  1544. * but is is simple, fast and gives a good enough result*/
  1545. if(line == 1) lv_draw_shadow_full_straight(coords, mask, style, line_2d_blur);
  1546. }
  1547. }
  1548. static void lv_draw_shadow_bottom(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style)
  1549. {
  1550. lv_coord_t radius = style->body.radius;
  1551. lv_coord_t swidth = style->body.shadow.width;
  1552. lv_coord_t width = lv_area_get_width(coords);
  1553. lv_coord_t height = lv_area_get_height(coords);
  1554. radius = lv_draw_cont_radius_corr(radius, width, height);
  1555. radius += LV_ANTIALIAS * SHADOW_BOTTOM_AA_EXTRA_RADIUS;
  1556. swidth += LV_ANTIALIAS;
  1557. #if LV_COMPILER_VLA_SUPPORTED
  1558. lv_coord_t curve_x[radius + 1]; /*Stores the 'x' coordinates of a quarter circle.*/
  1559. #else
  1560. # if LV_HOR_RES > LV_VER_RES
  1561. lv_coord_t curve_x[LV_HOR_RES];
  1562. # else
  1563. lv_coord_t curve_x[LV_VER_RES];
  1564. # endif
  1565. #endif
  1566. lv_point_t circ;
  1567. lv_coord_t circ_tmp;
  1568. lv_circ_init(&circ, &circ_tmp, radius);
  1569. while(lv_circ_cont(&circ)) {
  1570. curve_x[LV_CIRC_OCT1_Y(circ)] = LV_CIRC_OCT1_X(circ);
  1571. curve_x[LV_CIRC_OCT2_Y(circ)] = LV_CIRC_OCT2_X(circ);
  1572. lv_circ_next(&circ, &circ_tmp);
  1573. }
  1574. int16_t col;
  1575. #if LV_COMPILER_VLA_SUPPORTED
  1576. lv_opa_t line_1d_blur[swidth];
  1577. #else
  1578. # if LV_HOR_RES > LV_VER_RES
  1579. lv_opa_t line_1d_blur[LV_HOR_RES];
  1580. # else
  1581. lv_opa_t line_1d_blur[LV_VER_RES];
  1582. # endif
  1583. #endif
  1584. for(col = 0; col < swidth; col++) {
  1585. line_1d_blur[col] = (uint32_t)((uint32_t)(swidth - col) * style->body.opa / 2) / (swidth);
  1586. }
  1587. lv_point_t point_l;
  1588. lv_point_t point_r;
  1589. lv_area_t area_mid;
  1590. lv_point_t ofs_l;
  1591. lv_point_t ofs_r;
  1592. ofs_l.x = coords->x1 + radius;
  1593. ofs_l.y = coords->y2 - radius + 1 - LV_ANTIALIAS;
  1594. ofs_r.x = coords->x2 - radius;
  1595. ofs_r.y = coords->y2 - radius + 1 - LV_ANTIALIAS;
  1596. for(col = 0; col <= radius; col++) {
  1597. point_l.x = ofs_l.x - col ;
  1598. point_l.y = ofs_l.y + curve_x[col];
  1599. point_r.x = ofs_r.x + col;
  1600. point_r.y = ofs_r.y + curve_x[col];
  1601. lv_opa_t px_opa;
  1602. int16_t diff = col == 0 ? 0 : curve_x[col-1] - curve_x[col];
  1603. uint16_t d;
  1604. for(d = 0; d < swidth; d++) {
  1605. /*When stepping a pixel in y calculate the average with the pixel from the prev. column to make a blur */
  1606. if(diff == 0) {
  1607. px_opa = line_1d_blur[d];
  1608. } else {
  1609. px_opa = (uint16_t)((uint16_t)line_1d_blur[d] + line_1d_blur[d - diff]) >> 1;
  1610. }
  1611. px_fp(point_l.x, point_l.y, mask, style->body.shadow.color, px_opa);
  1612. point_l.y ++;
  1613. /*Don't overdraw the pixel on the middle*/
  1614. if(point_r.x > ofs_l.x) {
  1615. px_fp(point_r.x, point_r.y, mask, style->body.shadow.color, px_opa);
  1616. }
  1617. point_r.y ++;
  1618. }
  1619. }
  1620. area_mid.x1 = ofs_l.x + 1;
  1621. area_mid.y1 = ofs_l.y + radius;
  1622. area_mid.x2 = ofs_r.x - 1;
  1623. area_mid.y2 = area_mid.y1;
  1624. uint16_t d;
  1625. for(d = 0; d < swidth; d++) {
  1626. fill_fp(&area_mid, mask, style->body.shadow.color, line_1d_blur[d]);
  1627. area_mid.y1 ++;
  1628. area_mid.y2 ++;
  1629. }
  1630. }
  1631. static void lv_draw_shadow_full_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, const lv_opa_t * map)
  1632. {
  1633. lv_coord_t radius = style->body.radius;
  1634. lv_coord_t swidth = style->body.shadow.width + LV_ANTIALIAS;
  1635. lv_coord_t width = lv_area_get_width(coords);
  1636. lv_coord_t height = lv_area_get_height(coords);
  1637. radius = lv_draw_cont_radius_corr(radius, width, height);
  1638. if(radius == 0) radius += LV_ANTIALIAS;
  1639. lv_area_t right_area;
  1640. right_area.x1 = coords->x2 + 1 - LV_ANTIALIAS;
  1641. right_area.y1 = coords->y1 + radius;
  1642. right_area.x2 = right_area.x1;
  1643. right_area.y2 = coords->y2 - radius;
  1644. lv_area_t left_area;
  1645. left_area.x1 = coords->x1 - 1 + LV_ANTIALIAS;
  1646. left_area.y1 = coords->y1 + radius;
  1647. left_area.x2 = left_area.x1;
  1648. left_area.y2 = coords->y2 - radius;
  1649. lv_area_t top_area;
  1650. top_area.x1 = coords->x1 + radius;
  1651. top_area.y1 = coords->y1 - 1 + LV_ANTIALIAS;
  1652. top_area.x2 = coords->x2 - radius;
  1653. top_area.y2 = top_area.y1;
  1654. lv_area_t bottom_area;
  1655. bottom_area.x1 = coords->x1 + radius;
  1656. bottom_area.y1 = coords->y2 + 1 - LV_ANTIALIAS;
  1657. bottom_area.x2 = coords->x2 - radius;
  1658. bottom_area.y2 = bottom_area.y1;
  1659. lv_opa_t opa_act;
  1660. int16_t d;
  1661. for(d = 1; d <= swidth; d++) {
  1662. opa_act = map[d];
  1663. fill_fp(&right_area, mask, style->body.shadow.color, opa_act);
  1664. right_area.x1++;
  1665. right_area.x2++;
  1666. fill_fp(&left_area, mask, style->body.shadow.color, opa_act);
  1667. left_area.x1--;
  1668. left_area.x2--;
  1669. fill_fp(&top_area, mask, style->body.shadow.color, opa_act);
  1670. top_area.y1--;
  1671. top_area.y2--;
  1672. fill_fp(&bottom_area, mask, style->body.shadow.color, opa_act);
  1673. bottom_area.y1++;
  1674. bottom_area.y2++;
  1675. }
  1676. }
  1677. #endif
  1678. static uint16_t lv_draw_cont_radius_corr(uint16_t r, lv_coord_t w, lv_coord_t h)
  1679. {
  1680. if(r >= (w >> 1)){
  1681. r = (w >> 1);
  1682. if(r != 0) r--;
  1683. }
  1684. if(r >= (h >> 1)) {
  1685. r = (h >> 1);
  1686. if(r != 0) r--;
  1687. }
  1688. if(r > 0) r -= LV_ANTIALIAS;
  1689. return r;
  1690. }
  1691. #if LV_ANTIALIAS != 0
  1692. static lv_opa_t antialias_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t line_opa)
  1693. {
  1694. /* How to calculate the opacity of pixels on the edges which makes the anti-aliasing?
  1695. * For example we have a line like this (y = -0.5 * x):
  1696. *
  1697. * | _ _
  1698. * * * |
  1699. *
  1700. * Anti-aliased pixels come to the '*' characters
  1701. * Calculate what percentage of the pixels should be covered if real line (not rasterized) would be drawn:
  1702. * 1. A real line should start on (0;0) and end on (2;1)
  1703. * 2. So the line intersection coordinates on the first pixel: (0;0) (1;0.5) -> 25% covered pixel in average
  1704. * 3. For the second pixel: (1;0.5) (2;1) -> 75% covered pixel in average
  1705. * 4. The equation: (px_id * 2 + 1) / (segment_width * 2)
  1706. * segment_width: the line segment which is being anti-aliased (was 2 in the example)
  1707. * px_id: pixel ID from 0 to (segment_width - 1)
  1708. * result: [0..1] coverage of the pixel
  1709. */
  1710. /*Accelerate the common segment sizes to avoid division*/
  1711. static const lv_opa_t seg1[1] = {128};
  1712. static const lv_opa_t seg2[2] = {64, 192};
  1713. static const lv_opa_t seg3[3] = {42, 128, 212};
  1714. static const lv_opa_t seg4[4] = {32, 96, 159, 223};
  1715. static const lv_opa_t seg5[5] = {26, 76, 128, 178, 230};
  1716. static const lv_opa_t seg6[6] = {21, 64, 106, 148, 191, 234};
  1717. static const lv_opa_t seg7[7] = {18, 55, 91, 128, 164, 200, 237};
  1718. static const lv_opa_t seg8[8] = {16, 48, 80, 112, 143, 175, 207, 239};
  1719. static const lv_opa_t * seg_map[] = {seg1, seg2, seg3, seg4,
  1720. seg5, seg6, seg7, seg8};
  1721. if(seg == 0) return LV_OPA_TRANSP;
  1722. else if(seg < 8) return (uint32_t)((uint32_t)seg_map[seg - 1][px_id] * line_opa) >> 8;
  1723. else {
  1724. return ((px_id * 2 + 1) * line_opa) / (2 * seg);
  1725. }
  1726. }
  1727. /**
  1728. * Approximate the opacity for anti-aliasing.
  1729. * Used the first segment of a circle which is the longest and have the most non-linearity (cos)
  1730. * @param seg
  1731. * @param px_id
  1732. * @param line_opa
  1733. * @return
  1734. */
  1735. static lv_opa_t antialias_get_opa_circ(lv_coord_t seg, lv_coord_t px_id, lv_opa_t opa)
  1736. {
  1737. static const lv_opa_t opa_map[8] = {250, 242, 221, 196, 163, 122, 74, 18};
  1738. if(seg == 0) return LV_OPA_TRANSP;
  1739. else if(seg == 1) return LV_OPA_80;
  1740. else {
  1741. uint8_t id = (uint32_t) ((uint32_t)px_id * (sizeof(opa_map) - 1)) / (seg - 1);
  1742. return (uint32_t) ((uint32_t) opa_map[id] * opa) >> 8;
  1743. }
  1744. }
  1745. #endif
  1746. /**
  1747. * Convert a hexadecimal characters to a number (0..15)
  1748. * @param hex Pointer to a hexadecimal character (0..9, A..F)
  1749. * @return the numerical value of `hex` or 0 on error
  1750. */
  1751. static uint8_t hex_char_to_num(char hex)
  1752. {
  1753. if(hex >= '0' && hex <= '9') {
  1754. return hex - '0';
  1755. }
  1756. if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
  1757. switch(hex) {
  1758. case 'A': return 10;
  1759. case 'B': return 11;
  1760. case 'C': return 12;
  1761. case 'D': return 13;
  1762. case 'E': return 14;
  1763. case 'F': return 15;
  1764. default: return 0;
  1765. }
  1766. return 0;
  1767. }
  1768. #if USE_LV_TRIANGLE != 0
  1769. /**
  1770. * Swap two points
  1771. * p1 pointer to the first point
  1772. * p2 pointer to the second point
  1773. */
  1774. static void point_swap(lv_point_t * p1, lv_point_t * p2)
  1775. {
  1776. lv_point_t tmp;
  1777. tmp.x = p1->x;
  1778. tmp.y = p1->y;
  1779. p1->x = p2->x;
  1780. p1->y = p2->y;
  1781. p2->x = tmp.x;
  1782. p2->y = tmp.y;
  1783. }
  1784. #endif