From 4bce3b8c93c9abb692fe2255e00df787bc5ba2f6 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 15 Jul 2026 14:05:53 +0200 Subject: [PATCH] fix(core): fix scrollbar position overflows for huge content sizes (#9871) --- src/core/lv_obj_scroll.c | 14 +++-- tests/ref_imgs/scrollbar_horizontal.png | Bin 0 -> 2368 bytes tests/ref_imgs/scrollbar_vertical.png | Bin 0 -> 2257 bytes .../ref_imgs_vg_lite/scrollbar_horizontal.png | Bin 0 -> 3964 bytes tests/ref_imgs_vg_lite/scrollbar_vertical.png | Bin 0 -> 2359 bytes tests/src/test_cases/test_scrollbar.c | 52 ++++++++++++++++++ 6 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/ref_imgs/scrollbar_horizontal.png create mode 100644 tests/ref_imgs/scrollbar_vertical.png create mode 100644 tests/ref_imgs_vg_lite/scrollbar_horizontal.png create mode 100644 tests/ref_imgs_vg_lite/scrollbar_vertical.png create mode 100644 tests/src/test_cases/test_scrollbar.c diff --git a/src/core/lv_obj_scroll.c b/src/core/lv_obj_scroll.c index 322c81b516..d9bef351ac 100644 --- a/src/core/lv_obj_scroll.c +++ b/src/core/lv_obj_scroll.c @@ -35,6 +35,7 @@ /********************** * STATIC PROTOTYPES **********************/ +static inline int32_t mul_div(int32_t v, int32_t factor, int32_t divisor); static void scroll_x_anim(void * obj, int32_t v); static void scroll_y_anim(void * obj, int32_t v); static void scroll_end_cb(lv_anim_t * a); @@ -600,7 +601,7 @@ void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area->x1 = ver_area->x2 - thickness + 1; } - int32_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h; + int32_t sb_h = mul_div(obj_h - top_space - bottom_space - hor_req_space, obj_h, content_h); sb_h = LV_MAX(length > 0 ? length : sb_h, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/ sb_h = LV_MIN(sb_h, obj_h); /*Limit scrollbar length to parent height*/ rem = (obj_h - top_space - bottom_space - hor_req_space) - @@ -611,7 +612,7 @@ void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1; } else { - int32_t sb_y = (rem * sb) / scroll_h; + int32_t sb_y = mul_div(rem, sb, scroll_h); sb_y = rem - sb_y; ver_area->y1 = obj->coords.y1 + sb_y + top_space; @@ -639,7 +640,7 @@ void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * hor_area->x1 = obj->coords.x1; hor_area->x2 = obj->coords.x2; - int32_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w; + int32_t sb_w = mul_div(obj_w - left_space - right_space - ver_reg_space, obj_w, content_w); sb_w = LV_MAX(length > 0 ? length : sb_w, SCROLLBAR_MIN_SIZE); /*Style-defined size, calculated size, or minimum size*/ sb_w = LV_MIN(sb_w, obj_w); /*Limit scrollbar length to parent width*/ rem = (obj_w - left_space - right_space - ver_reg_space) - @@ -656,7 +657,7 @@ void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * } } else { - int32_t sb_x = (rem * sr) / scroll_w; + int32_t sb_x = mul_div(rem, sr, scroll_w); sb_x = rem - sb_x; if(rtl) { @@ -748,6 +749,11 @@ void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en) * STATIC FUNCTIONS **********************/ +static inline int32_t mul_div(int32_t v, int32_t factor, int32_t divisor) +{ + return ((int64_t)v * factor) / divisor; +} + static void scroll_x_anim(void * obj, int32_t v) { lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0); diff --git a/tests/ref_imgs/scrollbar_horizontal.png b/tests/ref_imgs/scrollbar_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..b024a8da608af155d4c2a4ba4999af1c8700445d GIT binary patch literal 2368 zcmaJ@dpwkB8y-xIQ(nI^4#Suk%I=2ER!TLPjKQ~kvcAZvRN}Ka%sQksiw{K-KnN%tj2m~)*zO4JjoeqN`Iz5Plqu;B%e(^ACI&$k{sTyk& zT^E5A;*d;-7@Lw8wLyLt`%+J8_UDkQRlhF5aeO%ubI=L4k^0CViE{!a`OAZf$?=BF zQzEE$}@( zch-P3EjUO~`^kZD3nJ4MXR#msQDz=Rkt}>{s zzvaA-4B9{jEnohFOy^cKryi|=tGY7qp;}=G0J}In)CZ(<(dY*%-B&OHpWHkGcXiA7 zI6`h(m5ZiDLrS`%Up$Ri_wcf|ek+P4a%lgYqH~V`F+R?l^=nf!mgbesZC}2f7Vb{l zqtz-=;iyqSWyv^3wZ30)VJ5Udy$@7fS@pNcBX>XHW z|0L?fpLH$6lTO{6e>C7S#C6L~OY-aZCj#{Y)aAA~n(21_pybR-OJPOrN?*Y9>Q#+p zcawm@Zr%>B151L)M_KLeyLQbe?$OcIf8AZ2*h9B5#gTmU*wIKl{Bjd{9Z;Bpv^NgX z{=N~NBgS6Kt3jIu8@eGrYveSk`~SN#lUWoN7UcxgG&>#_0o2sCnZ3oCn6R`5$DYsJicC^^9|<%z6ws2^ zLc&tITzo_kD5xJ*NXs!5yb4M6|L*E0z9{GD zDR{SFa%N8+ni^16+OT4ZS8FKj{_Rm3!oy`Iisi<1TtuRtR)3r*u<=)hCp`Ys<2!Gs~(V!0T5 zC#Exdj61PljE4uWGeNxXaBbsAN6$_3zz7r2)$UujD=LpM6Bh3FXK%T5DJ+aHJqM(5 zId-Nq0t(zm%&kF(y|M&r-b1e>Q_ z11b4Gr44jU@<*L7R{gwa&$D>e$B4R-gHC&V>a@Q;DEif2#!%`Ho{-H)`%f%W4*W{n zGBJYD8T-fdu}G#dUi&Ic4~ezM_#K*K0U4ai>Dyd1+mb}m^zhUk4jpQJW3j18vQxP{ zypGIs&DeyC5gHvVE)&((%+f=`^}+5lD(;meKy4PEHsF%;1@raGS!k+Wqh<3-(Znta zVko1 z~uLdFtGAtSgNPg!1MywkiM-AWL2`TX`U|R$oa7W_RUtv}al5dFBvrf=Z zhPaFi^s>O0^?J`JDnv zC+O+Se`elWP&S=CA6!1WPv5xzNtZgO8`yxiY4Urm)2K|k|BKeh5-{vGGt}SgS>1Wq zRJBK1&D~BxoMqQ=TjWc+s_TFybONM8cd$85tp73-h?Q3>w@=>;k%JwFJ7EQW%VxkG@1rX|#T2w+4cTt0(dILnox3Daa?;F#g*;uuoF_~z={tVa$K zM?WraRxFH96TEis|JSXl+?iKd6!WsgOTwS8IZ`<1#sBvB8pY?9@_cqWZ=5f$GZ#DV z92j&gcIdCs${2N?_F!iAkGI9e21F{;Vd{tt#*KmAStA z|Nj2S;k;r}vd1H>zYf3De@Cu)y*=)AGm|p!La#nW&DAzKw{FQTFMPlK>g1pMx7pkR zo3f#6v-#(X@8|#exAC`|w9`}>XQ8l}Ceq?R|Gz){W^eYYySHyBe%*Zb?^5SWO9E7n zPl@fwKI^{sKY#w)d%JHao`3!I^HI1=bhPO91ItfctA3xZ0XN1|@AGbo|_sy$!crqWtzV}tJ>z_hMSA8u0HtuxrmeKDj8v^6Wu>wYO4Fx{=d9% z6W2nn&^`{Hl#4rs|KCVk7tLd1zZq!H(|`m8@6XHQ9{!f+pA3w&6-7Xw9a|MFAN$o_ zPHj6#1<>(xr!KeK^`_p&ClBb}Q;g;Y(mS{@{l>)1$ijxr(_o3B)T7^8V+w5fL0IQrovm=P4!c7qfeJc*nZC1+_A5 zbE7z(l%B~pKhuABLoj!v%<|uGw0av zuKE*CYv;sm+sABkcKcHH=Z5F^`?tS0tloX@d!bc+Eo;TRqYlS@e)#_0pNIGP*;Sek&oZUlzDqXGw$%{(GX!!t3_%Bz;Ue6bHn{AOnPoGOmj>n$UzxT(Rv29f~ zW8JO0lChhJU!EU+Bnr-c}6DEc7@VI?t=}pU2bRFAefb-hbV6=hVla>;HeV z0BhNE>civnFK@438&_|>r{>c+z4nNuM-@%Ocd`eaditw)_3iddyPALA`Oa%DJ(_T8 c^&ht9k5)c9tfe^{m;f0(UHx3vIVCg!0LBjc^8f$< literal 0 HcmV?d00001 diff --git a/tests/ref_imgs_vg_lite/scrollbar_horizontal.png b/tests/ref_imgs_vg_lite/scrollbar_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..7896ca213fefdbffa07318ee36aca89bc78efc16 GIT binary patch literal 3964 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