From 2631bbbaba2af68c42b82984eeb9195ecf677e1d Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Mon, 11 Nov 2024 21:43:56 -0600 Subject: [PATCH] Implement volk meta loader and ImGui --- .../index/buffers.cpp.FC2CC275D910BCB7.idx | Bin 0 -> 9970 bytes .../index/buffers.h.CC15E40B6084C10D.idx | Bin 0 -> 1476 bytes .../devicelibrary.cpp.A6A50BF3BD186A09.idx | Bin 0 -> 8994 bytes .../devicelibrary.h.D7B097F0839961AC.idx | Bin 0 -> 1602 bytes .../index/entrypoint.cpp.9286A9B0BD8A276E.idx | Bin 0 -> 4378 bytes .../index/entrypoint.h.1BBE0276DA7F0CE2.idx | Bin 0 -> 1024 bytes .../index/global.cpp.DB8A6A1A4BC0BF3A.idx | Bin 0 -> 2986 bytes .../index/global.h.67055BFC9A2F7BD6.idx | Bin 0 -> 5460 bytes .../graphicspipeline.cpp.F94E3ACB17FA6762.idx | Bin 0 -> 10476 bytes .../graphicspipeline.h.093A3C67F46BDBAE.idx | Bin 0 -> 908 bytes .../index/imconfig.h.3ADA6E76D666621B.idx | Bin 0 -> 146 bytes .../index/imgui.cpp.2302CEB72C8D9CA1.idx | Bin 0 -> 406068 bytes .../clangd/index/imgui.h.ACC246D76711413E.idx | Bin 0 -> 208996 bytes .../index/imgui_demo.cpp.F91A753A09CDE374.idx | Bin 0 -> 179258 bytes .../index/imgui_draw.cpp.5B7D6E505867D9EB.idx | Bin 0 -> 110134 bytes .../imgui_impl_glfw.cpp.99B2D46074E7E29B.idx | Bin 0 -> 20496 bytes .../imgui_impl_glfw.h.45E4F0EDCF23819B.idx | Bin 0 -> 2434 bytes ...imgui_impl_vulkan.cpp.258735B3A169763B.idx | Bin 0 -> 38136 bytes .../imgui_impl_vulkan.h.E293D4932B27A48C.idx | Bin 0 -> 7970 bytes .../imgui_internal.h.B25AF3943BB1AD2B.idx | Bin 0 -> 234328 bytes .../imgui_tables.cpp.A43F5F055F44B37C.idx | Bin 0 -> 105700 bytes .../imgui_widgets.cpp.2C6A8C974E4B792B.idx | Bin 0 -> 240552 bytes .../imstb_rectpack.h.426331EB29A2B17C.idx | Bin 0 -> 8202 bytes .../imstb_textedit.h.421029FE98AFFA96.idx | Bin 0 -> 18352 bytes .../imstb_truetype.h.0A3563D846CA9ABF.idx | Bin 0 -> 67800 bytes .../index/main.cpp.6FA2B2FABDE63972.idx | Bin 0 -> 630 bytes .../index/model.cpp.56D1ED026EF1D5F1.idx | Bin 0 -> 2122 bytes .../clangd/index/model.h.C3ECCCBE7C04E4C8.idx | Bin 0 -> 436 bytes .../index/render.cpp.657B8B8CBD5B3B6B.idx | Bin 0 -> 7154 bytes .../index/render.h.A0C955D8D0DA424C.idx | Bin 0 -> 698 bytes .../index/texture.cpp.14763AFB742F8112.idx | Bin 0 -> 10590 bytes .../index/texture.h.712506A996DB5236.idx | Bin 0 -> 858 bytes .../clangd/index/volk.c.7B12D4D8A514BDD2.idx | Bin 0 -> 130984 bytes .../clangd/index/volk.h.58430F1E9A139B4F.idx | Bin 0 -> 124198 bytes Makefile | 19 +- escher.blend | Bin 1027232 -> 0 bytes lib/imgui/imconfig.h | 138 + lib/imgui/imgui.cpp | 16789 ++++++++++++++++ lib/imgui/imgui.h | 3714 ++++ lib/imgui/imgui_demo.cpp | 10384 ++++++++++ lib/imgui/imgui_draw.cpp | 4674 +++++ lib/imgui/imgui_impl_glfw.cpp | 925 + lib/imgui/imgui_impl_glfw.h | 63 + lib/imgui/imgui_impl_vulkan.cpp | 1593 ++ lib/imgui/imgui_impl_vulkan.h | 203 + lib/imgui/imgui_internal.h | 3578 ++++ lib/imgui/imgui_tables.cpp | 4467 ++++ lib/imgui/imgui_widgets.cpp | 10310 ++++++++++ lib/imgui/imstb_rectpack.h | 627 + lib/imgui/imstb_textedit.h | 1469 ++ lib/imgui/imstb_truetype.h | 5085 +++++ lib/tiny_obj_loader.h | 2029 -- lib/volk.c | 3221 +++ lib/volk.h | 2089 ++ src/devicelibrary.cpp | 5 +- src/entrypoint.cpp | 7 + src/entrypoint.h | 34 +- src/global.h | 4 +- src/graphics/buffers.cpp | 1 + src/graphics/buffers.h | 42 +- src/graphics/graphicspipeline.cpp | 5 +- src/graphics/model.h | 11 +- src/graphics/render.cpp | 106 +- src/graphics/render.h | 17 +- src/graphics/texture.cpp | 700 +- src/main.cpp | 1 + 66 files changed, 69886 insertions(+), 2424 deletions(-) create mode 100644 .cache/clangd/index/buffers.cpp.FC2CC275D910BCB7.idx create mode 100644 .cache/clangd/index/buffers.h.CC15E40B6084C10D.idx create mode 100644 .cache/clangd/index/devicelibrary.cpp.A6A50BF3BD186A09.idx create mode 100644 .cache/clangd/index/devicelibrary.h.D7B097F0839961AC.idx create mode 100644 .cache/clangd/index/entrypoint.cpp.9286A9B0BD8A276E.idx create mode 100644 .cache/clangd/index/entrypoint.h.1BBE0276DA7F0CE2.idx create mode 100644 .cache/clangd/index/global.cpp.DB8A6A1A4BC0BF3A.idx create mode 100644 .cache/clangd/index/global.h.67055BFC9A2F7BD6.idx create mode 100644 .cache/clangd/index/graphicspipeline.cpp.F94E3ACB17FA6762.idx create mode 100644 .cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx create mode 100644 .cache/clangd/index/imconfig.h.3ADA6E76D666621B.idx create mode 100644 .cache/clangd/index/imgui.cpp.2302CEB72C8D9CA1.idx create mode 100644 .cache/clangd/index/imgui.h.ACC246D76711413E.idx create mode 100644 .cache/clangd/index/imgui_demo.cpp.F91A753A09CDE374.idx create mode 100644 .cache/clangd/index/imgui_draw.cpp.5B7D6E505867D9EB.idx create mode 100644 .cache/clangd/index/imgui_impl_glfw.cpp.99B2D46074E7E29B.idx create mode 100644 .cache/clangd/index/imgui_impl_glfw.h.45E4F0EDCF23819B.idx create mode 100644 .cache/clangd/index/imgui_impl_vulkan.cpp.258735B3A169763B.idx create mode 100644 .cache/clangd/index/imgui_impl_vulkan.h.E293D4932B27A48C.idx create mode 100644 .cache/clangd/index/imgui_internal.h.B25AF3943BB1AD2B.idx create mode 100644 .cache/clangd/index/imgui_tables.cpp.A43F5F055F44B37C.idx create mode 100644 .cache/clangd/index/imgui_widgets.cpp.2C6A8C974E4B792B.idx create mode 100644 .cache/clangd/index/imstb_rectpack.h.426331EB29A2B17C.idx create mode 100644 .cache/clangd/index/imstb_textedit.h.421029FE98AFFA96.idx create mode 100644 .cache/clangd/index/imstb_truetype.h.0A3563D846CA9ABF.idx create mode 100644 .cache/clangd/index/main.cpp.6FA2B2FABDE63972.idx create mode 100644 .cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx create mode 100644 .cache/clangd/index/model.h.C3ECCCBE7C04E4C8.idx create mode 100644 .cache/clangd/index/render.cpp.657B8B8CBD5B3B6B.idx create mode 100644 .cache/clangd/index/render.h.A0C955D8D0DA424C.idx create mode 100644 .cache/clangd/index/texture.cpp.14763AFB742F8112.idx create mode 100644 .cache/clangd/index/texture.h.712506A996DB5236.idx create mode 100644 .cache/clangd/index/volk.c.7B12D4D8A514BDD2.idx create mode 100644 .cache/clangd/index/volk.h.58430F1E9A139B4F.idx delete mode 100644 escher.blend create mode 100644 lib/imgui/imconfig.h create mode 100644 lib/imgui/imgui.cpp create mode 100644 lib/imgui/imgui.h create mode 100644 lib/imgui/imgui_demo.cpp create mode 100644 lib/imgui/imgui_draw.cpp create mode 100644 lib/imgui/imgui_impl_glfw.cpp create mode 100644 lib/imgui/imgui_impl_glfw.h create mode 100644 lib/imgui/imgui_impl_vulkan.cpp create mode 100644 lib/imgui/imgui_impl_vulkan.h create mode 100644 lib/imgui/imgui_internal.h create mode 100644 lib/imgui/imgui_tables.cpp create mode 100644 lib/imgui/imgui_widgets.cpp create mode 100644 lib/imgui/imstb_rectpack.h create mode 100644 lib/imgui/imstb_textedit.h create mode 100644 lib/imgui/imstb_truetype.h delete mode 100644 lib/tiny_obj_loader.h create mode 100644 lib/volk.c create mode 100644 lib/volk.h diff --git a/.cache/clangd/index/buffers.cpp.FC2CC275D910BCB7.idx b/.cache/clangd/index/buffers.cpp.FC2CC275D910BCB7.idx new file mode 100644 index 0000000000000000000000000000000000000000..c6d053187c83d139fb4eb401cdb5d78171896309 GIT binary patch literal 9970 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 Jjcq_7`9Hhl$)*4R literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx b/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx new file mode 100644 index 0000000000000000000000000000000000000000..98a1a8c80e9b4c6e5396c37044db2c50642999ed GIT binary patch literal 1476 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 Hc+0gO1~0Xt+RCMgIOqN&YX%l;2iq zTVt!K@6##M=Qt&rrkv@NZ@+paoD5wLI#_Hle5k+vNxyDoM^|^>#kzHAMZH;_kI$#9 zIk5c;@0VT<37Nll=>E3CLly58U4A8O)j_e-mi-H|)ro@(OfCo5Rid(Whg?R3ZViUd zFy_YPjJoL#?KydTcyoWlXkhb~o5EJ!x>w;h(0ru9%jf3qH4}!J?`JLkBeiGYmfpyU z>SBGH+d%u$l}YyZ_FUeR^-_8Izt04ZI(;$!%neg*Y3^ajL*X>h=!3&SA{DK*qqr8L zX(2|HOf54|;~-n``~LTI$&IK{DN8v}6gN*iPcim*BBZFd?e=$F4EW83r5r)55~~zr z4;26NPGze0qx;iX$_d29;$r#O1I2FFCQP0p`zcsS+r#cMh0IN5C$-b#-Xo7sT#%&< zWGd@w)+t}LhV-NIy zQmQ^|UUOszODRBX5}Vku2a2!ki#xG9E^iA7JI15AISl%efof{ZY(v zJ~|(7L<9f*^qgmW79lK`!{_*6StX}FlSl$OX`s{jbb-dU0F#bEj*(bp=C2Ofd&E1x z&$ari-Bkf=>q}3FVD)pN=Tunjqy;L^+Z#f~KnF{MjuKJ{%tUWjss{pz{SKx!Tj%Nk@?CyA$ z1A4h``D8S54`#ZYE*JVIBj5lv^|c*SfnG>2RG|@c z3=(IaBxr=?ItkrL7;F?ADufxEDA=oP(TG>)@be*HG~N(D5sij=X7-#iG=u`pcrcSO zj)Bs&W|`Nz2pg7@(ozjhB9@Z{$!6mYER2kaZ6plGNXL=T=^ewfBf(aLJYqZ+VZUeR zhpMlWqJeJU8$2&gC055ka1fxFbC*7 zK2JEP{ok103T#fB3$zd8EvB7y>75?M^@a)<8NDal!gv58U!@jK}L`N24-9 zJD1NDY-8r2XyySfRD&~5a?EzX>!Q0yU)L^ywW|renkXx(q= zzI()i?%aH!n~0%_2!ek6EZqO5ZsQQz4i$Fh29;U)r z@ZtddC$|RbAM$D6{_(W4WuTvENDRQd%5!?}&4?;}0q6un!epeeFK7|hN>r^xhhvrb z>#jkwXQOj7-_#ymDuKD8DsaU0ELW&1v_hqtQXTdKbcd$Q3RS8q9WgyHhE&T1q%>`- zUw(XF1vr;#OnnNCukLO3K2`UM3Fu|UWkI+WZ@h8L-)=gXgZ?q0m>)Qo>V+<72@aHR z{B#RS2uKLGphEY;KnrRjeoZ6Ps$KYt^uE1C9Yn$-TD5V9$jsZ z%yhmne+pXr)#GqVy`MuZoY6Mo&_-M_-z-$wu%38h*;%Nfe>(}rvR=|2`?D_Gxen~j zoH8>Aw_zNUay?T<%=FXhr>7!q+I9Z$#+K~mKu5ejO8+v8CED$ znQ4UMD{ zvk7YEHy4W?f zqisODGwyoSK6tY2oy$J<+ks9tBny*cVdm_8Z|3dk1-ejI=!Qlr^@BNUK9%hRx|*-{ z$7oze&PDbOdzk5&@|kXE`)2czd1?L5Z^>9LMVVriwk66XRw!GUZH01_IaVlFnQMje zlzCRDN?9eO8~jmLTcHMJgMb#a@Et+RU+e?#GWZN(F2!_SE0f=J*bVebLa!uhj3%7R zZ^*At+yjCXK1JyGGj@+xC!E=8rnC8M;r#a&nlWAxug8SLOj9ISp+rTZ6-rhlTcJgYMOLVZ zD4K{KW7@T)p*^gy{}}A*C-H;Ng(triZ%-NCe{ ziuF9PaH6^@|Mzo1m+DJ}+Ytk1SxzFB$nnyF>sl<6Tdr%?zY+9zo)EBF7QLLZ!qTh#X7T zpB+1VeSX~qh^vGtG2)cJ;9CC7)@=!wfVQD*7#z+I(t4WLdO2Sa!$`MJ9gjII?jKPX`4ONi zb(KCCe|X#GAAW5=Zl<+r?F8(Ct4-;b7ad#wGthO@>gJ;MgZ}TXPRjh{F3?qcmGDaZ z$F=WtDYa3fKsOS)k#MLl*1cNriSdJbX6wG%0JM%i!9+EG%i#fFxi~sb2+!AP)|)%(%z^ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/entrypoint.h.1BBE0276DA7F0CE2.idx b/.cache/clangd/index/entrypoint.h.1BBE0276DA7F0CE2.idx new file mode 100644 index 0000000000000000000000000000000000000000..d43304632b59a1182e09aa094ea61cb99e62bff5 GIT binary patch literal 1024 zcmWIYbaVT`%)sEB;#rZKT9U}Zz`!63#Kk2=nJa+wUq%LoiaB%p9J!hucwCDYor`;M z$dx;UQLQ5iI1(#w^<{|~1Ln{_W zh`(B~(%_!Src1LfTtC%$RsXS|zE@TtpH*AslXZU-i`63csb?>{a``5w+pWoSr_7ya zciZZA!J;xbwoUA!b;0}-|LmJ>7A3P)IUzNDit7CY{+L;3ry5CCZPLE6CZNpQewyX3 z{^H8qq%xpe)67{u{@7#2BE-ba1av+SFmnoX$}w<2I4Td64U9NnTM967!KC@v`D9?y zyK*C2IW>A6_?S4E7?{P_#U#N5O#03LLzg+jcf|-W3Bq*qvh%`qui>v-wIlsOh5!=} zOqz?GOAw}8SXx~1N9I5t#J%PjWm<4W<=}F)_lV1(^i7VA9{z_JmjK zidXV6@dBMD&MqzlCScO0(|=D|>F3-oz{C%f7GW3R2NN*qnE%!OL2vH$f(>UDW)kKC z6ENuoF<3za1hB&x j3=E*`3k7U21_LaXK?2NIp5DB>&+E_{Ru)z^b`Ayr4nYqm literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/global.cpp.DB8A6A1A4BC0BF3A.idx b/.cache/clangd/index/global.cpp.DB8A6A1A4BC0BF3A.idx new file mode 100644 index 0000000000000000000000000000000000000000..f4b33b7f6dcf542f4818e41f347fe32bafb654a7 GIT binary patch literal 2986 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 aqhn&@5<mvpL literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/graphicspipeline.cpp.F94E3ACB17FA6762.idx b/.cache/clangd/index/graphicspipeline.cpp.F94E3ACB17FA6762.idx new file mode 100644 index 0000000000000000000000000000000000000000..db4a4543d1e0e8a4e3e0d56c8a619e77a3f2fbc2 GIT binary patch literal 10476 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx b/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx new file mode 100644 index 0000000000000000000000000000000000000000..f0baa8055afb1a4652597b3b8aeb62d13e9a19b9 GIT binary patch literal 908 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 M^91uD=F`j!0M*POy#N3J literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui.h.ACC246D76711413E.idx b/.cache/clangd/index/imgui.h.ACC246D76711413E.idx new file mode 100644 index 0000000000000000000000000000000000000000..829436b0dd422e214dae1a8b769ff7595b391cee GIT binary patch literal 208996 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

SzmfV6UbsAlrMqh>>-+PQG zN7dv}wIx1JwE6t<{i%S(49`W4?xLm*-dg(SDc1g+wRsDAS8j>GEs+&oMd2g8EgE)P zv=CQ1_3SaNoggx33mWqvKnJmwEmp?wr|OwlToM&K4DG>@}Li7E5fgccWMmO-$@DmKcp9MLLM|Dj2^Z4Q)-)HZ+W!f#L^g;F)jQT>e{e@=shAEgX znT@?Qa^6b6ea;zliUqm~%}4{DkRH4{z4nO}HNn?0zl7}()K z?KS|%sm;f!t=KGltta&h`VcTW{Haa;)E}4$_ND;p4+9hIAZkVsHD|N(RiJaJ>8hKAOO z)aFELD`rN$Ac4eI&Qa?zV9(fR zI~m$%pQF4PgP)CvQ8@eF3LGMEqv1}KwSmr4DKnUJr`qJG6p$QPVv zZcg5W{5-YhJacmz0z1KZ_U7b~wQ7pB>PLfxl(lNLwQ8LKII5a-R5cY_NKLX)O*L3I zIjAN%sHUPXEb}BesAfB;<{5xgdVeZ?(7+Hqjoy|<@4`&Xx2My8p|3TgBZJ1A6Xq3C$xE*EPKyM;v^6!(V;z# z_~XAiw~zn8Nh)MeA?tzmf0Whtf;`R4kFfQ80R0DkZyZ^-vi0C53klanKYZmnoGWvZ z(4XWD98s_WcDj?Hw#-oXH`q0=peI()Q(f(|o_rr=TEE1~+;dORo+IpM`<@i;Nr$72 zd}b^jNH;u?Itzk#_M6=^FWVMvcCbyUQbLtdhxS8u>XVhR&4ZWj=K7=BZ+_+!)=blgvbNb2bz!*y& z8+SYDBZ~@lza*;pMNtLueM3YfC#x+&?pS;+j2 z1MAauzawz7&)uNoS;LRAZ4DLcL-~DR@9E+~jN~S)0;)p%rA&7i6Eat2=9$@f1 zrK!J5Q%^?0L<80Lm&ZD}b3c8dwS2*!?lUdOl;W> z2Ff6i-#m1>dtk)orF}U`w$?J6kC=zTjId>FG+G-?ALM~vYWaKXw5-`&nNudzDc&jJ z*^Y0Q(fiigvUhbop|9s(KlIZ_SxA)tmbKZXS*xxSV^o#6|2#q<=wak zGr#VA>HsHcQ0p7`=pX2d#g<8NQJ|0FKOFL;1H~H>H`}uI~g&I|%MuTFDkDEC%?*8q~4s6A)pm*gr>@1G1 z+xO}8VNT*E(!23aPsn$ZtvG?A6ZqgsD9RD5&4bW;@LQm9qK}R1JRfk4{a^$ij5NqE z<>XP{@Wh*~Ts@AY-jNTK`gBms*)K_!j%+;_WpI%{&fN|mTm#OSP-jfKH>cJYYHMGgoyBRrAb|^5oZ1}8{2a+2SX!AKxe_M<+J^gW zV=BmxZg6BrslY;6U@;I)=upx&w#R8wNRtji{jypy(Cfcxxm<;LW~#jZ@e%iB@>z!( zZl)BOjV&^pg#26%^a!umo53`JsnJ%fx8=9ZefIwG$K~5{PI6hOzs!e%9bD0F-{Plm zV%u_G2KRY$MC9bbY6}K^FmJnEQ8nSgz=el6Z4X8IhkS0P?7ubi4If)hTeb|c`HjX4 zb2nzxybtEebb`Kq&`IY9~u($Q!%xU2kYs`R)3UFK`YgpHHJnTaCv z+pBW8Dp#R*H{)>|;jqxsnbmSu0%x&Hbx|_>qGS$=s;W*MKl{{>4V;o_6Dpej(4fSK zta?0Y8p;C4N=i(bKTEWMIsM|+xkqylSJ^`Lf zFM+mwTq7s>Y@zzh=g=9q)8)gv7kymVdamfGE4snR-}K$5xkj#@gE+}aC7e`RqV#tC z!c%L%as8H)B$-l4roB+x3Lcne+)13uNlqx>ghGvW*LIj4p47Hxkqg`E9MI?RmqV;o zd^q5~i!0+|3@*l&Xx4&^YuUD)H&>qL&2|D3&m?Rwf z+i4k`=7Y~|@!j1rEHRCf+>*jAX8I$ zDIrWrqr2*^b9C_IlGmK1SOLX~-st;=Ok>%8T@&iB@s{&Sf(x%q8sfVFY zJF*jI2j!_y|CB$IMmGv;0+cHd=kg)=fqzNxiUE>RE|lvuOV7F zu-*Bjwfw}NK>XdiZ_SUMAGk6$#$aRI6TKfspBwvQfzP)dY&`*{ssP?aDW&d3XiMvK zPExN@)vGipN$AAqwktc^W^j^BT9wI%MZ0`2q<7@bU=Owh`6g68?~Zn-$Cd$y_oZ`^ z1anD(xgKrz``qk1LOdvglYG$8ANW&Fbfm*-3pXOejk?cSuk;^Km%X!@D|1=_rxiWW zxGfM@ZCiiK>^NJdK}I+5(^_OPW6Jb2S|E zRB!(s`CGPUN)Q7&G|CQ*3Z*7wK&M98sZn{qZ=OcY*>aNsT^ePVM%5}EkQrO$!#XB> z)F?k{RDr^)1s(oB-(tWgjq;O5W!Hb=<{FPw)}|p&`)!UmQ6wCl#WzmBu#nFVSnQ1?Y< z*vyZM)x(R`^XCzC%KKlff*DY%URtW&jlBCAP^KPUrk?*M&FAH$flBqWCSUQ9nL%8e zk|dDC*gD2a9#wje_nddcW&QDI=QznF30#tBkz_{x6z4qa$H&=+z)hoaN-F@E>Z`oBC@9oK!sU!4t{7Zkq?M<`V-UoQHWvn%{H6ILX=%?6&legdDB*{jQ z%%57Wd^9D-`AeNAJ6^R)sQsTc(Vi})Pxq4O>iYZv+f9a_VD-1lp2Y>~8692)wDJ!|O;GcX>a$RS7T{jR-UW#YiPTexL zoRfGMfrrsRWK%t-^YZ+npxd0J#niNgw{F`M@W=Kiqr_fp+q{+F&2N&RuNf<`rB$~4 zig$mW{oi3TMtHHG!T^CjfVWFzE^ehMRGJzgM@k0VGlhGmMuM2Xo2HujpBk~%dYi>> zE4} z|M4~tpP$a7@lhuRJnpQd~UwZWFQcm(&Yx!9_6g`3$H=TVM zsCD2Zw-j)TKUqa5e!v{8`%|<(zvN@Ws9x4=zpUATVlXoxUb`(`yB%$UGT@{3-;dgZ z$m=9IWA-wc@;X<|bz{rx#)D9_*1j3JI~(?V;v|=)a9P?L&G?s${o#k}Tckd0cT(k$ z%G->iyqBz`zzhn^D0Iqvy>WKSk&Gc+nGyw*C>5Nj~g-a!&#G6g^NG=2}-P23ISl8!Q)}D+WJTOh-uzJ|4&%v0~T{oYp!y)bV>K z5sn+z4*P4q5BnT@lX`F7VxgkI|3>QlNUqE&1)SnPG7%{mAIe4XoTNo-*`ghTE)xpE z&T8|ZRUW)K5!xwc@6<<1c=SKkf}^UG^U((S-$oVVd?u@W+59`N1@hOJmGM#0_@kmZ zC@(3~a~siU8_`r`FU0^`(L7tx8nk!J02k4G7m*c;l)?a4(Lz_zIus9$0dAuCZXzqR zt-t^;(L^uNT(KtGdO}?B4HS2n?bTIUdzJ2w=1Hr^^!>}X*WbRZC zg=1|G`LgyNpVYuaG)85RjSpKUfY1l zt>ty?Ftls0+aM($OfBMC&`Ib!iLs~!=vyjAOB$>0Hzsb4s4z7*}tKQhVz zKg<`_i2c~Mxl8o!lJ12eiiN{!duK+!+QwwSJ2kvhD^~VB|F7dG`Y8jd>E6}!-~;yGS?@fx zz=Q!c^rjkmJ3663@gdkI`jL7+-d_uac4m!`Pg5&TQ|pl5J_DkumC@8X6ziR7Y79lk zP`wPSP2;I$@zffF_+}|g2013QyW!F7G1a(Zs!7O((qxR^-5@iaKYN*qjiA`5FWMu& zS)6<2I!5wVAb*P;rd)?m!F5Owpum|->ux}};07cMe%;nX(Ac&zmO(cebdy0S zj3ym85M1pWGTiv(#r_O=21lMT z1f?itNG%+xWk}b5x=9PWs04;QhcklbkSRccjTux2yX(M4pjH0Y{I-`^!;pH|TMzaE z6lH)R4Y0R?A)On$5<*7}X9wy9?0vxy?U?>PVN-YXX2?q>RrX6r75tF@Vuyc-oNdu7 zNE5t*%L24@%b?fHpuA=VrPs*imdWOSA;U?4P*)4K&fE& z$dhG(!Cl5E1t@!yc4{p92p3(yS0hbhD7Rjw9I5~l@y9j;X_F%njhbc9^}B? zUPq1KC?83`-8<9l*Z7P6>=RujkXK>1SB4fcv?}3GGn;g2>%Lf}=TB&Nzwe*JPK6gs z78^RBUKYY-%tO6M2t^nu7D6!wN`z2?fifYKVc@C|u43St5Uye1x)82o;D!)xVBn?@ zZepNZ2;~^45JCk8?g-%y2JQ;sE(R)vP>F$iLb!*4`$D*nfd@i(fPsfXc!+^VLU@FM z$3l3Ffu}-vih*h&RAZn<2sIdZCxmwxXc9sb2J8v2Ck&Tc#|Suv0S5vcFyKOf3kF;X zaK(Td0d5#@C%_#89t3z`;5Y%tG2lsnCk9RsZ~_Bf1bAV*AQ}U41jJz=o`84^BoL5*fkXllF_1(+5(bh9NXEc90?uI|g@6tUTp{2J2FeI1!@vy!ZeZXh0XH#FPCz*Z zZV_+`1Gfpdje!aRDll+|fIAqdCZHMvH3ZaP;28nWFi=ZCEe4(w@Eikm1k_=mo`8A` zG!W2$ffoe4z`#oaUSgn;fJO|wA>a)L-V*Q@1MdiUhk+&nnlR8zKr;s36Yw4bEd;b+ zpp}4D473r@hJkhh+A;8ffDahxAfN*Sodk4ZzyZJk4BzXH0FD@N0&v2BGk`M&TmW1! z;0oZ10XG0Q47dZhW55Hz0|O@jPGGnUp3z9HBDjTt z+akD)feH~+VBn4j?qJ}q2<~E_QUsM4cqoF07X@*B6yC0IuX=ipk4&^7-$ed0|s7*-~|R=ir^&%nnciqfo2gjW8l3A-eaIe z1T7e76+tTo+C0T(g2 zV8C4r?ilbAgBJ$8#o&zrA2Ik~z*h{u81NH=9|ruz;E#a-F$7>BPz-?>2oggO27<*9 zjDZj_gkT_4451h}DTb362opmX2ExSv28zW{jDZp{lwhD#45b*jB8Dp%C=){&2CjN;80Zp17Y4qF;R^<=C15Qve8U`*z%dLsNWcLD zjuLRhfRhBAFyJNuHw?H-z#Rh~67aylaS0s9zzGSQz<`$oyf6?bfj|rdNgxOV!4e3@ zK!^lFFmTiaj+z*D-^K)NFyLhhUZ#e>@HPc+4EUIW4+eZq!50I5rr?JGe^cB^W5xKq&^=G|+~Db`7*+;DZJ}V4y<-9T@1;Kqm&eG|+{Cj~e)hflnIvgn`c* z_>6%s8u)?%Yb{t~hVVzVa1;ZMT5!aGlNOvX;H(8_4ESon7XyA;@WX(=7W^>~poIVo z1Zp7=13_8{!a%SVf-w-Hg%AvcY9SN@v08}5K%5rhFp#8$Bn%{LAsGW7wD17~9a`wX zK&KWuG0>%jE)3Y{z(!|ypW5oc76W!Vu)~184(u`Dr~^j~IO)I%1I{{d#(;|sTrl9O z16K^V>A(#G?mBSCK%NfrFwmugE)0Cs!AA^y(!nPT*y+JeZ`gf%J=kNwK@Sc*_}O#* z(LM0OpEXa*rd8SWaAZl_=bcI5*L&pwtVv^)1giL8f4w`t`)_hs-vHL;EJX?_ytgW2 z$(Abh5~NBqyB#!B2PKp?_Zh;eDVIXIbRhET`|*;`f#O9>rZ7fLkw#Ud(IMyLShZ=? zQdk^7F_JBkEv5!mq~2iV4Zj-zU$FAUfInFIV;~T$0x=K_R>2qu1*=dDc#=PRk{b0t3F}USINv!7u#Cy?z*|CTCTXiw%D9f}Hz;Ty6kfihg`4T3`UYBs07u3k`s? zYKpU}J9gR6S`*oliQ5At%rNJXX9am=vcTZSdE{jVT{T4cz1!RJt zfK0d@ZiDAwBw1lEcD07sR1En@(CFRCYZrlTTFl0%N9Pm0}6UcV33-n#kA6bfr zGzAaZ_}PP#)DZd_VmOjK*juF7Q|!alQzU~TK1T)8%K8E33B-8#?)z+X z5Jl`5e}!qu6=U%gW5ZwIO_NNqFAu6M;;QkLfiLf=YJPF%g>83h1KD5<^#XZ4mIKe7 z80t<;l3MSqIXJOq0dvcFi3fX$C!{70`97zQ(RU2^Ou=W$e1%!)SDw-D=QH371z#w0 zr-|Q*E=%^bM&JEs{FJTzg4n^T(MxLd-6@`qr0W7UT_3?oVvHchs1NeS+d8mk-;IYR za+0eGxT@%n+%U?kW^DcagH;eaz!@q!L)90B&_+%&tRzmPj}vtV@2+SQNysfSwbp*BWIewg~6(wm;ro0XaoA15}psJ{B%ewSO=W3Ij&Ddb2kkvC3( z+gE!IWIPXM2Q*PdC-QO3-(}ml+-ZBkNnCZ7uDWjLqIIdSa}LZ?g>V-R^cj4Vkc|&6 z_WJF0{}6U?9+*-OOnagMMc)0aBvlHj(w?XVKD#arSVOvV+P;AP3$Ja2d8*Gnzw?~7 z+cLN<(<3k8$1~!>e_QaBlbn&m8TkMdXFhey$?r35n>lSkTFW5q4CMKds;o%!NtqtX zw)&<>e^b<*^)r8G^)Ke%%?xGRR$)q2@Q(k;3Y;y|2>M3;Rdzak*3U1gRh+g26`jD3 zTYgS%!JwX7IBg*sRfwi1@<_VUlSrO7K7uQgLg-WYDAaYGZ!Vv|HR~kXf-|ytXZSDz zD2)r78|b=#ye{aDS;hR+Mn>Amx&d?5Zy3W3V~hT~PZ)jvIBMR+j924_ExmJ+ZG8}_ z4%Q=cZ$A=;Q?jR|MV)XJfj5A+(*6yP*Q{0%3$WAq?GD(%)6U7E~ks}Sic_+&)h*Y}^< zTQ!rDJfu|*`2G*8LSoutGiuInSpFpL$lg! z#E0A0+n+^^ow7fra)7gQ1l#pM#u=EuBj;HbhPio{a!R}?+KcyjVwBXGO{g=QV!XrG zL>sy~R~CF^%jhT9IVH!9;5hGsB$Q9;QPxnyNrI&iEFFOS{(~gnJR@%`IK{R;nA8XJ z;gOK94(loL|4X_VP0`W3r-Yn-dg8{ab*I?Y`)O2u8cXy%UF^H_d{NU@PV!o7`I>is z9BmYIM%Z&dSMePS8DP870K3fQms0v z?tw1YunODX`pf!ql8175$Va6eblIeSuxw8x>#_JmYxzVw3^|rCqx4a`@}qVWa=hfg zcE%YKfiJz=8xMvTa#{;ar~*EUTFXfP32Q$+;Uu@@aEs3lf*gO?%To#ZNIksQzI=Wrg{b?@yZeZ$f)5k}#lDAsRw|wh&I_OqMRXcN%NZF)F*+TRl zH*z@n zHms>WotS<2isLy>{c9<_=A$QBUTo3VZ_MD-pA+lPiM!QXe=FHMGvXR2@uF2;{PT+< z1F+rjP>>#q?m5o>ya|mf^Hrx=$wMP}Xfy~lDZ}ZYcbd@@PLgQ^nMPlu*-z`$uO8~YT|><~AL_04>^z}6D(|X&Ky3I$bLAmqTmQ)M%)qHdlvkoiMW9L)3GRNd_Os+yVNaV8#8$74A zaFSLfv?_Zb&&0XU8q4<^4?4qYvnKV{e6Xp+U-F(#>a~EA#M7#HKJ?nqO+zCy+gv$G zl}KO3N7c8lmv{ct6w671#QGrdIP@Uf{qv1vH2IK|oEPiQ^8uGoDo?fr7YO|Y{&W6) z>Wq_n+;y(rO4fQD0TO zG=!_qL!$TK9aK?-clPQ>nbA>vRQNR)E4~;l%#2|rPvr1Ku0eOE&u_g)6tc$O2>stm!JqQD1I~RnHE9O5m+2;8`T^e)qI0cuD0|-TY4$-`Dc{b(F^V9 zr6__8%s1{e;P8aAY>Qqf;e~P_x(iUIZFW3gi}kPhkni=MHa)oz9m17KmO!$kH+u3~ zwF{f(TNIsT%e)8udwyY8pi2B>-$-FBTTiGQLginha4{By_IO98IF>6ztKRdg&}}x2 zy$*{MTp3>l_$vCKH^>t&B6eDiSuFb$oz>B2b$S$Df$3nHZg!e(9?BTTf%R#+jR-6n zYjt_)$R9YZWpXIvuL1HRWm|Fs^f&k^=_%2y_Rs0!I96gU(OXNpqX$(FHJRjj%OZ~L zSBxBDEdT8I_~Nf)xH9P`R63tU4P_>0>v<>EzvJg5-D#v5 zZK=j&Dxc{BO%>UC+Kk9Hqwd+tY?G>8e;iiEvyxaj#L5SvwxPJRL-(_x$n({|ZHb=g9L0>7aw>1^9_2z?G8 z$P&fSVcYgaNq$jw=laSKIR71Ap2*eXZ$kN-=tdkX`q5_j`RT6;9|ZfW3ph!e0@@V4 z(Edx7Ly6;9*+WipLk>6iFll)SZSCz5P4VpO!Bb`7snVgeMa*q>LN)n>YC6hn^!Z}) zROpZNvk~h*Wc2@%i9{#M<=j@lP4RPC;JY40-i~FQ$3!I z;UsS;>J8NoU4)69qkE3bUBXFjnNqh*^=P%J7+yE)lknFBcDu1jt#49yr)goDbdFwf zj$S!Qt5pdIFw2s6<{P- zkV=OeqBQ#VGGa>}^gkG|7o^k9 z0t1vmZ_c2%8APqlp#RRG|G|KtAcGDTAT)5qLx16zZQR(U$smoNN@%>kkL}$jiIX&I zEt|Fd(JZvFd58b!nYTH~LzU{G$`XZm8hU8<=EqO(a@X~aP=ANdZM!G^@SH(CM2YO( zk|);Z@h=OeYx(B$^UZ&mbuIbv-aeyPAMFBjs{->~C@rKH{V474>6a4Od+;Tpf62$7 z^!_n&!)JXZCut@0t^CLGMa;V+E~|`_*qcAd7=n!ZqQ{O=A7<<)2s2I>IDGHDbj?&^ z1zKscH(eG*XHi4Y8zz_N+n}O*C$W+kDa1&7p~XO9j>)PEJ%(`-dtv40DT9)Z0Mi6*ma>Yk(0D(lx-SK_qNP8x0}aaEKg=bQ{NEC zZ}6`Z=7OCkhMXtHESY@~N-O7#X21nv$OU2ynt3tc7BTP^F>-I=fQWhfKlNq69b)($ zVw^z|^)&JDH1Rn1n1c;v(e4?{FWe=g+$Gbkv7CX+iD%w`1M+v|NZ#1LI6zX%V zB;J&Y=eLD+95gK-RX>Q6G*MI&A8>BY3)=|=KQ84Y*>cF1TVCnEJb$g|%4d{VL&yX; z383fzK8OetRp*js?6ST##rJH_u>)*R z>h1Y`l?%N$?ONI9#g%y{g?CbQV8Dg==(Qnjd6eEwcV?%}6W1@^yED{bzAXzL>)o^Wo^h3>>+u zpWTABoTNnpEs{PcKkC9q+KK zShLZAD|6G-^d_IZ*h{(fRrQ23oTS!-sx=vf9_M58udNNb%t#n*&ZOR%9D$ZTkA@uH zKDs2Ala!MBQa=A?kN>XkstvrB`ZF_JmxXgL3k{FBiiAHD38$k;0rQIr;rI&SWCL(V zIO&dXssXqwoP1X}%>Yyir&bDQ7=U}i3HO9Q7=Zi2$@j4$Plc18VxU_1Lp9cp8sXF$ zOzAt}^moEp21)}6H$ed5*?s)QG(LbxVo<6fIswa1z|RK7;^9y{SR4K%2^J@T;mP1F zc*4YTd1m;VO1Q?vUnw_4_rQgTzv6+TCu&;V7L!=W$fdpgV@> zB@;~MB}5paSCGgAN=Y$9Zapb z=9*fQXBZTRqe2E18zM*2THr|98KOM$1alzkYl!lhW7d2!%McYXzbGKFp(!NKGv}%o z4S#ioTz!RHXV9uja$zO8)Bv=R^V_iVW?RurTT%DnG1Czj(JU9yT!SL6qB*Xj`3AsE zG|LV9#ZA$VH$}4ze(_i|_p!+E`F$>0`CPQd;1}n`OU{c|7=UK+%x1A+JDenQov>r> zK*`}iiH%`*ovGE%lwo&WsMRhQ@Syg3PzMaEIc2uul-X(nP-r%>&}@nUcxE>5nb{%( z@WyP~8?%`Pz*;riS~brAII3nk@(1wD7-p(wWvUFvFo^y$h~92cB$##-1k>0Z6ii=X z&~?KO7Heh{YvvdfyQ_&{vQD2i{7r{uL5F6E!EZV>2Rk)}MhBftn(9u?DZ@ft8dpJ= z<~WX$7=%4AA2sI$A2ryW|50<7K@SWIe$s>qK4~IxbcsPFhR8|lB5=}TuOVj!IpgS) zcKs)kXO1Y3RgoBZVb&v(bIf5LrZ9@DDFe7O94b=?;8 zX=Z8chxdb8|3N(f*|DIhI4g;il9AHxBQTUkjFp5_bU453gub2ZoFH8wP8W2~TbNtm zs!(}V*yGT;HQ$FE`$5Z;y{Hpk)EWLFPp8b&^*~D2y_|h-OUJG>_RD`uqq?QB#Byz{ zysO!WzWh&Jlin6c52cYzy=4Nj3=1P04HKi`$Fw$|topX_nT)b_DcLSHwDq|`!3|2j zeqgXqew$<%)6^FfyrAUg8nymgB9}iw$pF~?7HFVAqelieJAC2;9&dB!M(=?P9`Hv> z9mP*=FZVf>#(wYpLGRD+`kyN}eMK3N&y@)@qXW$>(Wzin-T83Jq?VJE%Au6sY$&SQ z+4TOBJe}1R5Bhk17WS@EkUUvTr*mzSK!L;(ZNPogd&Ex@_Ri$Wv=jPveqFb4>)Z0i zg!Sp{vCt>A>XW)3iVMi7Z=_X?bU$g{F)V^FhnOPm8sm127FEMu zb1l5k8lzk=sSTn{-_G{rTJ~OR`CdB!tF_W2p>&8ON_GvKsv_-Ww?hhwgRCf0{OFd$9$W14OT+JWT2{50JH1CXwp zovxdM5)m=KNY^b-*R4Pxd2P8#;^s7NG_AFk*4n|S6ASXstKN*s<=UKVN+t7&VXGI` z*^kJ)#+3=xT83)BLuIBnehcM;>p4jgp)cY$(FIXwY<_hx$zbofT(Lfv-&0uM<3ep? z=}J!G26{JsMYC&m=?#~vEu7>t=s)xKDOwq_+S0`OG=4UXlCHA%jgyjeQg*));toAG zK9+oi({_^7pCr5EKDIYbDtPkcH79Y`S-R^+qpKsBHepD_w0@bawzm}Zmfy#k+1Ga4 z#4K-45~hGKK62>R$z?<1lnI$^+tNXw&i7}H=fIhN?M&v%oG^wH#>3Gz#Oe<>Oa2^s zE0cXN{Efl?e|A7dhZ!$M`yYO{7W?=*H)gU6Gk<~J9}7Zz%^0p38$}%XKG*rBjJ;v5 z8^d*DBNXy=#V;>kYad%=aov%^TmIB-=QW?75=z%|l5h*laEpP9k5_*xjGU zm#(`xNwkiR)@jj>_?YG`W+T^Ra*`Z5}Z z;Jv~U?X)hhqob(O7*5hmQO*2v9L0}iTTrclYQ@)RKfPg^l z&r6g*9L1-*VwA+I#qsLy4SnVc;u&!mkgAras(T_O40x}G_iBp~J9i#ia?jfgr7&h& zbl(i_o2gL_<7Kt>i#9$AzQB&~Q4{4+6B=#G9=ZCd$MazkTw5KD!O^%6x-s?pS4ST& znUl@ln4W66=l|F%c{9n|{JN93(GMon52V&+|KGc8M5J&e_AdK2O86c2E*pDVI1YQ4 ztx3~a8N9mEbeqz2zZrmZ-R5-NAL!M^bUj_SCtbG}fp6a&GbvoLIh*ZzxB|lYW1Wgk z&bAvb+h(&}uORdl{O-btGMgFS#wT!PKAOWvK8fFqPY&^0P2OksVYD3=njRM#UX|yj zisz;(lrMs5iH{lhn8`fX^|`12dg3YuglVK<8j~KwPcX1Pf1A?`xV6ATQn#1(fDo-JM627Lsh#rn+37+CglbixS{?ePFd$AlBThRH z?S3+#R;#Ghs(K9jecPb|^S>GJS_`kWGGt1^fJQAeYGqsU1_Um>JN6<2I<y z1~R}^H_TP{4YIN0z<5{PR0H6q8|kL|c2LMatH@7QP0TOu>)^glHj|`INB7vMe|+8X z*aTTeHhaM8Dpb2-k$H~`CC7y(=&obR2AK{CGX194RNe8gyZI9sP-_acri#ZurS9x= zc;PMg{ogjsoM@TDeh8`s@@g!KusyNPp4g6M16*;8_zfikT)d><{?d_NIqdV0MCgv(JvcU# zlXOX;OKOT{>SKHsh1qpn;v`jasN(H&0v7jqSUTeYCrMC1f}%It3M8h~SN6T|@*+D1 zex@ov(*bCAZOC|0!V&M!7uf*HzCxw1u%E%|FNvI*M6N_RWSFbsW&v&%+V7|T*6eoO zkqve1ZUOEV+PwK$TgFzNc*1~F7I4Z!JFSmac`yDJo4z5^0wOK6DK^nx2Fa+6b^_)4 z!t`Fb>`;^{pj2UrzDSQ&@pFt(mLGrG>q64}%yMN{s(oSe8ad9rzVGAndCk$tFl(1Z;x zoFqy%B}%pwNzSzg>4KDAoaCk)Zt_Ep9x}FVU0Ta7K6Luhe=hy(SQwMXw%~~jp76$@ zeS){>3Tln=SxJOO6`|>e48|@Uxc1A(HJ8MiQ|MyQPdxW~m|5hr^}MB3Z~2hr zOUiN&J@B{W%Gj%Ddp?8v+LXWSMiws5XH!jv3&i1qZf(qEd1dkAD~tIkSuE3%4hwOI zh2byk)nKnyASFzZ(`v?7 zW;;<1`f|Q|s@)e9^Y@-DVDG{>ImF3(A=`wk^HXO(x=_GrdqV1;@P|0YtJmhejHu$u z6e*yHe|k5R_Z?HW{1aE^HmSeOTT(WDp7GWVtu&1Ql`UJy%T z+i`JppfHX;BSh`|WyQbBf$#FUI+LZ4Ed3hohz)q^@rFrKb&0JrS*%YM4?;I;%AjN0 zRO6;!VtbG)8=out3AOWHi_!Gmi)L|U&Zy`!s-DOVWATcCKh-emZGIhb-(5d!$4SC9s&M}0n>iCk&2Gx!B=_ZTpU=rk-Mf;!IJS*z zK`@~Y=KX2ZXQTZ83^u*Yw)(vi-t#%4#~fVc``m*$d}1VZ0(l*_{o_Uqbt5K8ez5za zbfw*r;O{g48@^@OWo}T&e#zu`G$@wAsds{2{=livmO!?oAKIcq>r(dJUMLV3VyDtE zVr7h2wgtqi!W zn|NC{=ijqsE|FK3wr@FgIK}1Wey)k93H@n)1eU%D_~GL&Pfn5u`b0j7U;n)IkG~f9 zag9%q{glAJjnTdrJ8BUo>Ijp*=)td9R&N)${Vk{MEunu)bQ{E`d4@lzNNVD=WoT3x z8a>Lg+cz#`lk1=IB34_oIW+UDnvK)SW7hhc7qJ6-OQgTWo5xq~zg+SYspiU5sG&kl zpLKA|{;G5LlAl%fv(FFHags0#%P@<6Xmh|n!ujyhN&Pu(kA?cj!U5 zpWnNgkuWT$(RM|VjA>iCxhmaUBkQ%O;`L=JPL_FQJM5e%f_?8TGJ-`s)8dF$GZ z_D`>~`*ZiNT-Tq7^iM?Hk1eX1O^cFaF3T}DLmkXI&`*7Ht`9emIWoxMcMHCXpRG3A z_xTdjV}K`f3MxS@z(&ZOq>Z7XVRTTa$~;}F(V0+Q{ zbf9GV>$*4?|7X)`1_Vh)2T7)HP5pLJ*BHBg%UpiX55JbdX?sddr zSk7p@DUjb3m>X#IBc}KfbLM3TfBjET%dQ#yiD~}C+($;ovhrKqCbHEK(}Re4-~YJk zPU_F^*zfj9V&X|+2Fg^=fG}cm7%}UJ@!Q@Giz3(+VL0(aI5GR5hj%u2MV7FsFd~Ts zk;KaQ(?d&+H+HZKuP9 zqU?(de+!1bD{UQff-6%a)Yk~ReK0rQ@;+g=IEO29Rt{(7dNlFMyZSoYq{nrxj5Fw+ zp$~e)3=G=)V0YozD{KoM(W*zhc`tIxWLr>R+`oX&gf`U7S`}Pl&6V*a^q%~>7p;t0 zQ|@Dg{ut5yCSz`~2A!8Vc3@kk=y9q+<1=V)ZWM*vNn@>Sb5dd!AQ+@3`;g{bmM~sTY;0ccGkleh#lA ze}6r-j2*28Qr|#!C(JI1QZFdEC9y4syz9`5qyxq|C?;}SY|MJ6C zR&qv0o#F4LA%ka3I`Lq|RrVWSDTPYu5aejiH0gnK+ym)al$u?4dqCwoR&P$=kezWXseFZ@29o& z(~d#2RcZQHlxuScSH{~^<;{CcP2M{;<+W)Y*V-U6I>@XyIxoDv>Djfu4;r~P-O#9R zXf)UX>43YMA?Sc~-<9LfM}#fC&JI(M4uW*Ok)7j|5oZHuZ4TokK1Sffdz_(#FKbj9 zCtxhzd!g?NqvWDSd{NU4m@&W)M$y-vt?z=l@&cdX7%p$$NpAV>20H{XDmsRr2#4wV zy_EYBj^KL?1HK#4tLY2#(TU=;<(vZ7<+)Y-Sqt?<<+aFyXw0+A;IyIJ^ zn(k%BTggyKub(d8WWVXJj3uv(4R;@6sC_ZiVKi*aD4nGo1!pP#@P6o{!_<*;&u+4P zej|l9{Q1tl2Tz;#m=NV`jU9x(gZF#cS!Yrqd2U+HmWeVZqj>xOc@vhz9E_d9Nm5Lx z6qD}k(eG}!-;$F%IBm}f{d4{`io8EqZFaQEj$e*_w|Ugh%j1u6+ImWQcF!tyuYa8j z7AH9#AcFvY!?Eu6t9s`Z(OfOwV!b!-#Z&3A(vyrW?>~V{& z|BNx5G46@_hqg^wNv$bW%O7XTo%060D_hBx`D_lK&A&z;YqWvRj@n0!ISQba*SL@A1(6cvMb+LF)$wHAWxcHm?l@^4mcsQVZ^eHHjm+y8OYFqOb@&W z;k#xGV;jRbVITRd6zX$a;)KVr8s32OCS%yx^0x<6BNiaUUrqZHYZpGyfIQ8tWivYz zH$D#AfCp&t0&zS!0_Am#(~9G?U9gV4CO5PLZfK{KT=tjPZ2SrC{8F{bRBcZzgeTn5 zvUjvYde7fcs*OLpqFY_rcdi{q+|-qu!<=^QPS1l4=_BBL1eQi$|DqOL)JhI4S=(cA z(D(_vKA(Eor+W<&a!?8mlKXDgT|AyRYl1#9l zy|)*2?guX5kZf<1X_2G=&OANUQ|JN_t=e$L>G>ruy%Dl@C8pAm9SYT6q3~ zk`cR7bp=o*PYvTqX7&1Ido7w)7z*GbvRcDbYuG8P|DG~(SonHgr5eqs8qH|zIL;H^ zYev1-jPCcSP__8y?r=L-tFfuoOu!dot}GLKZ0~Ur6%(ZZQ3`8(m~waCuva%l*9xF& z7scE~aoh58XY=^D{DZd(;EZ-k!<^Ds;ROdTK2WnDQ1jD}BR2)AxSaVs;gZJol4dE6 zbIi}$KC(F@9r0EO^S`(}L zxe2n7t|creVY~j*IJx$OSw@G;k=?JAAOI(jT=MM#B+s~}ORXZI}hxVCiS6IgRMgQc5c06uS7yC!b(&ca_1atLsT$miNL zu%(~vob69wXZs+Ke`r^)P|?&-k)3SB(D1Im*_17Q3)t`fC_+Ml02c(>HXHNBKRR;O zy$fN%nIh{DS_0h#$PTK znGPwVBeI_McnXzuH(0tG+U{SBN^qmLzg_ecmJ=Zb5z>x$owk9AzIku=8ic%~>34K@ zJWgHP)&JtU`1Ma=#+($EP9euCdi2|Qeb_@-Fzdhh95`T%mX`bzlzqZrDISwTNPnuIF&FQuf z#jrj{2b~{>kWx7)C3fBYCf^PfU7h|67W_;Bo{?Ld`GQ|7dcRi89QRoJeAF*fKOR~d zE$*7K0tq=N0|#YYaN?60BW(xNY>4?!hy*+%Ny~U4ZzTQSNG4%tn?ui?bEjD)A|bg3 zD%W6*C$KNQAN*O>EgK;r3J^jp+1*x{+BjWQ&wCvzw#f zN*O}-E5Uw}v!YMp>hd1fRK>8yAI029GQ3ubn(}?N1t8>}mcFO$ffqK|(*$Y|%+kRm z2Zz?CY31ojX^6)aaIR!v^7dI8D$IUS40&9oz*X7r;$ghw z(7{E0j{o)?E(7Crmhrl7M!rdT%$PjJ=B(oeQR~0;b~F6#vlA2ldJe^(Q-E`dPI&%^ zb7~p#&hjooie#XO>;kacEaa)As7jKXMKgOwbM!Ee68Pb^OSW4X&HDZ(9sEu7MuZks zhZa>EA#dxuqhBhHl)z3NWFZN%V2oZ(wkQ-NTU55WKjVXZsDQWD(p%eWY=`NIUS$pY zUW6|HZbs`WcZj;|^ea1At%R$I}HFaOjU~?|MR~af@tK({QJ&hL4rHoxE zv&!hEP?jUYClwti->a(wYe;m z-Z;$a7Dpi@%Y@4^Y1>@y{>fwWq8#Qgw!KSJbz z%JPA#56QfW_|QDnKC0(hY zyeT1G{jpCS)aa6l@{$RS-R_=PC1l>3a35*ZDCQbT9+AS+mv*W@G$5qCpZxE&ZqIYx z4}1xw6;M4YlSV!IIIsM?i}OJwe=jl zN`g%uD&j)Pvdq2l)gZrRvyrp}mP;U(JbYO=ly;D24$_@*@MxE7sbz!C{#pim!DE(w z%ywz_f33-9ci{g^8*rnxSh}YS7VIVAyd-TZVV4Dn_y{>4avi$IMwRREoG2tdPD96O zn06b!VQHF?c*D0orP4n7S^?62nPgd8cKCO9GFtnsc!G)zW9Tpvd4W${V6kURxihAN z@n4*1yLWy>%f?r*z5y~2Ak*MK5_|SRo>O|uQzRYas49=BQKQZx9v7N%Ar3UhdJ2!7 zt_XYu+voztTxgelY9BpqA3eMLht;RuHqEzDeZ8z-{3-|uIjprjtnG&fPTZ{Ue|W%( zAF!O!|B%mu{C2j-2tA0#c_6 ziif(K9{U=bd0~Z5(BK43V@vRt$@L@j{xwJ=Zzb?ncEYQ-YtNj{^S4x%!=}hI;WEj} zU#fJ8-O#|6Lum;Lkf7j0c>W8g?U3=?`X8O`5faZb@vI&ji?Q zq!qH)5b>7-f4Kqg9kKTC?~2f`h|sUcHRfMU*ITFS2Vnchz{NjzEV}yBYbB4g3Y4wH zftcrw1y-@3A9h0L32|Ud9GHUh|M7$tEojlI@P?Ns9MZ`S>9lxz%M<)`z)z>b>9BZ0 zgHF+)V-Br}e`|iW4!R=lW32Wu{cxU7o)E@#3}brY{>c--&DC*;FOzz@Xq@P z#yWx-fD`@jgpZ8XN2VXn(!vuw^}RgxLt~;|B)6_yH<2gA>p{F;W$v~6a`njOnNLn` zyrO-w9kpG&1jI}F;*Bx4SbyF%;0i*%YU!_J^S<->?XQwS^+ebEs7TL{g5 z-*20zdIOOpnn|L&;|;jyfrOHgm(*|IH_Bz`Tyn6KUgY0@;$I68QZ5JOWZ8UhZu9ix z2Z9k&uK@K5YwU?WX^t{FJShwzXIc6z+Ziv<`-c{7u}z9ah^N-tQ`;B+A5+Q;dRKP4 zkB~r?4kQj4e^j}E5u4jFnp_wBo*7$JjCjIiAdEP*w20`?GrI38V0~^# zzzt$|!~xV0`C?$c7}^fkn&!(oabu1no)p=r6ykdFXie?rv)Pvsa!(5GNv*MUv4`RL zzgu&%5Yj|ZO_UbT9BFp{T8}yoDxqWeOM(0)vHkNO`G9ch0pSuHCC?KM3a1_vE}6Gw zVck&KAzPl{DxB^rT*g-2?KYu2N68Zo38x}5|37JJDpRe+(uS=WJlKk{K}e~Z zDpeaWV%^o}Q}NCjmGFxum~sikjyr7f%8tA2*B~TM#N~dxj*EE`0 z6U}VI2g2OgJtj-70#P|$X28p=6CR7?6Ske(+9?MiXXN0F+!~KOe+4+DPiz%c!PbeD zfmq^|Fn(GsyKt60Lb7EbThYT8fT3)g3t{i$-lc~=lmjU3dF ze`ID_d0IkNJVGL6mXWf)SembgnteJV2T6-J<>HB3;V%meF@p`ch^LC8s;JJmPgQ=6 zx487|4MH;1R0dgM2lqM?5z^D58g58#in*KOwtt>)t{WEfZ&<9uE*j2{ZRSrtr>}-p zPtwv!+AetaRQ>n3NwrEtHJs-zOTlGncf3yFg~UiV#Ym3{{u!FL+|1U=#_i;)+jAU; zR>Q`)Ewa8X8i5BHdCV3tA?JG}|1m{9ChEU++dcbU>I@|Rl|cTgoe3xzEJy~9O~3n? z>k_#E!&3t<_6Dnv_y7$PKoWl{J`|7pE!3|XI$h=npof+t|q6ENkc{n33o&{r1n zS{H)Rgb==vFWlr+?)Me+kF@C7gy6yUiVcFUk3aAFA&)DYz!>hFi@)X5qu-@6~T% zOUJWxJgdc%<+vj~#}@_|-U43QJ9_#%dKotS@Xc96(=~JtZ1>^cXryN}(hkcz3@;v) z{;&^EXrgB{(GKZX&DM@KF{7{r2Y$BOEPb17yZKk|u>J3_Lvg@Gt@3lF^`pK>qfi+a zO0uTDuaf*}dUntMBOp|!#2wYC)Z^0iKd6RA{lCIIUBLss`3QMCKSG`xV#l-N@z*BOHg;z?KX!(SV|4g)487;QLS;lxJYjmJS$9PnH-jo{X(1|BB>r~CU zu6+hjle%17ILEL#riXp+>Ka(VRvBm|R$%_sY_%aZfWdqX;${u{0a{H_H?lB zq*yRWWAz?3R;v`WN_*jXMSlL5&Qh0+@1d@N7LvgKu~#fhwrC4|_ldpRqeXP-J#5z+ zF;_#je3uNKTQ^S*Mo7BII-QJCpGVR)YEXy@c2)vsr2%_>q^h=Qbk}Q9!7r8IrP2x? z1i|)H4r*5ZxM=Hs-%(xTzn!m|xOM90Ce#!$6ca<@-S}7Is10${W?Mgp^B*QTz$rbR zni)^c`EKeDiGqZ6Ql5}N%}k)?;N(uB_O%J0fB%FuN;apGiPPIZt1MPL?xd=PHjs}3 z`NwvK(Rk7Lc+mpa2ZY>~f!oAnb;CQ@VPJr+4t}mEiiv8sM~bI5 z#8X>v%@sZlTE&q*bx@_3YU(9Phvqcm(M3u(sSeKZw=}bEY3AX6GOi_}qA6uM;wd+$ z%FPGjx%c<@dAqx-W+BAc3^<$h#ytp!B*AXsWw7)jrxd4{-<$fMjpwD;UQtiDOpdJZE*E%-_$PLVtjbk}C(f#I`ekXY=0p8G3|-NkJG% zS&!ZRVKvSPxpTzX-X_y8H{5dq;(5V>7i<@N5OnK$;<4#{7a_z~!}w|p*nYS6*^CzZ zeH;Gck$@MH&e*}=(qp^dwXXva5+PGZ$U5N>ZD#$V_urR?Af!x9m64O|Ze@-Q-u)y0 zOogJy2fUGSEs^3Q{hBNAGXwDxYzhnVMcl2(X5LTR7P_`HJV zIHsz(x2Kn(xEn%hl%R&Bam5D!kmt1myjFC@OXF#F+wQ+vxE=A7&~ypi5l`JK6;Dle z741Zbiwd}q6l~aL1RaA?1>#ha<$)Jpq7j#9j9u;9`)k!ba0&FelYyijk%J?01}|!N zgj9zt4gK~Jwne^(%O{KHouLKF;M1adhLe-XyfbYDODyvFW|4qe)9@XR3dY(|FvaV7MoN~X(XV#vg5x?vVxwLK7 zd?Yo;l*=KOnR`}A1%hNhg!rgwA9dU24RAba6v!Lf#pYZ#fXfE$_lHNAOj>R$ z$G1knf?FwVE7cD#R@Q!R7d7R<@dk)EbBr_Bwi_94_?Nr-H3A_I&A>zA^7mrv=po%L ztr{Uun%**vm@r~)2Tj=C#Tp@Z%&9xXx9CvTqSvce+90Hu1;s3jf7ehq_lly{qX=>1 z7&orX3*rhh=`Y9l$VT`{PpRosq#>V=6Rq%m8jW~PDZnY>m~qBKFvA@@K}ZZs$FK&x zAYH6KaX)MIMP@+N;#i6U2_Vm7gZiwY0^1sj(_WtaG&6k&{cqMO5jNe!np}TyK&16W8S+b!R<(j@FkyZszMN@ z-g@O?%A##N>0jXilYfOCChJzO%=tD<3LV;0g|GQ+Q*Q+;9doB-NLKv9lhTClCTYSW zCYzRi_^@r*Cb%0473~cbxd^b4o2NK44$h2YyPV4oE{r4gy5rvnV}1+clbjkE;=-5< zd~i~bF^LaO5`^<)BkNo^lgyJ++moL2q*v`pfALXG2ThC^^I0!GW72T}wv6*SgzJw8 z!u37^qY&;G!^jxJxOSoZBV!HYaOD1g^%H0PxXkAh{IQOka>q^E1{c3$Hf0YwJU~b| z;KE59N#&c^IbrrKsB4DH%)`mbEOz?I=`SsmpP{r2k#z>~jQDep-88Y2{b$&nLuErl zWn=N|J+M>e_{y+l2>B!jpGbmC+>4>K1E$;o@&jKOo9@tkWC1EC$ArrGUZ;8B*eGaKV#X* z>xjoo3A~8edU@QDy9I*_5t3z2WsyjP@g>cH7Rq{rxT$~}$tW_+G;T?Fh`bromiI7astoLAfsc8}%AcuaV>9F`m$( z0WBK&A6tA*WDM`TohMwR!9`jgI6W@u+TBAx@B~jS@YKq&1(+uU=|GT9ZWKrytOLP1 zxutE=kZ^?~%&{4z15xnCpp4Q2wbcop@J0vT=;S!mm?u=|K!r{|{ORN#T>^GP#VU26 zQYTMb^?p!xbv(Qps!j*$baI?&nJ3iiK)p`BIpWotcPFoU@`MH*Xwb=xEd7l-(5RCe z9X{@7!G2br39dw0EgA33v!t*fg_UEU9G;NQTBfr-jnY=!(}R0@`F-22<=$(a+rT&U zAYCuVM;<&OLk}|aa@+xVLWv%f=;io!4W95y4_@izMoA#b^`KlY&;5Z8burV;RgOMY zaAC1U3+mM}aFygncKoIMY|NQn2q}_-B9fNfE~U_5Gh`q_Je9yxsmI4bw)66wA8xWi z$R`W%iR=fD7EfesYty5cCgr3DRxH+4IXkTWcOh8X&{J;@!}Tb_xK2y-gJ z+!}A#@F4-zx>g~qB@q^De`TojZQ`#G373rwmrcSW)%MF_5kuJ_U*R8D!_YM(?7iD( zKVh#IM-UQj%7v4W>e}~1d@Z;$NLrRglBH?OXb_&b>y%q+EkaHzz)6KAcF8dD(5gCY z`-TvAHRVngRa2hz{a|@>$Tz54m5!?-nYxdb1XuSJEcpg?i!kLPhz8h90C}2~pjp`& zkBHc(03r!;VS>CZGhBalSY+~E9Z{8l3w8C=oO}Km#N%VEuewyqD;3idmcD8NbyZJ&(i}2)P2% zD0inlpD(KOLp&cT>LUpk+v2o-1sq7XZdtW;Ieds~bKmXIA;#!A!RR^K!g6NgqTZ{0 cCW)?F#974iv6-qpdxB4U_nvjKYRBdc<0CS@Vw9Sx$nF)J3DQs?0&3HP7XLk86coBb76}`_yENsmynDA@H-5|e>tuMK%hzA$e!cJBE#}{qpZdAZmF<*W zoz6_#9{2v$%0nlPjQCL7QUBDKj`^m6_BH*zu(GmjxRY*bea=_4-*^?byiA+8f5yL8RIi?P?5}eiwsEU_YQwMR#OiaFr~~k#)5d zZ;IM@`NBtk#049@>-b=4cg$r>X4BI{XTIgpRx5USu6mRBU7c%$|N2GRg$qLFw7v7+ zP$fP8-E+&GFZ@P5wR9Xb=RwUe+iMfw{p7bmvi|2spLx445?`D){$R~U52IU|b|{_%YxW-|ptaM}G*lnZf_O)ZiM*cduY0}tTy9;CI-ja3(f3xab!n$1t)`r)Y zmU-R0K77cFJA*?k-@1&BtWB599Py;?mWTY?_m9#?X9Q(g{%R%PTz5aa_(ATdpFj7X zdZ{oo+p6bf_|?ziGRp+x>#C|YO|A}m7Z?|lJs?aWo|KR=bz-dTrIMyG>xyp=J966g z%MjN{2C^Na7iF>4!EBsz5d9`)AX!pn5~ z^M9V2^?s73tM10+TxT}1di?ZEpItAv<_vdF>#*Nn8QpvP^B;vlXG%o7+>f_x9eP~d zwQTNyPXmTsm})DTawY%g2k##3+nf6)Ej4rBKW_(}>G1KZ`t+w@i}ejD(aLW|O9#n( ztvee}#6)`fq-~Xi%Z@;HbxIJmnyvT8HzxwycrV+}EqkeOx5B(QwIBQ2H_4uvn zZ*#wvHhjjjxX|P!UAQb3EhU@kbSAjBh!+-LRe?Gj(0KiS%$; z&8GFAzuN7TlVDMN?{~Ldkv><4kN94)F6PR)(?!2*9DRIj`6)Vd?eM7|`R4nM8{!yp z)YPx)RN6Y{`->dJc3(yxyjb%*Dd1w&oV0zPKepNTTl|;;Ucx8Ny9DFa6PA@W`T9>N zZW{ZaO~!sz@n46Yjgf3Q`ew6cqH*<#gdetEo2VU|>D@S#vD&zYsW>z-_D*EY;I5BU z-U9~se`+;!wpX`T-q0JSt0q_ET%NUIh^%er`o|kOcb=24$(uFdVd1^5;e(#G9{#@a zz)x(|#1l`I+ibpR4)4BE@M_tohqpv|7bpLo<^Su2V*bI8>6+lt^OA>V4bu2;yRY5j zAs_Sl*M|eXAN}-TxapSg*$WIk$`8?#zTW#f$>RFn?T56+vfJG^pS46PDnu@k7i@OT ze)Cb+_N60N>_6T+b=<-H=YyUNmX8@>C8(Rl9O~LR@rQpL_fC`@ZK$v^UwkR$%SScN zCti=PaPW3ZsQU3l$3K))?}ou2$_)j|+;Iu-E3G~kj(`8Jn`F+br1|e_2IOD4)w{gm z@BG0pdW7bS)A!LfukXHf=$*f3Rpy1v3pR%aKN}hO&k37>D}G#C`Mpo>Lo1o~-sX+t z|1G|n`({(~V*fj1y(XW3^81MHC+lA=YBBjOXn6h-yPt1%9VtF`bL7&jQ5W*RzV|Gl zNqbBsdBY~T&)wfSoLQOpIV&jO7W?HNF=(hkzUuhVv9vvPAR4uo~@+psN1}sk*e5T zS?nHK&-+j1_Ma*Ot40Kh%EZU+QjEbLT&fyXMvW>7wpetyySk-0?PYTcRY}r96F#+)_XSctrOcmF$d}$? zm7SGMTxis5Q$BT`HxQ|gB{>{RQeul$Wvy3D-|b#($)`S!Mxw?9hsFdswpjF^^OcV) zCY(f~uenqm(Apl*I%A7P>yAjR$GmOi^C?LN5*1L61(X}MSmf(8$aQ1$RrqwJ6i|7K>UR zRIJL<{$x!Xtl@F0<8u1A+_9e5%A;F(j##x}TVVDU2TztyeZ^hg5v}bJEh-9ni$%YD zR}&p(lxoYTiglUZOqn-Rih7>jV%4MpgWdPu^0DJn|8jTvghp^eV~Z^o865G8I(urh zoq+m>D|#E#%{QidV2efLeVB`D+D+|ggB`l0cO}wYi74q;c=Q#XBUUN5H5=FVe1=rh zb*daSouft>s<&9RVbP_Zaz;P47f`2p;V8y?@$|iTq|#fgI^Dj;NX zZ?VdxZ0FQ>{=3DrK^7O`X3AkR<%=yAIUIiPS@>7A#Ka(!6P3AJmbtUoV$s~vopUNh z5-Fcrrn@=!DD!(1D#m(?Ri9UmGml$nEu#&#>oV(bDt$QB1=TJd-N19fD%06Lx3jmU zAk|i#YOb1|t46oyB9Fev^T4Y8cBR1~!3*VlYKbn!yOjA|%D$ebw^;Sduaz?!ZdS-` zs1v%3FN(7+iWAoJ^cJgXCwp{EsMzGhr&zB1ZPD6p;i{M3V$r_3d}_3$RB1vba`{jt zG_4Y%rcZCN=mGDCz_RMIE;f`icXRFwCHI9WNA(tqg1?z@*3|own+Y|D^VDO-_E@3n zrMFl#(kJVH%8+&!P<~uNzNj(3s9~_hqTPj`h{Md5Batu-iDnATW(rZU(pxN=c+M;E z=*JeG0?L;w?S<*)h3V+l=q(og_QPNKPWkO#w83Uwrq?CVbqT1=zQCg|@SLzJHe>id z{~R{=6;N`n7%zyU7sR33rMFn*U7pt#vNd~vJ@q4(^3xil(;7auShOy@@_FmTd;WY% z#5H@j5*%(Npe)y0Eb`lOZQ3nGn?G%^MVF|xDfHSDFVsiy=<7T$tO}nln>6yEQvjb* zaYcP0<+zYS#YJziXaaxuz`uX#4&qazxLdqkYrCDh#d?cHDV-kEwX=r>^Qi;6GIfhG zzr{6tdW%(mZA~!Ci%$s=P{(vN@UhVRvCyWTr?*%&a$?%;utR@`@~K(6o@B4qb}!cn z>Md4z|Jc=;IiV4$#9TJqOmMiFfErP~#iGYC8KDbKRD|)VGrInCs+OLr71ZZ0Bo`7<1Y<;8~&<} z;!|I8clk-I_(?3vE4{^{bB3DFN*4G>qfT6xSM8K}JB4~my~V0CNyc!#xG+{C=FL3&PdoZ64X)ZEf!UohoyDqcRvQLv-{Tz5r?Pdb<3h*dLLb!e=qy$pbBXtI`e}RuZE%$L8H(}pK>G4P z(G^}3C2gWS49qFY;12IH54H8inUn8?mb$kk*-*8*7UXt3b2}a-PH(Y8Cy{Zq_sk*5 zw81uAN!^`pygMD$#7jK-5>JU$OVkagSJ!V!=2I(ldG?4hf5i2^dW%&#p3SeNf10My z201!kOWo9(|MNhE++kx5bQK-IT_s#&J#C zQK92eA&O6LvFP5o%c`fp4$Uy3>U6FOtk?o8)Y#}PR)tUc@UMHvBQ4*+l^b8Iq-$*a}R-X9-C85eIb`P6u>Zd7S)tF-6>^%jd#pX?fcyj?VePc70#*g~1NP^g91TdZGI&sBu}LJm>bj`O>#Jzgf38Tv8eeuJAGW<9!|v7pw3uvXDmvT-eQr;%x-R7;F6(y zDqq(^RA_B0w5Y$=TdX>6CcHa4{n9AfV59B=JB{g1W7Li`^XTUOhB;{5M$OByPez$g zYVJPw+MD*;qe7~;*j3n=h*!;Z{-4qYRXSJg?sU7m3RPL1#j3kUC;ffRSumP5s6=%` z_gBxG9_=(e+80YsDZc!%oOU0>r$%y@*AXl3h(+DF-eS=V*}}%e9|wH}VTUHCtB)U&e}G z#=6(@^cMTFb4av(YV0_cPkqPT)dW%KpVwD%}XhdHMs6g)aok%i2k%UH1 zdW%I%yv~M>9q;@lpZbe?=x9|5T2-i<(_1V`5bP*(+p%aIZE#rEhLlFrrO~o0yqlEt zCgo#bZp<^di0VJrhMX^+JY4=}$v8f>TvxL0Q|9-%0UY{;tuEKL8`ImlhT;y7zSG}Oc(rEzJ}qbZWC1miYsIUD=G8)!A9{-u_;Q)4;gem@ zC-W>|o2L+HTT1Vv9vZ)5l~?8^7rf0p-sP^Db-5FLMJ_y~U#Y@yU5tTu%IHLjB3* zM~y#Q`gRbhT&YbgritPA2$(a~G%Yi7~tUF`1HQ|+k;u6{IW zjG8p4Y0_IPT4;IjSWBgFnhBN8C4DAkI+J_8&|558al^8%(R2e6#d4ya4AY(rRMYep ziy9qrPW*QL>NLjSH%`=~GHOzxE<9$r#*FS&+9Z?T9!YVfhzvOzOwgG?^IeU!sKDgaw7n%jGL{%6Cx^HF!s z-Q(R_$!_i*>n#>l@Z`H5HeH`h8*I{*saYxXtQ1u9ukq+>{SRzR>73Iu&Oe^br#|5- zb&E>SqC(?gy~VD)mz*pbmp)<+ZBVXrm77ZErlQX0Dv!R}-%%eMwQ$Ptt~E%tPgmaa zqUpS7=@nihC2gd)f%IR%iv_e<+@I1DVPc79o zy{(jaD}_{gi&ZVQpN%xLT8C7haAm7EgYL~hk1TqNMHauU9KL$qMkHFObM=5Se?Xyf zs<&7*W&U-3p=#O!)ZlO<`Zo#mn*>zB^cIVTT$uf}*-z?)Ce)AI9Xg^hJ)*J37K>D; z7Jie^aBm@PutAq&FWu>vT!(OvN8jT)VwL)BW&XNXe-;a#+3}tC~mdpsZdz-eOP1bE!I^H9Db1)k1HvXv(*~zyEZ4h50$W!^!da;mpjHRFc|*R#i`AXSM@^^h`u$PGL7 z7OO%6CXJFO(7XB6Xf9RTw6@#0p_|@f(YebHEgt+8x!Z(7?Gq~L8q=#9lN})ypB5$7IFDRddt4T)ls)5Z6l<%8_gPPH6Y(oWo>1mbC^W#+TdaCJ`BQ_} z&33h@CUaT!L~H&;i-wAN+b3#P#a=uXc!p2ea-z*z+s#_ks_88j1*9z-Qdu?h3~ex+ zOZihC{gj8g61~MDKgl=s_fK6v%cth+64geTw{ewJZ?S6q!LI4u%cSQ}v&|Kkr?KLv zv8dqbEf&ocz7UV-mLrjZ>)#qQ(gqD0&*&`{4Ij*F^lIFGo=*+q>gasRaXy7ouD4ip zp`vWI*~*VE@TobvI=V;ev`33-vfg6VySi`Jobf5TL>uhXwN&+qbbTV~mM-(?%RCuY zUE7veet!6nD^>=Bxm@_bbNRrFs^`7uxxD8^VNqej^0>wkPwQ!eQk|=9;q$H5=e=o1o2D@|-)+N(*$*4x$;L$gD&RF$LQ1)lXl(r3AXT#;z9LjMHh1x2; z#i}nHI_}*}Qr=_?e&?>RL1omSLJ{gM7Ih>qq`t|>X%tZQ+??G8q4@?O8V%?z78!Rv zE^XDA7 zMH{TvU0-7;-582$>3tr3zkgUb+g|jSi_gj1eCltm7?*2p%e5#`dW&6E1+E(?QNO!Q z8|=}!+LKK0Nk)%h^*p+s=Z;lFF8r}@_je}Ew846vYMm>+&eai3`S9qwJV&fbo@(?c z>Cdnh0p-ATTepSgw}q%2>n#>N%SmJZvJ$llC?D=FKhc;!(V*Eey~QG@@1EMO{-w2* zPhHRz(MqjtCD)|sEmn=$`1#Qr6Fcwye>3!)VV*N6L-iJm?iN?1d>>cy0JX(j-~C?# z{a*rVi}ev`Nj zRBy4Wy=kCo$SQ;9d}=ILstPH`LJCb*=`9w`d$8@Gv(Hl``ipBEk7)$QxFrp}#Ue@2 z>g=13WiLz&lDPs?LYbCuEw{wzMp`4B)!F=>LFKCo^E>hich(7 zmsdJ^a|{!JHXgeCivnfF9D? z9^#gW^cIUQm!=9!+Y7pAgBiM_x7wXv?T!kJ-eQ$ii^aONwnw{+4SwOW>YB>{aF(#uOkj|e=M9v)gr(En}f7CK+ta_Bvuny1Tz+m!ijZeXvsSaoqr zsU&CH#vXeric8d;1fx4#rt2*ht($f0;mRxD_L>-c&c%0=GQG)lBWHQ0XL({QYT6Jf z-{Lo^mo})-<Q{Wwb0|%$_Ddn=#u)fTj7Dqt!3d9j{X z8&ZV+*Y}~*om=Eh(_DF6N8J-H=?Ra(nKF9of&~?{7mq@}ObZ+|E$}YwqVF5dOQASe1QANVN= zIk~MdyREUtvoi?X(U{#KK(ofInE)*svlarhYRp;*a93k?mjIj66`RuC@g4vaOHI0} zCf%=Z38$V{mmX1%D%_qt4grgWF4bRVF&oi4wft^#0MhH6?yUo4i2 ztom`92#QAWuP}*RVG<7-n3X2+D^1h@tTKsPWs(ZO88+$+n*hLBHsLIr2EaKs{u~Q^ zoM#7~XOn^AvMl(rEDF@Ya^=8sWiqIPdzHz1l^Fn3D3dCb=>VKp%g?J_@y%V9^X%RG zsWT|9nC&z4-e>qRZqD&O9usrCcL(<^Ncvo5_FN^!kCOEh{l*~d6mJMJLci#D1A+RI<8Gbf?vBGEXHQWDdK$E{ueKmm7xGtK$oxLC z?ES(9pMR|lxeI$69r`vp5m$<@`HxHTmC1(2=$9FM&l&sycy%9vT)uZM-yeV?eqs?{ z4Zt#f+A@A7xG7cfZdLJKcwm9|EE%Kg&~I46Ge^k#y>#1q>5ia;Rc9!xGX?G?_>U{0OSV-=LdcSKn-f3YC^X5Ww6t@YDxJg2Mvu-%te0c zBEQdpc(I>)F#(RpL>-SobDaM_jgIF0VtzgzGnGm(4NB`TTyIu2*;^!NEipybo zCH2KWynBY`D4rvRmPZUF_!k7a-I#7S1?WGP4iqjOsKQ>6Vo@ryC{@unm5H`Qr83J2 za5B~MWU8cZqYEnSsh0nxO8S;q(IQ}~WhVi;Q!Tsu!J+%Z-39xsjkugRDhN9&7z#?q zA#3+T*02uOSSQq2e-0FPtds6okHsMV_nkN2|D!NcB4^Vbgwq{7@%AAE${pBp2R8tA zcu015_=1ct_LLNRLU6!Sa)1C$o{}a4bbCs=2~glADe!_mD!e2W1gP_p)Da-tTaxV! zgIw}aT=EG337hNdIM+82fY-i~*S;{w(gBjC10XmsKyrWpEBz%a{h^{bKvEn4!QueP zVgf7;kSry@>Hy*D08fyPH37mk1Xvd!Tt|RY0m4%RxDg<^K{zcA6cz_Ur%MABO9SEM z*9J;z17S+r0wrw(cpoBp9|9H2LKVwGp^r78iZuk-5~|ojfbF4@?V->|d8neiU-4x0 zzjW;<9!6Zt*X2C2%lT{2FSJKRwMVJ@RniXd{w`l~yD{Pj}>i9og3 zsag!d4w=&q83d<&WT$;xaG5|s-UZ3t1wqgrB5MzUpd(Dw5$1?zYEX~^iB1O+-SJWa z0?(6W&y!v7TonS(GG)*D$A=%yx?>YVk2K*Lja~MMyX?n+%iC?AxSIfb?3H`$2LeU8 zy|TO?I1L*-GN9l)6Eoy&eFVEc!VM1=5h#mb%Ls5Kj=2)&jF(xFqArQ8OLD{O>v~;M*OK0->0p66$jehkTa`E0%x5<^9eII>0z6? zWRr0Uetu;Yp}yY_-EG3v!RKa*=Vt!kuFf@A%r%E#tF2_Ktq)MVm8ss!!a>?gvs9&7 z;W(+h{i5*ihy88J)xq^Fvz`^=up9TSST@3?UiV8|bYffd5L}V*rf2T*^c$Y>+6;1X zJIUpC(g092RwUc5NLJvLb)?vsOm9pUV(@SJT&qbtz0A1F%eG>(t=w<|5SYvVcP@WE z4-BT(^4Ya~D4D@BGgxO3W_7&T>Uaq*tV1R*yKcOBtQprTtfy?(Q*L;>6armIvabHL z`ajK!RE0~;IcZr$NLfTIs1tQbp>;`d__7}MJS(^VV~jbMYs>iGF5~}%6R<5LI_vWz zU+bJ43Q0Z`l8IXhBs~?HdMdPUh}2SUurX`eHgi|>%Tb}#QK2o~euY4d(5gmgi&ydy zxGbbE3t7Aph(N1wY^(6Q!Q&78wW{%EiVpnRD*PLOsScv44vwJnU*+Ji%0U6ZCQp z+9fgVk|Y40NJ5`TVgPt8iG3|e0iajn*DDDHV4*Z>p)>)2<{FIwZAL@$g8l%)TM&H6VD2i!{ z^22?3Y3I3<2Y;Vw!Ik$8wWLGsgC7SIyl&Z*-o35+rAIC7QM=(^42oZE_|?YPQh>rd zHDRBch;bc3;DrhMf&ec~*p~#DX2VRgu?ImGDcD5{G03we3U&zrb|}~#1lZ*qyvsQX z1j+MZ@_g(;jeO?AJR?Ad57R+_XTic}!EzAfMX>M%0ak_xSB5~vvM^>@m_6?5(bc|A zVPB_+@lNN#J1xX957d@iN4{7nUMw7dw|FD4Tqs^nfE_~d4&ea2poA3rh0^^(e+(|Q zUzO5=;E8TX3biA zJPJV0TC`y;+GGIQw83rK1OVE#j_q1s0wlL<2LrG#^V5BqKAPCw+==Kl@ zI>O94!W=+BJdh|jkm!hS2ujEEWcqos6puL(c$P^&%ar1)bNQ|DN1N+sX+!kO4VTy( zE<-?NI-QnyI&B2F+)HWhm(rp!7;Sg;h&I&Mip!a{sMxlsVYoHXfzfSIzhV%vJ?QG2 zWuaDF^Rky#?WOw$Fh^#1oH*qXuXA#RKkN$s56~jaWrF51eUHu$#*Y29CH|Eam(~T6 zK?@{*fb8v+4(pZv1Hh@wL8mhRz}H#yakXm2a$7z(WpLJrJ!=HVXKhAo8v#npm=ZJC zn!UAS-`dGR${OsM279QOFIUc&`-1o{`3f)jI)l5pJc3;wAqO?_c?A2sAM||qBt!W) zkdP<%Sz;(W4sI`1_a8`V%?`V9i{#mVks@v1YFH1AKano0!!f9m5;oP}rSR z8g;Ki5i^%mWH6ZFev&*aOVW5JE6^-ihv{t|%0eg}Am{jT@TW1&D*e#upSFwoUt z?bYD%AkdkRL1#jK#6aXD-23p1DZ{nWo#v8Gb2wmaR!f@I&bZnjMT=U}LV#Aaq?G_w zX~L>BISvw*7*{Pa8OBIZ*nA=*LuQDfk38{@dE#GyVxKrs3-9DDTk20H5{b z9#m<55_%RTH+iJ;ewF@qX z2+Y#>&C-MeFk9m{yB}QtuJy=Y7Ct&>^UR#)nMH%P>X2RZA-fR(RN5;m`=>0B_mY_O zl9+FB{KA?*dHFBd0(j_a|N6eYG6LQsEO<;RB;>^ZBIB2P#(kuHvosEr+ei zZD0OvUnOvR*e@HkIE$dB!7r)7ZzRyP`6aax;Ju&fyPiyk1Q5xtJ7>i_7}`=QW3a-6Y^T-DdURX7!ig-nXcS zx2V4X;ECGni8>MkJo@7nKFAZr$eDicCzc>^GxPJCnZGU^Hf-XoGqt8V@bAsc1w25q zKeEjJNP$la&D|c=qnu)8&Gn0|a_?4o92mPCRwN!)jKIKgjEyK9*^O%QR8|wrhmj`@y(%!V})7(`030=x9W_(}P;Y8hZ^{SFZM~^&y(t{Dc9^Djn0^ct?dF1ZbNEdD z&|L8Fe}KzFb59WDk-6Xzp?GBO@`zBBThQeeHXz7e3wrPW0O?)}IAW@>peqPPg@v?& zQ0%vG*>3^Uamqq)$^s_+l!fe+g%U(tVqoBKEokT&ioZ zpc{yUHCTunEF3|Q8y55pLUF@Fbi+ac6gMsCn}p(~h3F=ssJ9$YZy5}NRMIJxbOr!N z==38rymO7TZ6giuZ6mE}?0?Kc1*Vz)w3+T(R74=pDm~8%`k2fAVlMw1Fc&+Q|I=Ll z?*NqWqe}R2lfyY?*g57?kg!M0w~v?~0C>*~eb0;npobaV!+Z(Ae1YqHffoQv1+Gg8 zutE^ELI9)f5KQh6OaVb&2xjnJ2=d{A&&_z;0POfgoOi?j4xOSk(o#?^zY)StHADWNm@s1*>{NC^p!-Z?J`BXqLUtEc-wZWWT*~zdej)ri0&1 zhY&DeFK`GeaEKwm!~%z304Q{bEOdwipx7a@m;jXybftq0P^=R9t`Y@-LGCe;?=b@O zioAP80YH&0_RSWU~lQgDjSLFP1^DMCQ9h27Q#s1SK+i5KEaXrA(Fq zz;b!|a{0#qG|1f>Jc z1Mt*I`P9h+fagxi=L9(8oP5R^?%?Tmj_r1an|{_P!`3KcfRD9Gv$aZ?jP@v3m~2a4k^%yAbx08Y3tCkW8$V$m0Lp@*%YtBjv<8K@2EqK;9L#JEhFQ2d*kg0>0N~?rh}q!~ zn9}1RUmOql1}JhveR4zLo|yd55&5B?0mY$E#i3AF01k&L4ijKon9H^>PcSL`D9rs) zm@fd2!^|Fs@d+S(9Oewb+wcP3+wg@v{6K@s(B24-y%7TdsEKf|iSPyBT!i~M0$hls zFGSh^#l=YaA^|Q%(w7L39c7js#mDo;DBA2OX?Bz|0Ch2bbul4$jsPj@V!o@3`4NCK zF`j2)`~cW7P`rb9NZL8jW9PsDK(T9}(=K9@*xp#>-dGRt7S9H8u?hgn;+)FjRCu8prL-x|zbP&hfcJ5c@8jYCSdi$oAkiPp-7`m3=R|LNkO(UinG>0F)$ol@OpN$-O4Y7eC^2_cmF!H(A+tGziU+ zC(F(zE8((1a^$n*H~_knL%Wls0jNlEtVnUg=lhV;iz%TOQ=;*rm=35frep$eDJAq0 zp}3TyzCxANZiuyXCXh;b~uXY5N-;kniAQVk0;Y}$6f#N|5{UF5# zfc6x+odAzg+#aQP1I6={q~|Fb06J2VItb92lGI6n!c?=uR6f`cS(Iv4M1XmzX7dQJ zJaxqK)XzYWwp5F@Q~}r_`CqE^zf@-cI#Z>c1n5qcb`u~^ZIP!I^u3G?9n)7cGu3tg z%u>r|sa*irpr$vdZNOZ{2DNg7+5>QJC)Q7c;rMT^?Mg;2Drm92!LRqfwODDJ9Ncl$RYpa$fwI_j=E9>j82J>;(X6968l zl@AD~57hn-2&e68Wjmp0SNpdUiic|DLqhRT?f;Na{HJ#PkI1nOwQq+y2&AJ!{c(qS zI01g^Q2!0UE4Akf%_9_fX@Pl! zB0tS3KTQRqElhJROmhdIC@r8UEsOv|i_%~%TbMRrVOlUyEKG}Dm60vO5cx)tmY}7<=Bx2d5@z_KtHfd5e5wUF7$hH%)T*!#N zkO5CCUCj_&&9Dc@J`QM&4rnd<_SkT*doVhvwZQL!KsTycD{9s%z?^iuR@6>_XIiIc zTDW0seP-nPOt@;fF?0CF%+Yv~8adsU>Ao-17l8el?)wRFER#N#X#*6;GaZg+$^mH0 zjBF!fd7NqUIMV?r9%lwT&I|(}JIf(E3;M{;a?Q^20*YN((p_230PN0^?j}HGR(NIB zK%l71`m-`?Di4sVtiY=Ny|Vc2wA`Ak{*AO?TkmW`o7sl$V19kJ;jr0;697179DmGs zINsJ96kTo6@W)77Za2_D%Hklg1X9G;7V!sznbjvk`4gcFek&yk@>cfkTiFj_60B1t z?o>H~16H%6#?6iz51x#Q;yx;h8;0MOihMlK1U=A1;6)My^0nf8ts_1Ih(Kx9*QHq# zaE5HiJg5CEy~1_?`lXi7*7D&2(JnsQMSz10dyrV9`M@wA7zZ%ZIZeP!6F~4-z&<99 zvc3|quLv;Jg`MgG7r5@avUgpjU~;3^jqP=V;FyXzrg8v^S`|}EfNFQP+FhD}U(JU0 z>AJJ02vFc*S>Ry{o~j>(GLJ&x&Z?K8%u52i31!|8peK~+AwWSGQ$Xy_+7Kq((0@b# zW%8adc2AfTJa-=oV-68Pu7ojH2+$P9G!fu#7;~2Zl@V-ZgcLk=zl^ke8EFd&N`4fR zALRhRxhUow0q#aIcM0$yig`eQ#%RmNXj||IJu{Y_87u937a%%K8f&>D))qVux5Qeu z5TG`Wsf}|0kG~CZOalRy$1}_0p<-SlGcVBrJP$8VVwWdL!P9PQ65E<21rN9DQkiwB z4&Yg3g@#?xzut*%LwmYqd%7)XYkRfAUacdp7D#a}OL#7;58(GxTcdVD|8i<@qVKH6 z?5u_cOO+Qj;)@zNeiblsc1a_?q>@TVOVSenC`pejNgoKn zmUO!<=^}9cbz8b(Te=c})9KEq)7`*DolBRUOLqdGHQlQ<9Ue8jn{IbET?7;}GDI^n zWB|<0NS>XM20%fEsDMxuWjGXNNU_55tBp^E(hNIp$#|(@@KVDx++O08atD$1@9qX? zh~K5#zUx1kd;Pw+?e2hwcHBzoHg}6{?jE2jpY#l; z3~vcTVGm|~b1>^4d{=Ri;a2|1*MTGHTtnZvhVeM9D2e(PhvSuhVTDim*o)Ej++{ts zPJY~fSZ~`F(dqIfpW1V2J#CbH+Grehg1`gg*aya+;SK^n#&Vl>$Ty86>3y}}zSqh(2Ur$ffP75qs503{uc;#I_Tl z(}?XPK&~;HYYc-NHD!*P!iCl4X3TOk2o9P_4w}K`+aqS|5n}&jubHCP3?AxOY0j=R zhs%|#&6(B2GV(riW*-4gn6oE{AT{Pp4WYPd&R!)HUFK{T0g5cxA`2Mpd<$kiu_oMR z!L$)zvn8|H5?<{+OXeN{-dVEmETPjpn$08DVzeQAN3-_`&_*+DG>mqp z6+6=k#!_a*mJwi&6}yK39ad}y0haUG<$S^ipDiQ67Cy6u52LN)vvq{x4WE5OfI|#> zh=ES47^aGWK4u7*83G7O1xzUcTCJH@Yp9rG!^|NL2Q0Q>7L$Nw7PBzOK9<=>fVV93 zmH@MD+1a)*r59}33k2w}WjhEk*N&ZQ2ZI#ZF-3MT+6ud26?UV+pl7N*Gu0j{F55Gg z32?`rxkG>#_RI?cbULt|4)AO?J z0%S|rYzYxW!fq!(g@mmjz-@`*w!{tZ142#CWhr}E3VqCy31`XVU?4k7uAC+J1Yn<> z-6w}Z4#=4Ua=1i%M!}p>z;(qXj?5BA2)Z1ZE&^`q=K5=87 z5a57{IiP}yIu%n#fEymn4G(zCWwj@>+7p7Kp3G4Kw0bhF1X$?BEcAjwE_yK+2~g$D zRCz8{Gj5cAM=s` z+x?mC{!nq-UwPZ#6ThbxmBRP_%zHwyIe^(50E65KVD1ngCy>nv6yt#e^079MSsMt0 zEC^y21VK<4#8eWXD3~bwMh0F0hY%|md7}QC!np# z?ABy49&kPx9TMr7a9lS;dmbJ7y#IA0_(L+>7_lYZp(WlOf1d!21odB&!SCT7KKk&n zVL!jN=MFODSj%&);g+{NYk3|4DsAPJwoviGR{ntiJu>GW89b)4Aa#f;jrZ z|A0>}h@sQ9lJK<>n6R~yVQVEYVJBokCuFf89VcYNPsqLmAY1-Xw)|59Ov#q#^Dw#S z^x~&|?n{N-jlV8;ye@}(XYR=z@AZS!{KTnG{@W(xcI8(K!m9;vqh77Gs@6Idj5%&w z=b%?2tc2U&?jUprpgBttJ|3yvp^N*iY|2~N?7n27C`z;bF3plc03arq+h2YeU8Xa5iM(*^vCc zE$jG`M`kEu{b!EA0j3?4#STgUb`~#_%9lyud8HLn`3eH;mAdVfdgG#s5??7zu9T(& zuutl{PYMqi@0SkPFAc_uPqN4MIvd+L=nnRnDLT#kK(-f}D+D)LcO3YevN6!p=gs80d7 z8$IxDv~L8n`-*sTPpv|=j>@XCsPMS#_Ob~PXR*vV&h@?o@l z`0O4+ah}hfC%}6?`|W83|Zs zS^rB#P$@jbGKUDz!!kVtm}<+SZ=ix^ve1?-B)}D0_6h-B*|M()u+WZKXa^HkVdqt0 z2lrXeuxDo2Lq(%K(@223_RL)Zv^p@Y4noj=wL7rw4iW&~IIwRBP#|IpL{L#FV$nzR zK#(>O+eU!3Vs@<#X!0Ak9mzmCRqj9M- zQ|b(Z%yf~=bb;Gu^Ia7AE-K*Ts0(w{1qR7>WwKo%IPA(Cb`^qNqr{CVaf6`4jp-mj zm5QlSLB(nhX0?Y9bR%~>m^%d6;K^+8go;z1%qar2dot|=DDh%SykL-PUd%NDRC_bk z-cYg2huP%=!EGPrHUakgGW&g@V#WYw#sCOv2Qak+cs+o5O@K;2rqT}v>Gor~2~h6O zl>0-)v;byW00g@Nm|X;D3t-v^&=bJ+1V})hZV69V)zy-fM>6l8)X-5X!WL3ISLU#w-YfV1F31p8z+)m>UFW4`bR1FfE*! z77l$BhbxQ2y+ACL;VzZo-r&)+A>6eg+y{Wi;ljt^3IGZt*un@20BsR$8v&+AveP4> zVnHOkfB=Uh*~9%n%>SqJnZi`Wptd;Ml+7mIKfKVCT}Xg#hUq32ZS!rId>goEyIRSt zR>HmWZvvP%0S@4vb_X!s1b7uLd=(Dg+1nK^>>|M86wAdawtb(bL1Py+TdnR}-#|&P zQ4gq5!(BIZYTr6y8`jcH;nK{$ML86tBvV+@53c@sVAy8wOCs(N=1hh2Oho`_Q4T5G z4iU#e_Cz@Ei3q^=LGab22%EF_Mck8GQL1ZEst;H{Je3-HDs>=!J>>C;4viDO5r{eI z0R?+N;RvFsac67X9r1D%estju#OlA(iWihn*eXg?0?Y z%AiVR7`~d#|8`FuZF4|(ohMV%PNrty96-{xRJXQN54_uR;&l1%QEfa4mnEy>%vQw- zz;!-PFngZR2RvVX7IbyAm4tf&$}@M)GY<#Dg3FSW%aYOfQ4A^W`lsIY{|wCYtxguN zPWH!BE&~Tknr1`|l?ajakTHA6Sc2Eu5IADY9wER{WA-Qkjv2GZ2yon(Jx+iV#_S0K zR2#F^1UPBTo+Q92WA+pQYK_@i0@N9^bp$wL%$^~@S!4Dr0nQn-=Lpbf%r+9B$(U^- zz%66;76I-Wvv&z_&zQYOfCt9x0|GR;3Y%QvyH)>lmHg+b1ifx~EK?pU#B2V0-tF;S zZ#quGO()&4bGl=fj&JTqpL|H$`|u~-FN>^)F0!74f5BfW=B6I>pDD(*2RYf~ZoSD} zghAA1{=AwWZb-NazQV+Lg$aC5Ub)h(Tp5fDFH+P+{!kY=1y}IebH6|G3^S2(NxQH9 z_~hYIK2D(d6RQM1T|F zK_|jv@MWPOC&H6Xgl7V9DbnRqBs`~aGtK#C8rV*lG)dl-qfg+ra~{ zYaA4796a!}6iUZ42gx%BS1_2JDw0eUL9j(6*+PITBE=Pv2M97>ESWEcK`x6Gm&H)A zK_b~8fr=)XvPtFxd=$x5MRI=t7RZ$g&9=HExm`0_=Ae?ss>>RR!hP%>c>G0C;dHKUA3?>I1s*XQ9Gp zp-x~plM^P)34`EWg!{YxP7*mSiWU|{JN13=4NW}7Nt)waagE^=Ns^)@SG?H*fxIk5 zUX}-*0oxOLb#-b`kgm2B#Y7gxq~qzVT=nyFc9{dETJdri zGLz>rtD&Tj`Mi)J0u?dpBBn2b@UY*eG`LsmqRuz_BHygvvPY=$nUD7u=5Rl=Nl&D<^#1j`Y*8KF81qNH(rd*3zTs!TYg4l ze#R)g83*qs;k=UHVMo&0hQ6~6HNdO>i|yE}{Zw^!d1#Z2Ym{yXTyF^A)M2k6s8;~1 z>lW+iE!Huh4XCmnS!MkR0F^QEl`#qUUL&Vx)IMj3ed_t?1M<`T@Ed3Gm*Tk=R{wQ) z(8A6&w3%zDz-dR$<{E0}8h!%6N#lVhjni=p8~vNt_q$&2kUJr1y{WX`wD0529IyJtD;_=zQa^pWd6MWI7Np+gz~#SRI@1Xv{sS%tol-XG+cDC8Ic zvc)0U==0(Iil^d`rvx}D4LT`}1d1iHkR>u0WSKm8nY`~STqx;pb9en7FzGjN$_-^V(UBHueO-y4G6-X6QX zVUTq`aqE0ifsge*(d&KsKAeMmlmvK|1o#6*ZfJ0BD7=(VG@&<~j$-z^TVJvyc!FdF@kdbsD1ARPH|A#l%a(lLl z;;f6}gm|S6#lJSrVQrioe+v+SWAWl+@s9XA01v3ztts~!tj*h4hsYd zWKIRLzIk~BI%K{bvPj(AAaFX!@pMpM?|{IT5XUVc5Nr*RZ4K$0WJij(@osP9{qSNY z0xQ#8R;KkGaYA5eR>0E!4cMXKsQyF zq^e5_0=q_EX_T)tea#PY+M`kS5MWxma$0`_g%o+|$~*!zW({r38jHI+ZOFXF#Dy0X zT>F2|Xw*HUzd-}l5+!emiow_a`=D6^?@uvzv_#Tofm5@f@9rYdAz(WMaG~afK=?uc z3)2UI%B2-k&G1^@9gqKch(sO*t1%&DF1(J`T7 zlM`2Ow`gs*Xq~|ef)-|mEzC^Dw+VTz%@ox3uj5?5`uM8D-}{`nN7VOj?0Yw7FfyC3 zV&|(M*s2n4Rk;DhnGE5XjJ|!ik6x)>t$S|a%ym75^ngMdeh_9EJzyCDHpzlE5wFJR zl>2tdQ@~(yv!`aWCw!UVR?qmYp74EMmpoH1d43LtN>4oheBw#GC2WEOb$NRZ+3LcX&T{c%JwDgFpDrIk(?)?>*-{=S1YY z{sp9{b&aS+Fzx!|wCgHOLoT~}8S?N9Ib2A&d;>dv+T460Y?=r1BkbQLm~BWNK7_J$ zx*2!6Ie~R&xkqHV!;NWhPi%09t;)CD-@fJk9msOlBkU|1Os(AW({j(>K#F0nds@R@ zkKvX~c`2v7-UMliy`{z8VE}G<+uiaG0IRQ+XTyyd&{%g%b6lua=t@EJldEbtjd@JitG3c*_Hx)u%8 zvyQs1n}b8SGe2IA9G9pIg#Y)p=Qj1Hi;lY!e|{-Z-?~==Kf4lUGJbitbL5Ik8$G;G@EpUi5p@5!UX~O=ZAlL>kNOP zdLf5+QHOY+anFm8%7MS|%O0aQ_89%d-EZ}h?QUf7 z+(J|KO4&onp1Gx|!IkWB6@r2$-J7O+ivaWl)1F`vxJ7z{X)l8N!Sp_YzF^vi;6X5b zfZ%m7eT^VhMN?I9E$J$njv!M-GZCy*(Ul0+sOTC5*(#ciV3&&SLXfYb`E&3fSK!dF zl<%m%yRQTrUkSDXyFC-^I};qv!Lg~U)j>Q1M-iKJqoLVG)Ei)zq1i43?S^LU2nLAZ z0HNF&VwaI?!p}7!07e&>jG|`f*9%NuBiL_gwjZ@AH(4%dvV@JhYpl%HpeE#NRtv6K z!4yxe%$}kYdxU0tP!n>E&4L;mm?hiRI@=ab7BOvWHf<{e?;|A=lM)GRnC)^j>~f^w zeJIDpEXM_2b(yXUGF{GY7?&1XW@p9;SC+iRb#M_tIyBXhnG&Nl3%JMV6R>V@?QoL?!$u3k{Qg)3^o8& zcB3l8Q5EGbZZ^gB5Z?6=gGz^Rncer_MyRvI@-kx$Knqw!ykSK=_QHNgdGU^3wM;am;M&gpByv$%Q9p6GGia^n59%WE?Iv2{n=mE zsG`=W-sSSxymE8T^!lq*-4}SBk?T66==)!^IF0_aco7SEMs9gVu>dNJ94m}MmpWcM z_)+wt8aBmiBiGkP(cEfS=m>Y{2v>1kk6E}MZgW4}kNcq4eLVHm7pwj!RUhmYt8ZGY z=K8U+lIgdW{VUPDE^`BttLx`77=8w0?ej%p!lTbd{=z2Cuu90V`k1S2U}3jS!fqP~ z9mz2r$scgnkM}caM!U2~YRM)o)1hTL*5Hyqr9)34Xwji92!?EhL$(f}P-?C$TmmJWAPv>%7 zQjXx>e9Cr_=SnN}w$7)W2u|{aC;4#5*PDEL6ZONK;EN{susME>o@k98goAqYAnH_k zSdTf3x=a@9(PEUMRF5e|DQfh@HF^$UOCRbn4^fH{J>iHR+|n^U;TVFI`gA4ghq+3h zu0l|zPsoe8*n%7qL0M+O-H3<6jNuNGE$gKvX)d0diL*YI{xM78c!a@XNMvh}f zFxX+MG2M!~OrACto;HTdIBzUGkD%IESdE~|n0BFMj2nx_jp2EjG?q*nJAq?bL_|dd zrdVP^mYBeO*=RyGBG_p{b|N@wLQWz$Z$i!^xM)HyBDicqE+e>RLaregG@*m2bLY4T z9Y-)}LMIVCF`-WoY%nDoOyRy=PFm5ER&cdFR`vWMM+AKKH0r~~mTiD(rXGI*1O zZbDz7H4;)If!D`X3Au{kk%T@%SynlSR-sI4?UpdlOD9d&yx*c_fKI+6Ab%M*Nk4epxsr`j^Kf-;sJt(u8M~U`dtOr?6IOxF~L<_C( zAQc|4j;g^!*x&*8w$+2Qq7=70$Snk29>Ok^Wz<7BieRUwaHl6+XrHI3&r<@r$mV!6 zIo@z@uX+owqFUaE-ol3n#=J#i-tc5E_Yo~e(+Fkwh%ykY^P%fdr~OhNVW|(?uw@G2 zG6jT{3gJowhy6r{{ora#{Y0e*j{Av@BdGObYW?8O)%h`X2>SgmU~GW}^LiWSK6r&$PA`qPyN_WRTQ2oCtu0|*ZK(}M^O`O`xP z4*S!?2+sMFbEuZH%AZsrxa3bRA-LjCt{}MPA9T$>6jYry`3E&2X!Z|kM)1^M_|zZ9 zUt}t2CJL2UrxdPJ!W0{oqK!%kSWAvllB0A2a6!pjP{O!}DkW2e;G&XVRKhCUY9*~k zP^0`$jq-O;9ePPgFQF8TO4|7UfkCZCo0kjxFNdPTDP#!?b5u6U7 zrx8>H&mALL1O@IL~t#DUPEvzfVqVNUD^VeHUuL9bOdFY44{(;mIcyf zf$$1A6v!N!3x#3p9*zVuM-Ws6GF1pJ1~L~BR0lHE2xmV-62YnBZzdMz3mK2 z?nEiNf|9!sbO$AOBj^cA?m=)rh}=gD?F%A(2p$BH2MGFuNI!ysAToepFo+By7z!do z2!?~mFoKaFGJ;?RjQ5F$Jf0-I}3gwhjpK_Bcrw>64xMJ;IsQNn^K5vYOgilSX? zJ6NdXT4#dg=WLc+(FV7o2^UYn!o6sNdk7vy3m-*`K#CX9^u=5V2Ad@>hUCRi5K}f3 zLx*A*u9cikF&0C|5Im1zp2rA5iaoJpPb>vZT?MhUAeI5JKbG!CFcxbt7E3@g;iFiC zM+jcT8oWTTFOKeuV?g`asW^HHK|wq%h=(aQCor26gdj^!0+WMaX9BYm!L9^m7lNV$ zrU=1!0vS)BTpw%p05vRP8Wst;_A3@z7D-wbIRV(8BH2IJ zvlPm~kHK$LJGbzh)eUL+l;qEiDDgMhqBCc%+c>Km=nmNYaKL6Y9Fe477S%8NEBFBG zQzZ5&zS2OO_NDI}e4Y7h=3Fx-*9`W5%rj&15Nt7Hwjda?77kf!I>fTe7_k-4}#q;8TYim|FHfAU4WhdH#Q8G6!GTXSwnL9VW`3qxLzxx-Nx*_C-)Z&KJ z7c{}#l3LuFgOI-pgT}HdWa=Y--hiGrfR*Ie4d`_QJMHMsxrpx5#dcaLpVi1_d+{2@ zEj9WARMYM=j@xGpVFis^K|cX0UYG~IFn<@ocH4h!x1Ae{=HLJLEz2&eU#5;xxTa-z zP0Nn!%f)VMmbP1#b_929*h6wP!tiQ@CPmlffBy9&6UJ421da+!jtXFxwNrxRQ-b%n zg}t}9VSn_S9h-ltecPoUa3`m*W;$G_$3 z<5^@ODY6Iy2dCIVQjDO)LQ;aD)Iw5<;JAh4_#8xT++#4P7rsT%QvUhuzT$|nIA*uV2G|B(30gt<`dVYBO_oR_8t$JsFX`uEE zR8v*|>DPztH{YF^{bje&8@uNMZ$cNn_s1V+_1x60*Uv)v&q7W9uV-eP%fEKqWFaGr zpAlxV!71qQvYe?$?1|kFX1F2D4D_w94pUTz1#`XV*eqRP9$jJn04{|aUJ5tkI@_<@ zC#^9K2zFB+@PS~(KyWB`Dh6J?`{PF+eLwq4hP5=qI+hDCjVh?!o_VL)P2KGLn2|na zVnI{DO{wB0YSp|YRo;@m&z(j#%Y=(;!X-gdK(SpETx637(qytU8BDQMCR>UiT_#IM zu-09+)*WV9=Pp}^V7-7f--k$8G>W((qjm!JYB0i;lNE7 zJzXy%81Z_4#7i^oF1!0fJ|7PG`~g-w?DOF;f)Sq&M-W{0{qC~wYS`>s>l;_=3upVf z;`{X#-wc?fFUqwqDv^72nErjUDRbJ|U44OP8qiDw8*uxSOG)`$xG0-qWiVM8Y{~U~ z8NK2qZ~5bAcXcoO>;+$EFG$k@Cw$X_4>v9N8Ng1<4|ZB=?hZCjrR4{e2vV&+NVS3~ zYOLR{vHlUmG;=La6@0;SXKvrf0) zz_s_t%m0yA3V=4BPuhIo<+?**yhEW0XJMCdRuOkr@d?OMkgP07euFFQ_bN|}_x%07 zr@Dhnd&GkF2z#CBp<@k$+wZY?yCeAB5rU>e`NCyK3SY9YG}3ixq%U}K)J2-qMOtyd zy>P0Za<{Y$Z)wT62NRojownOLZOy=Y?CSQ!819MDq$m#G{^J`J)1K;*t-*_Fm>cIR zuk0WBD=YN9)H4p`>(YE(I4^Arr7aY~DiK|U#ws{3V$O?Tx0#a?=A;CHD|6D-0UV;v z0NNP<$9Y&2E?g5X1*P)7Xu2<2%mwJP8#a|drV^~VSI#H@Y-s7PG4)b+(B5MhxX17# zaQn7cMYLFb3GSXuzyHhhL*>OXfB$9vA8?gM%B40x{JFx;OZ~RAah`bNJZCU^T*189 zf_ZOzeO3AI)F;nkV*MT$6h|t()R*m)7Maqr^@`v6&WUr2>sT1lV}|r#bWEj`R7!2R zwvRVs&+)F@it|!G_s@u!Goppy{ypr<9Cp=o=RfRyOzOJiwb$%(KTg;_H)+)Q_@7n} z8L#$M|I!pEXo|Dp-tBdNp69gt>Je}CrX7qCAB<6eN!5yCgheqP0Lo%4%VOL(_#5w! zMc4j&$Va`@78^y2O%QkZJ`XR*d*8LhN8J-RkBIV!CW4HGJ`>u9reV0ckX}WzF0?JA zZ3y}oGX1De)x<*4#6l@(bXwv_m!OFnZa6YG9K|5T6G!?4rC8@g*P%XD1x`$X6U>t4 zBTVywm*DjP<~q6rZ-mhsVbx%xkHZJ4_xs@ZvI7Xn&q6U)Q0yDR4N%q~6tcg6(zw!3(&l?vi)DNotx@5mDyf!AW;)<+}>?{=<6i99y*#56az-bK z>-G1jQ1^mo<%bIORo%#oZk+pqIyY5(@~d_06zb2wQf=2#?MSeY2e#q|w(wDU#Xj+h z{pa9jO?7lnb&LU5?1 z{IHReulnX)9}>Af+z?{1A;bkiz=n_{4!FP%b>6EC|0-iD>q+@JZQ0WQuyBVl zyu+BsJv))L_wlcr*n>F4=nXLfE_&n_k433hJ?;I}hbYy@Kh;O`0{G{@$9MRj`Q1-F z=*;mXv*Ss2AoOiTve}AcJMJ0B=50^@tvxwIGY}{%oymrs$(A6=wG^{!DVjX4|D2vo z{_d`ydc@8<5ex1_2*GplVFdqSgu$)nj*prETjbI(0u0xQ^r@06Y>iHbhL!WJWKE((w8^|vk1b|0d6ZL4C3t(cm z^gi{uPvJ7|n1=DB&J0@0(WYy@c zcZI+G3V(O*!Om{_bEWXPQVLpfUMPhxFa!u+1i%zk@v^G9dE0m&^f)?Rvh!CL>*?Nq zP4|ZHi>a!lRMj`!jX0!Q#GD= zrzWN@v#=$}dP|a+{we>@51wrgm<7izNiG16rKpajgo9hCJmsD8l=nIKHt@SuhIjZ% zbrryrY1owM``{do$)m^QAAxdqLLNPVU`&xTruY)1m{KH7&B58BYs21+04a`1Vvk7@KoNahl6+k9CV+BDQn};}4*r`b?!4Um z^X%Ro)pt3n?*$fiKj_!{L1|jtx6{D3*s}Dr9DNfeZXuaeAOC21xcOZcyK zeR17+rTXLIg#H^R^x=DHamcsDA^+hn7B4ndNF8ob9c~|j#guzGm3xK&xa|{l+Xtq&q=>sT_jTu>{Y|g@ z_^|->lQz{@mTJ5R#6@HqJ7pWk0a$DN<67fQnt&DUKQEOz9cT$qpR5X9QlYDfZW#=2 z`{(GdE`et3FDH$KCs9A)v&O=+2&&k@S`myIyN(*e;`#^?9U(GsK_7Jx9!2k%MGnFu z1jP=*Vgw})!V(0X!L$>F-xa86!CV|1yXnI!I*ed>2wjd^e0D|&cSbpZx1zmK!o3K# z$I*0ObCN^;Pn$=!3rTrdw z)qAYH=rMw6?M2fFX0+*yHXLzcDKBIxPct1AyW8nJCY@&w7P^ABXax_hC6gDC$x8w$ znu0u=f`Yj6g76h$9q!q#SAaWhSjUaMk4qR(8zPaaeCUExMm)bW{ z%{V)wR*8R;-=7Xte>+zic~ly`1)_cWjJ*1c-T{}+uJCkK++xq%Q0CTB;rn-<4+N>N zmt5kNOEgc{2JM88w`}kXR@Y$l&(rIlX9d2%)@uvaYfHdUU9bJ-dTmX_IlHhrorpS} zx4BQUn)R6zL+^(Nt54pHe*BF7SKw-Sq3`=b|1Az$No&DO@TOq(7VY9ITB>txdFw7|CTJJm>G&ucdCGHK8r~Ree+g0F zx97Ay&S{5$v-r$W^vux>e9x|lbzc*!0?pamVx6|d`U0qql~ueCozVUu}Lsp3TN;qNYaKaFd7}04c?KF(zE+_sU%C~;BTpyw?_G4yStDOnBT3!i<@4W)3`d*$?0SaC~;D%{GghZNA{@cfT_@wtL`? zB2@jZKdxmkuEl_Q$5uVsih{4sNe#}SKG(NAMYlXFy2Ii9i6oNT4p9~>5#TTAJ5tj}8RILU+ z3IV^yU!rx5p8?C0CN@wPriQu{(y^aDed-Q1!3W*%o zC*vrJvdMrnq3JEs4L?ga{1&Wsnc-*45G*(RY&n9nhN82EaKkzb9XkwRxwXuQE<-a> z6dTcE1YJh73&8^;`T)U*5gkD=W<R0j9jz7 z+iULxqQARQ>Y^SLE`j>bqCwq}OW1Q?^% z%qW5k8zut{c+zSkX|;hdk{!0B1I?LHE~4cq;PI}A+(q$?4@Kl5f+-Q1LhxEdUL#m7 zCacjj8@t6GyT!06qd@FkAPxi{9f!njhs3@B4vVFS#U22Th@Fmz-2s${;Eb4@K~O1{Rf@gALaW5?RboE?7saxRC`Fw(u1>6} z;$vTB>cz@>aTv(bAXYXYxGA>1i5k7S#cti`c=U*+J?MB$iJ2)e49;6-N0-^b0KXM> zbOnM{c61ej96OSOf)%&eku3pc_Al9mT(W~B2VS<5Uq)GK?WDCR%N0AXD|P{3JFeS# zU$=u7-3>dB8+P!rY_oG|vxD<2+_v+$jZ)mRbG>Kh0~XqD=hBX{bl7=xpe)^XuH7h0 z54)Kt#h4u(L&tQ&j!qz$vZGT7p4ri72r}(yrae6S8|~>v1bOx}4?&4NDM5j#$Lz^5 z1Sjmt2?W*lw$=8qaW-8dN|(U7ZZ=5h1_|7bqZ0e064*FfEg{t?RJBIpQ8PDD1AD(* zk&r7W#k7Q(mcVQMh68g0MW*I0Bzb5?ow|jj4uz@?I?_QDRl3rNu0$cCXQc*brIhO? z!EQ&h^xbCZ$Dof~uk_mOFy|U{RTj~vq8Eu44YfwY_I~sDrbXL2nLH_P1W)`Q{2wSXW=~#ngMj0KV5=^felmtKVDLkKzW~eMmcs3heYD zoe1g`ztk&I;Zbf-WNS4jav)t-FxOF};7tW{6G5ATX+v;F!Q4S`Pr=+n(4k;D5R538 z5d;$oW&*);1@j!i3kCB6!7^WFnJ+v>YkZkC2-f;CYY}YoWws$G@DmmI!SPOO{Adk| zY3%ef=tSX%JN(HG6bHD=pX@?#Qb|stz_A+T7d6W7KznMV@)jHS)(Jz{nv^+gRNGES zx0U2JTFz7eokBrds{-jN6fCtVFl19;3|Q0Vz>v)d3Ik~&%2FB_R~q;R$Wj&szt*~Y;$JK@1RFFU zXbm=KMQ}6N;3k5pU^*2n1k1=$(JU2&RVum)!7kOWyHsgfAWy#Pzxk@=P%2ck0%ba* zqGu3PsWxj>sdm6j7ukO=stO=gt9ENus}4fCrDATO=AKTKuu~-go71Hdb|L6dF+C_t zW(ds;fjhZ6gsw(#Iz)In1TOSk2t9{VRE2y~74kh;ZCxm>3l(ymMcLC)8Ad9@7*O?n zF^pV9P!mRK5ZnwSHxXn-lB`IE>ny@9bX6o-g!mn)DGo;bT#2Js5Tqs0G!)O1 zoz+;F+A`2^+q0JipKJ z{F!SQV6zl@h823krh{inw`WQ?x_w!IPgy_!C@nL>{W8K~uta8fXl8gcxN!zzWCJmt z;Bu^tv#X4A;<})+tF29Vvo_&9u6H>L9SN}=2}!6gd2Aq;u4G)eUUY2II)-1z2tdcXI>x4saRN}!@as{E zR>riI5prGg*k#;h_;(or0-L*x6M%aR{~k)w&G5S!0ciEl~qpo|-gmJCKqx#Fn)O-bLHZ#rg^-Vb`~e$acM1$%X{ zh>-`zW#v>-uPkRS(_$o>T3{y8{eRN#KJ%syP*yQYc}S21#MRBtne zw;4*W-&=u^MgH&H~YD4_VWe>!K+ZySD{v*Y4~-h>1zZ_!c3Qh!4#=srl|<_ zgc5cDF*Ok^?>#o%GLHIdnx zDCS})*)45Nl(Z(gf=Ai9MUr)kAT%Ti8j^(DQnKGDdA`4b&9XhkYkNu{_iSTfSBmeh zxk*iG>XSa(CjC={x@SU(4kc}j0gcIm(bYeC-L3kNS zUq-^v>8eChg`UsTNpw0%^YqmV*!yqVX%Jz;Zcx9TL%*J;L6e1J`r>1#huo0Zc}NUv zbe>D>pG)8-uUzP`av`i&%nNbP3kd_SX+AWZ(IYi_=Ac-(q(?3xm zRiAPD-}FYNWscrI@Pcx zcuR<3ONgc4ma#uM%fo-<1zYUaH!hmp{}OH2675(}V7JCbwZ?wJ71$5b``ctcT#8UP z&kqMFhl66l!=*l0SRV{Kz}E+VTOa%@ckI|KW5LR?;8+gw-7Q7$Y@ZjYeui)6MQ-MO z!manUwwBI|J+`yIRE79fg=ppyoA~Xk|0z!UM0&G%w&>6;IzlcF3%Q0&E*eGjfFW}L zLF)qAipGGvW5wJ-<^>u-}XBM>T2fUbG!SzBkQBqj)uV(*^`K^~vxA!w#dGu3>jvfI%|g?$u$ zFQ=J{(&mP>-Tv>tBzGvnqtxZ$bg(E6|^N%(1X1P`qajxHlzroNBX{VY3#TC^19ZEo1IXu&XIe z)G1B0=6X6Ef3oS+!zcN(t0~c?CAx6RvNBy-HV3~5cK5CSY5#1}WqiA3d>GY|qe{q8 zeaY=-Zd_aKuImS))t`ui^YjMiS%XfD8?^-+wHI>N2D>*Kwcp;T{S|-)ox}#6kGW2a zpJcwx4m)!=TK%px;392^C161rCS?mqB z=a%@I9qNVh8>R45WF=Ehl>y!n3LjZ1f;)pI+o zu<}@8 zpJ<66T=` zhtB+m&I`a!9dh9xa)Hp~!f!&bOUBjoIi+Qw;O-A8%%N0&G4d|HQ4k_cYdZjOtIgczaPOF5B?bsm?G7apXv#r)SF-G z4dJd2|E>>&;|jy$3e9SF{H?g%#n?7RU78OC7!CzkbGO0q2X6DejQb!)T|{?DeY&LY zf)>&luMcLt{>yDUclW7_=x!~;ZY?->>wqC|z)jP)niSpXR+s*^xqfy7 zid4d)xjKF>OkQ1?vuB=Y&pahptg~A*92GJg^*(nI{X1l>XHuYjtojk$tR2y;{WeJ2 zAE@XLj0TVyq|6M`OqKhu9}8Xmha+NVKS3gy{zwPdYd`ky&tiY$`W=7&<{ndzRo7$L z;S$&nD{VTZEe7TKV{Q5v!L&A=MzDfMSMXq#Odicd(8HrW2zq(67r}iVy^o-eNBa=e z@o6234Xx+XdISx8+JK;uPa6?j&~Y1=@f#;`t&h^XZrLRf*E}}gW#n;eTiVH5nXBo*OG2T(-AB;qRSENHKuz} zrQSYcx(`8tF)cvQVoX~Qv>MY^1lNt}bp$tz=?w%ojp z5V&*Mgvll_%R0iWLojGc22J5<7&0Y82u4iF2!b(FGKOHnluRI)G9^<89-ETK2v$(C zg2L5iP?CWllafpXgI09V3RbrdS|7QzbobwXwxf(=5tK?oPRQAjr; zcx=r)MwR+cteGbWrmdN21W&D*rwE=|GtUq_w`QIrn6YMN5WKKvULbgB&AdeL%9?qF z;I%dL8o?49;Sw7uS01yaRGN)24MDn%Fde~i8{u*UB_dja24yJ~(NY9uB3g#vn1~)j za9l)>BWRWyG)otNCrFFbpansz)Swl?bt$hNT9>2u7p^BMADPNx!q^;me2*#Yr z7=m$UGLB%%nM@&g>`Wdbc;ZZ+Ab93Xo*{VdOr9f1bs?!Pa7z!nki!U$xR4_VT3kpA zf_xdtm%(N1mXX~E_Q=Q{1bbyL@+EP!w5!X zWCX!hSF+U=Zu&M?vJJs@SF#q=@7TyZ5=5M;&1i?NxvJbsZXStIs zcetf1-N{M>2i(a41P9&8K?H}~$sq*iJjgi@xX=q8_U+5L-G;q_943w-18y#5OnyE4g{wa%xMK2V5vgER3JE`V9p>ot6URgDVSRbZY!AE2<|GFy9nA9Ogn=63g$k7J_XZ<;DLg9fM8g`3?mpz%?MgzNehD3SkgKN z{+%tp?O)%B^<+ElOzOQesrMoGb;QDyUf7hLCghaehm!*1lW2;vY5}Phm~mOy6x9Mz zwE({SH``b@+t`8k!DbuxW*Z+4*tMJy`JWMma`jg%%t+p!k!VJPWZ{Km75leE3#1ux z@Emeb0vL9X4?AeuaM?@~4xSSz#iWCL5~Y}S@SH{|o;tkq)ZsI3q3qt|c$(#ST5^>P zEEIY!DD;Fe&IjX64#t^tEwt=v55{>Oj8k&JW;qmRatNh36z6#er6`RvDMcwt<2*}I zin2J9GL)h$&a(`qXp9py&JCT-ZpX&>cQ(d<$Zf|ge77Pj zFX1zgVoSmoTN1tlP?+#RAxd!|;fn)vDcEB_kSrKT){Iol9)yA9z=34Vurjmo?m+U# zTsWeH9^Zw7c4i0HU1f< z3o;O_4LhQ>HmpqRUq7A5XuDHxo7+(G^NHt@ICWjn5Z`r(4?8Yo=m|6CnsVOge0inV z*DX$6ZWo*hX!)jdP9ZY-L+-tYL4gCWYRQkv)@&PvV!Uw{{heu0k=t zYj|`Gg7rMQ9>E44-GE>dk8VP+nMXGx$mP*o1Y3A?3xaJtx(&fD9^HjtH;?W{u!l$Y zAlS#F`w$%9(E|t$@#rB0M|kuIf+8L*LQu}5#G_3JT6nYt!5EK@A;{K|XX_|Hh2~lv`C0_)bmZ$0tk;pRN3cOh zz5&5T9r;ECn{?!x5Ny_wZ$^-#BhNvQt0T`vkf$ThL$F4dtU)p1*ZK51A4bgH;L{rj z3iU`Kp1D?^mZE6rvj*fW3VwcI%sfC5#F>O<61bKuLbDLm5>ks|jEhW25sLmTHX+3b zN=!%zf>IMwilEGdlp)w;N;aXG+I&-zk6^bc*^OY2DcOUd(3BRU7~@6(X+&|k%PCoo zVruivNj?gHJz_39V(tyzM^;#n6(}CI%7RoOXtyBk2%cLBpIgD6j>m-b7@B5u+?p9j z5wjE4%mji-Yi1I`lr=Mj;E^@+2tkuAX+klz&95otvcvz1~}iNa2+ z#H0$rMKQUEpju3-5gf56M^La{kv%Cwa8p8VqPV(Y2^~hkdLt4#f?!laM-hxk=oo@= z2^~i;A)ylpCM9$d!IXqfA;?@vGEwy2awobR#pbPaHdu+G=9ajSB`D%;Oh(2~@Y-%y zvKs}jUG-wFq6n~U-lA>ZFjOhcmq|mxOe=kvl?bwZnQR1Aex%Be0nh85{$wYLUCQ?- z`3Rl|8azjlJXeE5uLf&+jj_+l`ry!d1dYL=jR?-D9M7Pk$umM|28u^n6+%}bSQ|pu zB4`UGZD`_~#xU9#CIl_AOC#x0G=s$RD9Q6E8P|5kZs~LkoklazJdL4G5nPQWS5aU{ zO&qO3aUivEv=+hMc)B+phK}rur~443CeTzADY7(yE=ACjNPAE})9hp>d#>vbyXk9_ znY9QuBr_WjY)oc0BG{VDY@LI>)&E_!e&4Zp^?Te3y`U9(AA|Dign5qE3G>siZ)C6M ze|tUiwYY}%sFg=l-|o5>Z_2(mU0}o)n1!In`DI3QnQ;L9x@#H3S~Q~W6^4I>F#;)? z7>6dt8T9;aVQgC%34j}n;SI(Vf$a^H;wEEw6Q$^89J(22(6N7nF&<$kH`Xt^=OfI* z5yk~U;0O~6V95g6k_8^1YgM6@S)r9B_a(_LqtME&(8?RY0jmWEtZX=N(f>6&V)|9Q zdeos&zG9TGdFf?grJiD?9)tsC!UJ<-8L=sbYoRg3L6s}1Lfu)4 zh>op3Dekm|Q)07k%{Za^IG2QW}b%j%l)XUwh z3f`<*%#}f_{6F8nP#Kk^K8-7s;uT6M81Fip9ot&z!ND%~Q#xLMDo#=tv3069>r{W| z7Q({4uy^l;{RrMVE=T?Ca#V&E_m-i*U$=z=2?i#Y~HN-G;2P* zT++O08Y;1tsOS=vEq6A57Zw@IfBY%go?Y*<`E(h|yL>)fj-W@6_UOT-X9`HBz>2$W z*<~CxmmNie#@CBUz1Rxe@(p6rfS^%K8WH4sG5K?o%&-gHUS^U^ zm`)&=45pI^o&?h;2&RMSG=itW^eKX8!Soq|=fU(jf|+1CgWyFleSu)Limsl!F4zOK zRz=q$s0bw$p;jQwDkquANw(+ihnDmYQ@*JfOHn_b>XiZY%J8K&))`+4b-rVvL21~a zGy|n&qtd5Q8HC`6M&+*@oY*%tf7g`58|p&rFeQhnX3XnalV8@FT|GGaOJ)2AmGQrG zkDgV(t+wi2UOD^AaY~NQ)n7b1>ft}5>Yn{&%1S(C<;%TW{_``(HJhA=XMf3%h%zK{ z?ouru?y>AzmH4K*u*~uF$ngvW&x=A&MWLr=81agAKdh_h{`Z^ejeVKm{W2k#iwWe1 zq@Vycc1yWoKh`feUv<1Yr1-#3pcrp%$aXnJ-FJ4{K3DO^UUDd|LzWk$2i;HyTNxvb1w zGjuh(j6F_tkCP4PnO)?>6gk-exUFJttL(UvkjINm)d48wJpK=CgMdC@ebU* z!KRps7fm4;Px*X2KQ7y`^qYXw))n)N%q1 z3Ypq&nc8vOdf7wsDn;j2ikM6BvGv1OrB$YHt2chR(c8<7e&enL>7xTPYpjHCtDkto ze9*Ro7ZmxhTzEU!uqL6BUP{0Rpi-ZB38X7|#!)wkQ2bM%<=XW)Qba*ep;3d3m2J;TdA zVXVwmMbcHpw_FzQwdb_RiRQP}1;Aw^`^!c$FqnRWk$uA)SjGNyd^Fzd9d**oIGPz} z1L_d6DI0(L3rp^n`xM3H=msho01TI;jumj>r%NW{48G>(0K$;o;LCT;5Ti zi3?E+E=1Y>_ZzpLzpwrCQFdYVQ4#e~aonr#EPPTQ^%a1ID3gXL3jnQA3tFRWIdERO z%PRh#@v~c2Y!O*(@g>-@Qj5<@EtbJ*yki#O$1FbQlB^i`$$I$n-`<&hwV3_#2pX(J4OVX4I~;pFTCGH_2rk%2F4%a1 z6kBX1TWldbwG}?Kg`*tqa1iZqaN{mucA@8-nR93gx(m+C1q4;jOcjDwXQmZFktngnK3Int5xeD(gXm=I1BY5B{e1PDgtMDO$epg{Xg3WSfvmCDWs2g+C4ZpbW>1e-nRW(2t&G#9}E59R<`XuXH9-UIGjr-!f;!H9=&1VOW> zu-VgvYYSix&`nR#O%!z3<|%4JaNkpOAHf4p(E|h}KEe_oxE<*VVLA%jTdoi;N06Zq zW+2E?2(u83M~TK!(A`v&XbM4YG|7#&<>rfW_~P`)}VURidOEZ6QGyf;<7bm{?>Z{(li`CDd@+7nJBoTNB zHzx5LlPDPOp_R5zDToIN7r}If#Rg(~imhWLC6T{Q=b?AT0vTTPX{z0@8}$ zx`13qa6>?DAh;Cwv2l_XH!hf|MSHBFSt|1!ZdN3CU6e3Obb<}1?-8Kz2L3v1s{RN zpq-Y{J1yS;WoxBnbR~jRtLRiKm}1l_dK5v8b!3foGFV2gO?0je%(BJCZ3~L^-C`5D z#Rg{CB$90s!I5HeM6w(NM@6!u2%b6wJ#~PC5?4wcE2W+wJa^j5Y1+#R6ob#aoSq>_ z^_Hc2`+^ipy=6-gZ1H#6;_t;(rLqTk$X`C>?+@US(*2PVj-d8L>HY-4d6ma`RUmhg z*=5wLr1dIq054V2mk8E{NdF(=-aD+R?0fkAQbpPcIlzGw0)!HJ3!x)PNazWpj*iYa zqoY%t8K-_{#;J-TRX`MMAP6X6K`bbuf+&ho1uKf8Vnwl|i1nVs`|M=x`+M*6+~?ju z-aqosCu{F>&aP{(z4qGqlc2=27>{Q$5)cOGd5p(%43x&XmBxvY_DDaaD#@cNNrH~y z)8Jf^`?;h*0G6vgm#c$N|D+|Fl0Rxno{K=_YM=S@@3W>eO?f~{4M^ctLR(_VEwS#v zbFDp@YESk?X-ytm9yWPxHJur4Iu!ODiYTP;)z+-~)lK3&o@;}pM}sAFJbBNLe$P(? zZK*0bx+*yl)gwALy`07GTp^<-^KF=pq}?(J#Pj81^k!-K792x{7Gy0@J&nj zb4vK|P0RTU%K7k3ck@NN`JUiC4)G@+!a|<4iP>#pYtSz{#V(!L-1HKuU9fZ5R2`dOz=)71YoJ$X{lTUz$$srDtQ^7A(P;fd_ybhH;0D~%rL6tMGL)5CL)v9Nr;fj9BdG+k` z>UjVRsOJx;7Xa`|{mm=&_W+F1H+Uv%Y#9}kI&&svR!?Uv6)k3tEoSb(xX@Pe%8 zU?A-@n$~IbCjj+UKh|3nz!mQ)?!TwF$$d7`{I;GXCB*3EY|l5vuC^PaXSsuHhdsG4xiM z*{w2jMLVwJAb6FT6aycvGMfWHftj$t%o76<1!hVNd{lrXDKfJw!k$uOmQ-Xm4JC1V z`DxMY^@l%VzN*<*+3X7~OBVuF7qoVx>Y_!lF^^h4V&=$Y7E_j4e2=UbUk(N}uL*FN z!Ti%5GPOHoJ{n=>Tk8KgwCI!Ze~PVz#nzFijM#?-7G)hpGZ>$jbvBmkY}~+%Q)bI5 zvvmel(PGPL!N5`uYbggxG;>(Z7#QM^LmYTUIhRZ3av?a$W1Zx|JZ|&I+dL@Isy1!a z-Wfjgkn{Gz^F}k7%4@ZzTCJg(c)6W$xgCrcv&)6M%f%NM`H#6!$6P!C7<8cqG4LQj z@E`!*wzpeK?v{$cP@A7y4CrBNc!GSszWVj~0N@_>q!) zq!fTCFRzruDV;gyE|hxDiha+5Yp?pqxcbO=r1VGLd{bEb@ZV1eI@el5_gX`E8PzJ5?#PVMG3p>5b%;i-l}^&>AKd8=uaV4^1m{X1co;tEVYnJ-Di6bF zJPe-?z^w@Rt%xb;mFY~cM*3cji~`_Vr0+Eh3`TkmMur24);Mu%Tr?W4={(xv#BExT z=+NgrW5%ZO(mfvHdpslrjdXQ^Ixlyg-~9g+HPA0L&}_gl7Gwj{rv@5{KlF5d{!Dl9 zlm8X^tBwx+)d{0-m3qu6^?-r2Dm0J@rxa{#}^>}-r7x}nn`{b&T zNwb&^yFHQGp6L3Y!@l92tsAWZKD)Cp=l-=>%-p=x)@G@#9T;z4MH#({GDjhjZ#4h> z_jyOwr@D0eSQ_E5G{PIjGo{l$i6ozBH^@xxS}*$N&cRQaTwgl5zjV@sb*DjAgkx5O z8w!d2o4=*J-R0S*jFz?2kl2Y`ZrEo@_F2Nq;kH{jZ@2P8EtSbEjLg#B*-nFsFtP#z z9SX8T8-#J@n?3g*D!HFAB~eG2*HLV=)HSI)U(`O)W&EG}mV*118ne;BPP>%YGp?U8 zUsEDADv=T(;`1Z9(IYv5!Z+WY7q_saYx;O5SxKy{BnPBCmy(wY{g+pc|5Fm0P@;|6 zCd;W=9yru6{!fp-v`7DAWYPNk^5|g$K6f_r;7l)NrWaJ4`U6${TE(eVFz?4YQ_*a5 zx-mS7;yj6R0-HOpql{ihu~9wJnKj6a8f0v+G}?||F|sp51VO>$9_+#i5uxlgk^1OxHS@~H@% z_aQf|oEkQp(eS!#Q7rPA_?tlkqZh`(@@XfnTR~PVnqyjH$Qcxwo`Ct2DS1Kl4uf zY&0u*`-@*)c=LWXqlexzAnqC1qJ2{uJhr1A+ri5nPB~Jiusgf+T&O%3=zUV;N)=)5 zD5(lERlx_Be)J_0eTlZHyU%(#rpBD1>iF83e2pbpT;+6sSR|A?UR!Gu#+^Wq~s|KT$fVUF|9W>noPyc+U$-ccgJ#pd2=Y98j2U77DGR! zIFTq$q|nNN2DM3KZ4$ivB3@W>A-=C(wz~lcUm;XP`EtlT~(pt4YxuW_x8l!Cg9krGX&MDxLbG8j({VqJA}61h2vkGhKc)ZeaK?=POi%n}`xO$WsThj98S zn|`fx?c#pjgEu#B|L)oAYzb>3`no>c7)91Ik~jxlO9MM%SYy z^2{ys%=ti~!P2I|5=yjEMy-?ukVqFuv{dlds&u4I<03upn`WyE%wBdX4N%U_lj3Lnm4bm}f0(=IHWWV)Vry7sOP zdOCPL$@KLk&718$;jHkO{bCNI8C_v}USY%K&j8zV00S@B;umaq>}ZrN9>u^*w)iCm z3Q5mGQj_VE7tv1yH^M$=d|F1LI3rOapg}B^8ZDJt0}Y~3#wwKAq0gk>aE;7#jf?}p zC7IDBnKjZe4xSTr`_GO4oN0MmEPb|EYStlFf77vRR$L$drz|qAEb=q7R2ux*Z0qfB zUwzJu2FolWmRU>&y*1q;A{_(umJ#)qP~x3s#5*n6v!v?1n+a=q;Fmz^*)ttv_H#Fqb&B!R8^NngQ z6W}+O>zB(-1_Ra#E@uT72KdeACg*d1022A!fAYD-&_I~Nw@cwi0Ffp9sZ033U?7E# zjki&gXw~RNsrw)Q&JCmYyHoV^Q}nEnK1sjR21D@%Y++qu7*=8^1qEAb7+PwmX%%#m z|2LOTnbsS#K_36(7OMTmru#98eq+=A{~P!p_f(l)#w7kX7grHgEO_n2|G3HO42w8} zNnB+SS22L!bwz*eY75WR78=!`1|=3mi3NPhc?;q^20F;V4l)dUekU2;Ny-4|BEz~! zSjgREa5t&3Q_@e#u%a@o;B55&`vNUG(>h^roe&US~e?fngTXW z;>NumSnJUlH1}J_c@t^TL08tGtEFAlg2)F|gYW6bq>ALJq8PA-cw{cBD>Y z%adox+-KTAJBCri2d`KDHjgo;I3x+UN(= zuvTpEJABOIzh6xJ-xctXz0r_88;nVfjz*1+5EO~@i$vxqb~>GVg|BIaFWkp|7iRP> zjE$;@me>`++Z6$WyYGw8-xpyHMvM9gllllN1mvvRYM0v8UocI-EW)%bf)8w4EfGXZ zgvR;1yKv!0b}!d{!Ke$jbSK`@G|X6Q}u%WAR3%gnw(Q|P!-MNxGb&tUj8Mcs6Mf#o>)7e;-x`P zB(DdHZl5CKrC`DAtD*%}(QaT>aYi9Hqi{nF5cvo(UsWkGtrXbc_%Q*}!{yQLD)&I@J2LYWr=-3QE zoxj3%)1sM4mYGR*Xd#IzNrx7FrEB_1mxDUNX0H1i>gV9EnF_5Ab*&B!Knqy?jGCU< zph;gdI(n8!oP`bI%SGbl7${bWi&e?kscmtI7JNSMY)re+oUfVQ&_xhk1U&ds;6M~O zXby{P>q~sLN-tyljfN~lLl&CqIzL~(H*xdLubJX(piCPmF0$~_;I-}VuWi#bsxnR9 za|Q3Y(7&gE=TX25MT-MkqL?Qv=J_MA`u7aw><{(kGZx1h8)1!&AJ|jgVJp~S>jiZ4 z3$}s_7+A{@tmQz7K8~Od18+EjHyGH;6>R0gBwai~7Y~A3J3*}-1ZR@DXOcB*5}g&F z?cA|Ebw1Phhn={?PVkP8r%v3b7-&lNXiD}+gZejN7Jt>%r;VrGtn0m5R|3X`N?q?t z3{>fQS82h=?XxD!qtfRyttOT9NG0W{k3H%?wR!8z&EvVYhWNH(zJdM8KK;od=mqGs zSCXBtB>N!nRF-ga!-XB=xn49$xM(sT6$DCUG&qjX7$MMl;m%icg;+oG7~(J61YVY-!~HQXpJseqe}s&z#3i88eLxi z4(bLU)D1WPWa^Q&>M5yol4H7|$8;kBxWx6n#1*41Kuera_?}US(d0pcYYL}p3K0Mm z%9sje0&;8-x9h~URoZ{Y^z1Pc?wE-t8UPKdZCtBu!caFVHO`nxedqQa^O+AKLLO+t z3`N^ti?7U{@xL;0@gH*u19q3<*wErwIa0m8KVD&D8?|yglhgVUr}d|x!3`NT4C%7E zA2s2AlnrRmFQW8cL|Gvb`UTRY25C}j0Itc5uE}gstDz;X$y~0)~zy0u6 zmP6KEk53A=4E_1+)-B_q(q)A{xi6liLIryBdCCUb;JEAN@P5sW=+- zCnxqNe~1eIVcd%q58NO9%ouJk_Aos!`AMg!pq~>d+ zB=9f2E4REWw*|NG-i@DmH~up;=F^!TPvRa=vPY^B4F-~g14$wPZYK$EV_-N*IE;bQ zD)ZASa;P|VPwJArAunQ=UD|$Z;V(?Jj&STpuu1+c$NsGrSp4N!!{x8|g=rfFp$P@r z&Emndk7akZ?fk`*{-=R6w}G<=Xd7d0iZQo2;JdfFf3?*eyBV$5eO|BoVz^~e>hX7} z$6AbdDoZ^-2iffNnz7FdDzLY`e!1db2vcHG3?hb`I)*D6{`E zeKMUBlFo?*B~-=_EyH$zuc#$g)C$y@*nh@{tXUNNUuH&c#MxBUhXC-<>iJl#r`{=^k6=Aw7f{XNew60)8*XsqY z*NXA=bgk*H%)8#SB5zu0c2|E}be-ZXwHp8D2)#~@@J4S?)z5vc zv-`7O8Lyyj3r@EMob~(cBz<-XV8u2fa2gS4B5~767TY^7whsjo_wD2E+kXl`lcRl; zV;}&fP9ddEDgaVFLsC6epwh~{WaVDd0VwhbDe{35hkQZ~VPLJl-&%hqNU}B9YiqC! z_yF9AalI204vwPijd$4_9|ne#MzvR?cE&9J(s)zr&Ft|aOO2GLMru6DnrBok{f}PZ z_&+;B!*_%#(JWqCap=@}>&w3~b+yaWa+fCuyit|Rv`WTC)9p{)hp$auc;{E93oO;= zF4cEM)k=d(8`nyk0Hke=168HXOaw^BvL9|synh=XrHq3{jHjVCNsB%=PJC|sEdrJ1 zCLirGne!XdB%UV(Jl6&W37WOL>*BhW@qf-bQfIYJqsPBpDRlb&-uORn;;1)qnivju z7R|DKx5eUj=D~&flM3~xgO|P-PFxIkMa{Ll`xnKmtUrDqe=u*#bKbA0i&rfAZn{#w z{CA4Zq}qvF?c@yF!jmBKNe~}Y+gOm}Sdb?GuY#Oj1$hJTI>_lY2Hpg@z6tUJ5*fi{ zMlj4YGuS;dSPUexg59$)kR9xk9UKBAYJCi>L!=C7{w*+rcDxpdx@KTVG z7;+@W2K8*koFt<3(Uad9+rkwe(G{ObpbrlEhz2o`;_H;+3lGy&`#M!)U`!$!lW6il zMnPt?_^OWSRm=hNl%D=6Z3Gm0B;R3RxuX$gv=1(1 z0zEcys3wjJ>Vh=Lai?U zM|{^u*iq8AeCb>M6i^>)l3mwe7M03mX=U;hr1gvxP0)L=>+wQnfwe_txkY6U7K1C) z{wvgxNR)nDh1#@2ZG*syrL&Wke7ofjW+8bxVdCio;=@zfUm5)rS3rxVCR(H>QYcy! z4b~)Dtic?F`jZ^`lU$Kf{nley_pG5Ct`0j^|H%|yssWX10PnGU7EV43 z=b^qrOEg4M4cJa(zKqJ3*`v7@otS6bCQkT~C$LLBy8XPn{eqDQomsb^vfD2ifct*7 z_x+&j)=NLrmwuYPjnSQx7Gyey{l%#JjmA!m#_%{uld)412AV@8%^{JfMCc^VA&TaZ zBmmmOCbx&p1fViDv@%wP%qDb_%GkKd*be|W9`AcRJ{YJn$K&IV$4>@eFj+E~44cS} zWXTN-JWcj`nyk5mINf7POJkezFJ{SFZ{<;s1+u!t6_6ItE;ie^!YQaEnAk~Y3!9Z#d1NQ={dl(oEq((8YSVAq9z^7zN zs7wqLL~;uvosl+dqxVnzkKI%MGNE_DHW-F&Fw}Sm(4fRnR${0*8$p9oLusj@#-E1< z9b|Zi)`CidPEy`U#v^5n23@4Ai-dV}lasnhjiU`Mahe}^T3ePhs1r`A6DrXJNQ1pX z|b z<8RAdTK{4utOrii1FgBbNn#i_`F;Cezm=Z^eNRfnoZuxA+x+Dtj+wJ8Hn;C$h-(!GAT~8#g zCt9IFZ0$kkxWVm>e-re8@HmR|ILZOoyfUS{OsNo@e9e+_v!spy9FSQZknxb)6#ck^ zGMj@k0RW9ML8HtCfF>EMNk(D7u1V$$z!90z5g7qMkIbeA%d}S}?v;fBiPJLI(=u-W zcFUc2%RSK;O21fx+_OO*06?=`)+~=hAl=WauCzk=FXKU6M@Z@jc%0`t<$IkHgSPX4 zq8?COaHxEY|M?jIOVI6Kh@)PJHQ}u3OwUQe&q<9QP^ChfS#)hzsN5=45h&9W!@uf(a^}Ln%yFh0lBGP>4rdt$=CO{$S%-n69L`Y;T;y;rVxWl2EyB(#ZsT&dVW5=H zDdoeb?6l+Tw1c2uZQZXHg8q9p+4^j<2K?~N)ccP-uKznesdJ_caefD%eV?Pc&w&nb zW4y^@JdJl7vExO)#nfm2GWzx&J`6Kk_3)(6Z~8)9uXV4yiRx;ge^6p@(D zqe1<9gF06Sy{q~4zM_26Y7z5lw++eLhMH4%G}uBCTSyKXUTLt~%44@S`0J*~LjCS>&FF;+_XQjCMOP@pj-> zDD@$MZee8C^F@qem@Bo&mD+%L;Gm3k5F4_O$xM&QNHE-YoF}s z{B!W(Vy5Xnc2hpaqV)~?m(hmSCirC?fvOJeX`zf2pY9@ezgWyH^e!jpUrwNqQvjnA zBns*h9no~gxxGj(n{1H6D9eZS{15AefG&7gZ}MTi835$PI_1TBfIfLVR&qQx0(HS2 z$MZ!i8oX1O0k%K+hyLV$QCBRGPq=8}I4^}M*j^X1*9D%$xE@bk*M>4sWu&lVe|(?9 zOf0o}`n7tN;04avnVz%LcnQ!FgLbBa7-(~3w>iQc@RyFJFCAf$D>24bVmN5cPv?;p ztDhBX3BIa1)}%R>i@xgKoU%vDXBLisRk6OjSpPHhLS?=>>zdX)AOGi$o&6m<&Aoz= z|A{}4bK`jmqXTD9{0zzmo!X&6B_*h&{D9lS0A)Ww`Gfgmy^ZC18yB<~r<0W1vdV28 z!N_{dmURpRsT@`+2TB~_u#RBhF^BaS1M9i0^<0=Fl^2l8Q-FQoHXf^u2PI1QtP(y1 z=hdd?wc6dZ@611Xt1@50G=Kq7(12(bvh1TBYj?UyX6R1H(6t5|r8&C#Il5$?uB`Kc z`Uzv(%hff@)#YtE%(Iw1*3XpTX5FC8y2`KHLw-wnF~(?~mAXNd7^u<>s=~ll-3eQD zt-(I{R^9lmx-$USuB*RYm&AZ@yRJU~`*kPm*R=*a{6@_ zo>d2Sz5Ee{{t*RfWyY2Ug}-1diY*HB76l*K8fkD^VRc%&p-Y1Sh3SBT3nU7a)`d!E zw4qB&Y*kupRoWwfG(j`ENxw~+@HUAAO?rt+e~F5PMCi0>DtVep4K&a!RaBNL5rBM^ zdA^E+6izxxzRERU6#&3WmHA3cVx`J;B_>g!;#R0!kt$5*v0G)fTV<+lsxmN3?%%dYyDUG2bm#x#*dn)Zqh zI&Fufg8nsXrlvOgFQ3aAVwW=G{;hEFt?)^}^l~d)c`N(_1pdBMePB-3=B3P(_rOj1 zz-=^7DKm>b(IKAb2tXLQOB2XT6X0>e1IE+= zV^^U19TpLXMFQj_dGMObz&zD3l^MiR<0Dh!laMiFv9Zai%R0uXj5X|70_#`;y|b>d zhIJ+ooe30h6yKKUur1LIoy4Xyt4U)ubewlw9Bfd{{{y#z3>usaffXB+vlFRP26ToBg`}p!pu%ma%GZK|4@auR<;X+8oF>42(FCBN#X$B#sE- z4HKcIg9NETFfQk&AmOGUZ_s_Jf`nBVXqJ-A zQh4C&sFXa4fn!qg7zSFUqE=}DS_RS7I}l9_M01dv`l~A6K$nI?%b5OriRExfyMm&@ zd)624Szm%`t&f~uANdiorJe1Xf2{21W6PNSTq`oJ6?ve}NrSEKu3Ozdmf7en+UTuW+x*s4bVJ-8mB#ewlktHkwf7WX=x=II{`ar(e>z#yJ6S)YcUqC` zku)K7&G#s}d0RST zH@-R1=p?Hr&@emXwcoZZ+a8T`WP#Z0EyWyJ$pXOg0 zFW0^1UVF_o22?t;D-q6DB0^F7puq-(*9L_QZ6=nctS+nVc|KmQ!z^-`rEy=NMVl;$ zChhU@^>WjB8Ey_4O!Zx{a=2opv9Z#i##&fot?|^N!Cvd=z19m*>F&4a|JB>XFN3MR zgBGHL+K93=7_d+bSj>%M0z6?Q9hF}(W=k1;@+dVa{cMmJCJY8*{#XM%Gp65>OnOqw5#zyzX zeukEM@}_SSQX0=@F~wRMYf>63K&wKeL9yuBa<_`R8{0%{Q<=7@EPzhcp)%@FnPI@X zL&XK4OJ&-HB{{7!I;}E8&SdoS2UXlbl|49`TC0w(RmTBEtyY~}tJZi5(n-#%XP(C{ zDZixNLz~+7YwR1e%z%3Cfck5Y%PaM~S8B~g8nnb)b--J7FpxN(9CSWe6V#ZN7)y>A zOV;>@I+fMk6fY0TW(w_2NZ_52XtV+KJ^63Mi;t7DnOZ9}Aq!2os6J_MP(&OQQD9sz zh#(3gC}fGBz%hIINA8c~x%P{^`bBYQDcS5BwrE~<+W0@+adzEWPtGZmrf>H&ZOvx% z{Tq734XtYw4N_P{3JY2Q?}eG%3*&(CcR0*s7y}Q(Odew3ahS1oL)65u+L{ROr-4-2pvYA$T*8HQh=6`{* z$|XL^B{YC2TYfUN`+hdF1##V#b={SXoP_DLOGV*JMUlv{cpOA86)6E&DI!;jtT4b` zDdGdrBev=hQ(*L85-3`N1?gTE=)Ek^7f9@tSnZX-B%_jqQ3;H_@=_vtDS=AKJBi;r zNdU+rFDN!IC?0_Ppve3nxCO8}D0FpDIFcaU7boQOy&f;JTC;>&GmU>8{n@+BzTIV} z^#)LVoWF$=0|SqY;g1q~~-@&tVth zc^kW>lzS zDHe@dxviAU!$ZCh+)ecbrtaS)o>mUPQ z)WL1k0fxS6bQCr^!aRyZf+CSCINEqZBt0RD1A|_LuWN-b^kwe!_v-WyL`|Ij{3QXg zO9Eir-_(E)Qv+rJiCl?St^_7|7bbic<^e{weG!6v5w56e=uG>il749<(A~?G!R5** zWZIx5TI1YW*PCMn5ixn2|$hg8kFrMdIWaiQgk@_=2jkr8y>&@yv#eLWhkK zK$FRw7@9c|f_qlvJu4?5agFPAjT?x%?XkFjJX3#29M5bhJai~L9@I#C1h+lH1Jp=+ z#JBAce*rKY5jq^9iJth^g|^m?V;_ze@A}ZG>qCD*lPvuaWucK}p|cR!q2m-WcKPod zW?_Fcfps*21Xhan1f%u@6JVtnN>C0Z#Gzw^WnWzRt0gHum+7_D5ma@A3v%;+Rng74 zn)p%fMEaj$SBqg+r){l*#d@9fOocymb$sX=h|aFipipF3D6$0-&%~@}SPY{ciD?gZ zvw5n@B30!COjl_t$23(SFpyQK3@cQ&Kw`Vva=Y49(*n)PI}e!W<}%I;88%dgjSn!O zzqcjd+rj`*Yxv|EEF9@gA$3#e1G@LUVB%h|D{zm@AL`w)N@gTNMp zNys~K&+Va-k>~O}X7)R2Yje_8b1gOvj&p2|YwyXX!EQcpH{SuRY-rHtW!dHhkL|w^ zTfV_g`VUDghp>bFPlH)cgRMY0AB?s+h(${pqZ+lkxbbf#A8pL98gB}B zbSK`?bpTBvUyq%y=MHK zjf?n=%ZK3f(_onYVAvGYEoq7Dm`T|&vyt9TgDWw?S7K&hn}g9=v0osiiI(U{l5`|Z zM>B@!+iibEE;yIZEOAa5O*@6TD2*8Tju^rD=z!nU16rS@>6fFnWWB$g&y?a0k?{`9 z%GK%lSEr};z|Gu$e_sEILGeoFvqr@BBiPjdqhk9}4D?FednIBtKF~?dOL*rof72SZ zQ;k}4cLgo6Q|+`<3r@r?m3|Z0yOJs23^FN${0Wu(ymenMxav1y74uJum3NAj#v|?b z=4HQZRQRl7K4%}>d>?ig?7qk&igR2R=s~EVJz`KTl>j}K;7#K|84PxL% z0`CR}?j%^=Nw7ncH~o|c3A_ghF2H#$FHw-EjrL7TY)LfRl1QOV2pXJ7v^bFngZOnO zT6HE0Py?qWPA6KO#=t_NGwURSb_xt>`AiiNfH8yz9ic|4BSkzy{QGp5l4(u zI;Rvcg_|AWlN}L;#t#~-h!Cxa5F^kuZ|lAFob&>w+Ur=%Zv^fJv_yutV1a~MB7bR# zT%`%B&2{s#wi6nM}!we3Xwc0!}k(cm;CI!(Eu%TQ==juM^2zynJDfQm+rY_vqF zjZdkKFVZw<@X&TQ{oA9l)zWA=$8R}DbA1~PcJq99^ZbyaNQ3>n@cq090FLtgj%u%f zqa{v>ola>Z4bmVb&@Dx~CZ|E2#HmiAIVVShXA-w(+7&kqm}OuOeN-4}iZr6V6SK4) zi!@j*wO=iDKxZjwKzk=1l-VOs6&ldqiH$N>06Jx^oiY&sv}fUInGXQ3Wp1x!?#OkJ z&f|?N@QqC4u}y#PHp}Ay*c=_XIeHQrbm*s4M$fK{o&!L0w5&N=4#1J$*xfwOOpo2lI)IQfU)ddQa5N6 z2wH|V+`Uz6f-9g2fLZF_)G@uOV-M_6R6 z*G7#0b;82+goP#)U3tdbSzbRSjsH`jw69RQqtz(tkIbCVO-<~ktiZXI6E>_9Hayh- z&K_NRs_x>ZLS}HN=6hE26+kzw^yF1~!gX?$uUnPY6)nzYuKCFqX9_Lpw9h7TpG_2j zFMcibd@Y2h7vBgy-(cXr6ZgIo+;(~5#C?K+Q77&w2HK)TZBc<>59NIv^&Zn+7speJ zv1^Z0;;9r2EQ_a>VIVD@O2a^UJe7`t#(1g`1M3pF>$De)(4W6Pfx8|9RSDcG3~Wi@ zZo$BwWY;~(Vx*GOdDJI+)F*4q$aAI}Ur1N$u4V@KS8j?|*g57BPi2YrMDol4;ZLgt zzpQ5be$JXCoHc{GV6zb2Y;+Es`ln%oKbLR(9(J z(dz}XF|b%?y`WeJptmCTw<0C#L3A$F;t#6D3jnAI^{NStLt`l|krM8e5*`P%%l63V zcI;Br?j(=yq!=(QHmD;SwBaVhGG;|7ycVx!w)JvtxVbh#;6v97$n^raK5dUA+arBY zpQDrX#Bh4B3(|UHIK3D+9m6?|fip3jGZ;7 zX#sb(vB4#2>uSc@lWUWhYx4#8s0v|Fg)kWud%yIfe(6uZFtuJ0v0gC^Z8w>_OiI!n zoUn!&Sb2Jz#y(MFEUYnxy?2MPa0dozjfJ%sxEY>!Q|pUD=W#Rq z^PAycfy%rY{>#nqg&0VurIu?>70`)tA_O@RPDoRs!J|m0N7`^sSADvzOMGM3FrA>% z?1M_P-@yn|W%fap7X0(&g_r-$`F#9CUpjGLY7a%6ykG2PzU8kqOnGkhn6lYJ6KRSD z89uTM9~hh}+h=OF&%dD6b_IUi6}U`eOxXG7+wN~HGuQk-1jqX`JAA3;1CQVSJaG7F zYfP7pk1f`_{%X38F=*u3ETezLI-muX+Ab`$&4a_m5!;1FaFW7Vn8Lwi9&&zt z$XO0k9q0XaoRYdd@NO_Ae$bH%Hc3mBlx05@HglHb%OXh?WtMi*J*nV{Cw9j z%f4Lcv|Q;d5Zl3g;?K4=VpE&7lV!l`UbDs1M01D+!g>nz10MQbw z<>J-a8_;HOEKDYcFIZb&_8ID3T-> zNu~qP9{E#y5ZAu8#5cEIUO_gbj-&9oQX+3gC#i^GxZ!M zaXu#bJSK5IX2y9e(+e@l7chwnF*7b;5`8hreV9aF%#1#*#QphyC?}r(bG(UMG9)h< z@=+G5#c?|=*tUPvM2l9>w6UVVr;+?@nCk75h5r?8|k(Tmem(rbf5o>e%y#++KA#4V2qs3|#TyUh#pM4*GBhF_7X*rTD@; za(&5MUjca2LSM2F1J%A%HI}5!mukbnWnbzt2JZP%_b`y}N9AMZseAmmJ$|m>aB#jq zm5<%`*5XgL_`?@F>rb7ZiQ=<#2O_Rt!bmDa!w{i?H}*VWkOz=FbL@YL&GnfcjmQ@ca?|M#6xQmbd|%A zTs}6)-M}n^vPdEe zJEoUS64@A7P7=#8kV6tV7|11wTnyxqL>>lKki-fMq14BW?P>?m)K6@5KJj1|f5HX5@oM0j+7$zwXCdx6eE11}YfrG)sK@6M@CQf6Z zKbYvpz+f;jh=B*e!~+bx2`1iPAT@+Y4Z&V4gecbnPit2XmqXJ^n8tLJB#vtDH)*%i z+m#qvUcxld%Z`G}j$Wuc(O|WVS}lVibUUM|PHhO?@cR3qUQ^DMGBwj@diFG=e9)rr6{7bF9|Ve)NBq%WYqXKs^F40nc-+hj z1gz*VbL_xCnw*m+cLsWSsobtq?gqdSIp>I0V){R8u3T1b-N?+R{pPd!%`uPo%jR*H zF}H_&VpcwZiL4~zRuUS|h@E?;3g$03wvkaFo|}-*O`s0>!i0Q*fl(836a%TIfvKjG zQ1_+tSY{fy3I{-pm~tyHaLtr^O$#g=3RgV5uT#cU@GE2Ll`(Vx zePc|$!N5CX>YWxCmV6L6WXLIFn#}{Q_XDkq#@)e^nq59WkN=Y$smzX?fri#ct4@YL znzC~IpI5w3Uh&d3OFMSr7u4&2L(7@MD&PnTI03-=beAu@%ZIL9MPc?uVPaq6N{>K?S8M& zp^fxUINnb<8sF{^U7MpnD%Mso<(z30m5KSj4MZs{tf?A z6#_bKsUA_P#|FjRsze|?O}`WSNn z-o}``jWGuczP+&~dt=SP=KsZ5lZ&zD$X%R%kH@hlk7LcjwF=K;O`c=mZLG;#4CKcV z`EhKt^+V@TpI}m-U=BcIBGHKLuZ|>{j3k+Z{{B=&K2=$xg(scIt7PI;G8=()9|k>- zc70sIcmy@7EE`oGAmr1K%4kR>0FLFi)w0`KBQ%}aZS}{u)jy-mkokyN@#je`@}#!l zr2A``&1;!Gny0+ZF8#vUM7W9R5G96=C5FMEaqShl>=kNGoxFD#eehRV%ed%9efdW1 zscJfHe>l+}u5n;ITjKhB`hwfzqJ?atkPU~k)ofz57LcY73%~qN!DbselX?TP-hhLg zS!i&|fINkPy9VT43_P^sKD2{}$FDh4*PQLZs50uz9d&jDAj5^5;Q~R93pWP?>s-0( zT%p7XSMr1_2jsCNid+)KL6?cqZ@N5+T#kXtD5^5b4%uL6iRvh_I*J3p)+l0Ylr;cN zQA864x}%703|x#NE@EJbf?A@m1KsXaB6Ui8l7oKi`9$)3A_p|YC2DGk_WU<3k)ftC zFt9;QZP0?s+C%@gXYAh0+=No8Ygwu53=ValXM3K4{!T-qwjpr1dq|Vf<&Q#;{M1u>2;|0PEBtbaD zTHh9b`@`ki0XHj|H(07KTBBotyC{S zgRkd2&rSPZEU#uZr4O2O51K=rx!IiCjDh3k+~XK{W-fSU4x83aLa-BSYdf4K?ZBeZ z)H+S7#lTJ{#ZD(RP_=eB$#ywuVusN#RuM^7MDmeRLW7}5atH%UCy`4hL5bs&$m19& zS8&S}&S31RQgEvO!%Rzd?UB)Arwp+}?3d>BoI#$ z_{jS#WYe?HGPaFYGd<&$qv#eE$$3P$XhgUi?u0c4DH?-dY@tG#x={8BcwR?zct>;` z(i^ufk859+WV?kq?A0FT+a4B#Ix7wC#BuM$IfAxwH;#K314D7#Aq+f?7d(yEjFWU8 zBk_U}42;HeN8>e_a_hs5)5YQ~jD51r{fFBFQ_Enj=YcMeUCw_D4}bODd8PMKTIBvhHZ2 zJDNg`j83vjL9SA8!N9alL2ko9pMvO9P)IwVlPr!gSsX)xrN@~h>WtQPiOA+~+hJRn)$#}>8lmFR987~3l;{Npw(@Fkg&h*?F7w+8 z&)W(WDj9=t-oS||8@4dR?KK1Hnt=yW&I`YA@HtZ1xP>uit+kF`YYmsY>#U>KVPL(r zaJ@A&Hx*e6i?kqpLDg4IH~+JhS^TWhwO^&{j)D)-xo_5u-K;wq=$vIrcA56p4q9T9 zlD$a_^pgmI%fDe;85LxUk7|q0&)|bE27Y)k@DBuLIYdqmoD(%JdORfYc*u0nczfe{ zz45MSK)UmG-qnLu8^%TJ^*rmfNABpybjK>YW9OnZ=5vnvNs@KzR%VHR%#b{02p2zj z#$=u`1Z8I4WoGaS$Z|99atu_Mc~@XylbQD>3~V;@-i(1tGw(_aRGE2KVW8HGtTlu0 zvD=K?je)CX|FrdXrdK`JoB3StFSI(K!Rc`R>F`K2iubwnE_m`u|3XQu1_3oV!G8Ej~A=o@1bapc)7$ zvD=B-?F5aDCnpgnC&Bw|wkW7A3VSd~)Flvg2{x#|(~m7qB8!uFNb{T#U=VtBR%#9N zRZkkv_DL!(Yr9lqQBL|Djbw>rUhIGcUW>eEFnm>;-*?@!0Ugw5&sug=N;En(mnhK2pA-ALoN^? zKoSxl^j-smP(uPC>guj;U$?o< z>N}VBbCWasNB;TdOu2Js=AJoo=1j^f_7SRCPmsh3QmcY>F@h}(Hvz710^rJGFvxWfJIVVrMEEqs6KF-{vEJpeQlOqZiFbMu z?+bK|$rO)C-5D~EKNVlKwl_{ccdkKTt}X(>FD3t%=xT3tlxmSCHn)kbjk1(u{ovHk zF;|XK1FON&r@>JU#{3&cpEo+NA$IpCZ|v?JrA#4f&B?X8Fe?c3lVrc{4Iq#qBr}9i zPh2Y`*J2=FNaPEl;@Iy<_G7wcnT%H^6VWdZWqLG@Jc_Af+3`enyjC51bYS%FjQ5CR z)WZx(yoV&}kWa#%w%7Q6h`?L_(6|2D@TDt@6LMGTSsbGjhcSKQF?|j=JXc`ADKOxH z8K%HMQ-B4>>^6+#p z3DZyWNZ1TGr0)dA;6;7Mi~3#wT+-)V($~f$KuNCY^RMZ<1BqIFQLVmqD;7yy zG4j1)6aggajpo%GeFngh86ihz!~$?;M);W-3IHC?aDO->7=WyqzF9M2rWq!J3=Bj&R zn@WpK69Cv}=CRK#1b{9xL6;e<>X4au$jk>w9417E3D}0Ugl8=g3MA?X?>a&XzyreX z0TBs64RUDofW%tcd24N9cinI6v)>jz;!Rui zOW%g8 zjfFEnZQMJxai4%ZCK5s?65;^ZpB%G4IR$_c%?Bl#uW3+W`+vJEi+`E^M)ZdI_l8E% zE3v+H9s7qDEIUqp97j3djbfg_W1R2CFfh*fZX5$k$ZwaBT3_Kw?>9rwYFADdQ%gG5 z>W;S0+UU|Z@+@`wpW`HPT<5`cGN67uu>AIMN-@0~>3cU)>t9TLf}%pNMah9-sXA&= zb<~eE2=e>s1BVz^F|}GNQd<aS>=VBOfh0W6jS96 z$MA+@_+1-I626mP>qFp4jNnO(7=Q=y(GTL|=})fPLXv&mlv_-Fa=SuQZ9^{!kVKjVnPvec z(vyhvBpce+d{+4Rzu$e9Q%o)J_UT*f({}+To)(Tr3kTYZ9&kJ!VBiTS;0Y%I6gR;M zn83hOPQX(PY#=>0kTBB~@%AfpK_VUd#7af~?&;@#9Om^nES&DgrN8{}SM-X@#qOx8 zJB`SlM$mw9p2TNz_?z>nva%hk4w?JUg)5+2O$7;UEU!zJR~r5p(%kAzr*6=QY;nH^yb z&ZKoma=WqHdNbqsnep1IjZu3Xi6@T4TY!z7j3go>$$}niNTMf+=t;7mtr-Yh((o^7 zv{9sgPC8cl^Jx@(5dG68324*BcbLC^&cn@@@=j2r{;6yHQ&+h4Iq04+=>9o)m_yzP zhrA)U7O1|ayXSy7^;UE8)t(bUD6=L5qDgmw9s>6bi2E35HXxcY@X&yGh=Dc(q74J> z21GjsIt+*o40Ib1-5BUGAbKz`#pF*h;TCMUF;Q*|=e7&R#03moGA1t>!^y7FlBl$V zNk%M*5e#Hn5t&v{Vw)AQ4Fls=+;JgW8*Rvq z7}#V(ZoxQZB8+0wJ}&RgtAlXF!ksMB^Qjfj&* zuq{U|$WaT}+K16?sfR7sKo8zEQgRJerTtQJKL&2ZkvHNT=cLTeX8 zsc%Z1Z(=+Ax1>S0qzRx!+mtSCx{C~R%)WixA>DtHQkpL?6c-qafQnkdkXA500N@fM z=GtzN=gJ$T@DjlSy|eYhSi z*H0+dhY}O|f(d=Na(<)l@kSpmw$B(ioiT6)rJOVLJZI<&zy(9E3x;s9ebLb6q9HV| zRT%nI7($E2B}3OshOlCn4Mmp?p^5H_q4yO-xazGm^sK~+y=v%n6)U#dFreBH8Xsy5 zr8S1o?seVJ_qt&qsCuoTZ!HGu48!US;mWzi(5D5f`U6AP2Uyix4Lw`2inSSfw_#Q9 zGW6&&gl*bw=+lj5+GiNlX9%rYqfFT-6TaIu#m)Z;P@oW%{8Oaqc>z-8`o7T?8W?jo7HxJ)BwaU&+vh-4aZnR}eY_b{1z zNah|c)5KZagvm4^nI>H3K4Vi*v9f8_0zlup}GEMH{dr8_2~Qup}GFg&VOX8_7i*u_PPG z#T&6CId;i8cC)~{Ewqyq+Chuo7CXrn3>>lJAF+d$wo`UKr|jVTdDf11)(-AFR@k{z z*ug1x#9lUH59f<&eVpTig^Quq{M;E2NK2nLQSe2!w^n8N262KtnKeaay4Ed5Hqehl1Kx!qSm z%k=G};M+;i&blL+w<8&Lm_x~qhmxH^rYDjeP9#IS^Yvt(>&fupGSvJGHGKSC>Zo06 z_->b^crHnSs=<_lJlaX3J@eSerI~-8qKpQqk$$O>+Uq`wFYT={%deRJ=dP*4T~lY;x%=wF#bZyd zuu3Q$bZiDWHUsLo*=!=44YlBWHj$5kEo@?o4g?6jmC|41ODGL`zWqT#cowf;|+dFSG>rPq;)i}!| zyq8CWbzS`F&qhb~M+l5WvPN_Vy+4tye6M%w&XVm^+gvaRyI>#%+Urpx=~1I_P?JZ- z-j9rZ02na#9>Bn38bd!<(5OTZ$iBLYMYIOYtBEhAf?iETID4I857I}V3W{ulh6wTDVu~a+*O{?J5LDjl*$v%$`j58NhXDHlS1tk-l*7e z$B=TYSCu=?E_a**lDu(@ed8Dhz$;PwD-o>elqhCO6bmG#MBhz`z6W5Nc-A&CtVfHO z+k)9yTf{*vVr|p{RIwhhSC1G5DC-f2_K3qkrq9IUXJTk_c_#LGCWaC8Go5@gogkQ! zNT(#>z$CcBW7Y}}=qA3)BXpNX7?9ZMxnQR!JabX#J*&_gCOPLl>l_C1{1Ws0w82Wy zv()+})?#2yP|TVjt@o)Gr3>n%?}JrtgEXN*ssP}wH0G{Ud*ceqqbEG7N9Xy5KyUbaz2Woe z4H*Rb!UM$Rvi^x9Th{*j|dbc$cqx9mut0$#1*C672PexNaBhz6kKUOm`(Es>ir8WE`u zXabX%RQgUT{XmjQrEF3eg@Hwr%5O06$E5O408&+fsj46h#HFfWPfJtHO;gPS68S3s zd{qDj67p3F018z81(-yEYF2@2Hjvn-^52I^>{BJ|!}2(w@;`t{98k?VfaTGlPUui8 zzz%$`I<{9G2SC5tyI<`C!0D9lPV0{8q1SLeCGNb=n2Erp6xT~BZnSk1fx(pEL7ivj zGsR7sdfeEY*CmRP!Z}IIi(NuJHk&SmR%e#a%7d#20H4fJCWAQHos| zP^$T=R0FR7E7hc-aINXuK*Ffta!qu(CI;lvsgZSRqA-x$sZnF#&rZ!>G>91rX54n` zE1``4*9}ar8)!ofBk+JvJmAA!+e zfn$!uG3+Yv*B-=cY_E8?7qJ`L=iTJT+vFz!AMrdtULFPt{CEWzCuua>F2$Kr5 z1FQSa)kc!Fkpci-N0P5GuwF*4mqCebiR89K?KKvt2{Ti;nYvqg|E;LLnep(OQfhtB zYDBggY1ct(5B$-8ec#$r$`F1s((7cT43v8_((PuX6o6|{&ex)X=`S!HUyh2zyF$)f zAvgP`^VMdzU$!Rqa%l-Vr!yVHPIXZYNOe;(N+N5iDuuyKw~tk5lgZ(#$suVEgho(Wx6}YVmAi% z##rx-aiAp*|Jg&VI$kr~nD=?S`#f*@n6qBA)O}~P_%zkb+anXU>%7S0Ma#bnFWYdM zat*&g+Fc+6!NO>>z3XQCXy6IaCld5w!K1fGY_>@Jz(rA0a>0~56f{*qte_w^6gYm5 z#0y5^Lup4JRP4)myO;5S0PIN-?9l}SM-sg$f?gf?HuAd1VY4Hrsr6^sOtK7jGnvV) z(w!`Nf4wCB$Hm7_Q=O<&BJGrX0UVQu1AK-9;N8qML5Vd%KhtAo!Sh>Hi#+a3x7mQi zdqC%aJ@30!Fb@`eU4cF$|1z0>&}0gbY|h#({RvAw6<* z=lB=bZB!nq{A(|r>E0)F+S*G z`~}wEBVq53g#8ET%@PDQbImtn;3(JpC>E5ro-3@^U0;BbY_|^E zZXFFy7ap()I$#qCy5?=0u-i7UudcHVT4xI-ZrO(2vV{`6dBMARGLT0-PgKu?7s)^4 z1wG?M(v6AganC;N9yTIYYQk3PuCaLSAU(M##uv(bwf%J{aNuoyyK%$ue>u< zpIJ3y)~XrW7*+_3n8%Hne+oLl1I`Z*IO%Yc^C9QI4>=nlnc)05!NE2{CpkY(0y4MP zdJnt$z;tC+%R*Pn66p!EE${hHH6{Ji|76*DX4$3Cj~MAwcp&-j*Jmhyw8Jv*!?Hj+ z4FW|n?;;Ges(GzyAy6*b)q-}lGY0(H)gb`%ss+86#3QxqBege>7*N{}s6{kr6*XUr zUODG1Whh%`_Tf6SPXoVRQ9Hxqwle~SW(x|SwtbAr*`9JJnidGg(@`|l^#fIR9 z`8zkv=Yrb`Ub@)2#2rzQ1_}^FYe*o5% zD*k%CDt;7CjILJ@|*UNY*2c0@?P6l|5n=0MEp3&&2RH#b@H6XILJY zP6?S#3Xo*8WbS6kJn$LaBAK&A@(ut8ec})LB+%a`)JJNAf2<9Lhk9#+S0cDd8zCLZ zTntrT3{3$O{N+&1<j#gY=yS>0BUjS33KybPfPL;j??f;aya{;lK5U z{|<~U{o$YXhkpjZK=?-k;U5Dq6#o8D_y+(Chkr91{w)B$dg;-YM^NpB{&rQ!^RSDJRuO6RE{Tnc|74O07?@6U6Sx400RjT0|}A9#_~8J z>T!Y`fE)#xgBe|N6n;6F(IrO_m4kgNa};xPFr&*xh0Vr)7+q%XRm{PRE|G%@8D?~e z9LK;*MdV8iq$SQzOI&~%T_O)8LQBAbM9qQ3S@dLxn(%mHmc%tSwCef5=-SiKW zih3+D1>`Z7_|sV8&luQ*Oe(q16!J7t`7}`lWS%De@HBBT2C|X;BnMAaf>0?$S<2`v zWeiBRPC0v>at;Q5Tc`XTfKsJxDQ3ASReG0Vc8SwU)oEoCNOD>^|Fm)e09TZ8SCr5m za78)iiteHu)R2|RIhFqaA5~(8iWa493s#R7rDw}OEGX)BC2ZMoW#YI}3F}sOpo-#7V4_N#(nf%J)DilggDyY88AD zlgeC#@*ql8El5?p50a&-zDZSm3qXcyZpJ^XD53c(Xm`q2&CXZB*IA&7EKtENT%h`( zK=mQWbe}48AC|{H)$Dy(9tTvB2e3R2s6IG=<!B&)xF+nl?y6&ys8}<%STheKD%N~mtbtdUmuh}0 z#j-8cEGxLmVG~=3C^b<9Fp1yox_)rJEpj{t3%t> za?p)N)RGZ3w0VuFgGbad`hn1MK3*L^lL2oHCp`<1V-0Re$ zZ_S^aqqN3bpIlg;)OQxwCj--dS)MAF5zFGx>PMYq?Y{- zP(BZtM;$W%0Dv>*k~8K|O}=Hm90ldy2A56s<}3B;&39)|0<{*XHzMne#GvrwrsQ!`2x{ZFwQ&;K>y-Fmlez8I z1sABN-xAB+63eG;B7Y>Gsr)@_#|0+pWoOkEXVs3Nm-VZe{c0=FsYX-WN3kxmR5Nd> z=5x@gmTH!wsVEDMc~p!^k7Y=bgYZ-X)mx(RMj?n3AgMCX;#^GfI*Qmzav zS4Mz*#+5&hEB^psLizIq1}2q-=o`5UW}Bw^I86op>oQayXJ8;-^?tr;A;`8s^+kaS zdf@F-y}u8WIH3CC04DJ$MfxZOR{l}SyN^=fG3$X8>3~jR_Vo*YHs=W{s1eW?!|IDM zzxUF4;&z+xDFUtW&aLt8v~ezLOL=a3eCf34uvyA5=0{RQgccFnXobfIyUZ5;c)fz^ z@0Fps=(k^cf8!y0N7iK0o9X}Zt^M+?lj!x~ZROzOtXR$^YLdUA?{-Ds$Lxc5DysL- zS%JV7hxfKPd;})&Q<6_kNxlTP(w9bhlt%i2Rl_>@N9*LD|MF|;>Zaxr2b9Np`Mc}o zAC5SL7x*y0eEhxsfyU5o&Lu-M^hWiKN3mD~g$A5LEY`pl1L+n6C72yX7{McqcmTQ# zBfAWf>EHt|7rr{O)z|A1)dWXK;)pIN&HApZ@wJ|RUNT4jG%>kNOg@-MI{9QL7PoAe zPYz??C7*nWfmH%>l>i2)*dQP`V4zk=)?yJWx`kvn1_~U>0xV+1m@6^n$^}c#NmpVL z1Fu|(R~UHdMZWaXMpL0)Ss1wm3w72bGwG3$;MB;F%wz}y4GN+`!KE#W_Z|j_yB}vv zcZez`S;d4-H{DEjH&c5t11jz&&*CP}nKou2&~9(nZtn%U%xPcZw6736R6#tmAl{Zf zGuF3g@9O#TwU?-|P$T8mNImH9DFPWPZidQ(Rz4Rn(lfFByl&~{%&{({mm&mBUd~)duh4>p6Ly^G=Uqpl?K8-B?ELBY400R;)ib%(GligcU6&BoB{pEpoRe|S$%OQnAr^di zabV}e>8g&g{m0na0gZ}#!j?T@zYj8d&7S|7t#vN@hI$&Q!lGP?ph%C2NUODjO$>G7Q}q}{J+Ctv!*NXG?Di-(UpFU-uDH6$ zMh#&S+r7Tr?gj7gYxPQO_4))9JLvcIpdSphH|`fa?gy`9+8z+KJ>XrCq&dK>S$A77 zDt3FIWP6|tNQ?$5MguL6vw{So< z{ZE;JN0~tqJqTa!K5XLhS5zgX5SJUwEH~iN{~)kdVYXJm1E!fPikVk*^AnQjQOxX7 zaA{i&?cqrU<0&&G%S_=R=r+?2+e|+L1E|OJ-5yh@!Va3w8Z_13Q0rP|XnZkzRwb3$ zRTcZH$`ah}eM7~(p)&P2nc5q4c&Rt4$e7A+OchL@t3n_>$u&L6iw13~?-CLliYlqC zzD|a^ld%9iW*0-vyxN?SP57S5w*G6dSg ziFOQhgcBVY=nN-1b)fT|z7+-Ek5p14yn80mJrf4~KV?mxvbLo)2hrgzpJy&&U8P?9 zGrQT(?6k|nh0(VUe3)lB{m-5d@tzPr+Ldj-#Y)8)Pu^83`_*=W)pla=>BzEk%Cd8% z!S6RTX@5ETU8Mp>J!Z-tGvTTG)y7_{jiJwJ6K_@%Z$6zCC0TEmyxwju0NHj)*>>*$ zP~#X@;}{L*x)+WCFC3xYW@@N!YG^23ca&*XXlPcboCX6&e_D8WNx_1-&-8vxnX8UgkzWdKrJ+wn|BhleTq7yw&{;NpAMW+s~ zQiCMdSd?q*2DOSiu$n_Bp^vUz=OPutwu_xE4IBUz)jAd%} zmis0(Ei?8mL!xzRyE?Um-Xr=mDc#`LA0n%$Ox7|#UCY!4Deu`9{n7meA5H&L%%5M( z|C@f(Km6y+kvab}s-~V|NZ)u!-vWfY$~EBR8rXwjnrkpS*Wg0{S`4FGbQ&kU!lUXs zq&6N>n}Vg}vRUM^SzJ1lA-C`2swIA(R#Rb!m%1sIx_t%S)^2yjZg&Vud^hQp_@08B z3s3!IPyL{q=>33C?qhLPw*^LR!=~!mKuvAnV!E759;4My{j;m7);bu$Js9Ci$0q;k z&W7w2v(8sjeQnZU!KA@Y;E7)u2wxe503XP?PLkV6LWwbw z9K$4bS#Wn@Q9<%}+&rE=7(mzU`Pc280JtY0?+IWK%V7aIjDe#f?okmu2YydX-oxTv zp77wF@UW-bAJz1x2YJ)O27m%jZh&|Gi>|Zw zSZ5tde|1pWRVv9UmDa8EF6UFX9?y&#s_R~N^u6vVr3X3!FCE=qV$sBxg%iudZRnng zByJ}2ZtCK^Ki_0`>E@e_HPi@s86kZckwj0EGh+W89wX|gq15J0VX94GU(!=60(oKH zd12Zs01((7=CD0XJNdq7cZs@H;eVadI}fTY4ys+iB$1O6lam5%)O9KBx)c#TvE-%> z?*4L6ew~^%It{s5VS~-6WQY=1XqfbIdjXL zod9Tz;(dk71yc!7s(YH3t702C5nSY7Epd_;nb#$>86_z-{lz8u2TQU;va3BYp=4MvV9)7+5xgzib9f zvT+7~BL=q3;BUjg(HZ=s7&tqFe-;C`W-xEf-~erFat3n}1C>lxB@?Q~b<8z-b9%HeTXTTw^3t01(%K4ut31|K9t0zH>=8R#AhFtB zwA$VifI0_eodXAaIacslEBICbl<@f_d{+Qk_`DWAJhj!w=k;OW37_`_18@1fw-{I> z;H?qBJYEZ!uLZCkM}+(%LRjo6A?uV7R_wfxbshtI9GQC@VICun%n=N17BM%Apu{5) z>yZd%dQHr{CWaC#otP_~AlU58-0aK&U(yO^R)w<_0IklfRt(&hEW9oGp7wl0lSY^1 z+b+qk0CY>f@0R=yz#3P^8do!*+wF5@?sMe;aN3o58Uxo|nb$Ee;L00th5F%Ax4)LU zrNYI=5jWluHz-o;#;kRN#SFMH2QaYNow?Z^O6+mZ(c9y`1!mjq&TPhH`rLVa?y#Ik z?z~4BcPk6#S+C7==7%22& z7J5O6Ro={1-Vp5aX70kkb#LZ%3_SN{KF7dnALeQwn8$V>=5`ER@ZnwXfi znGG1&9KhTh03})jc&(Tzu`PhthJo5ZY=W(+(C6g|Me!$8qP47>~!y~My&plAvMD}zKUgJ3<@2eH=&*@AZ6930;qF)$R$9tyPu5>LYTPr_UQ$dvLkr4a0s^7mmN zH-eEHVMg0eP_g$S821p+9VB@Y!G99r3P5@!KRptH%t(GF2DV48LPr48K$BGdJD zL~eknGLlspX$53PBUz&un2cmiVjx|{N|(VTYh|pp7`Px~UBJL4*{_#me}htL70g-% zhu*V7PrOXUU#8OTF(9x)#b1GeycAJhiYL9Ua(9c1$ZnojORe-?TS#A9XpO`^yssO2 zuHLJq>_ff!#=ZKsz&^CjfV0j(1k~Jh2D8^0ECit1FuvOGU3!sr;JaUbH~4OIaM`kw4?KaAVQOWQ9_1Af0FI4AE za^5Dn0DxRMGgod3z%Ds+m)w*-nS+vClqX%3&jS9NZ{^~*a_v1`Yq(Er=f8BQGe_k& zsqC6m&UEBglr}Aim6l{h8@3TBOkx!#S^ZGw?tgLiOg9ATldS8L96_MR^~u)jlOZTe zHZ4o$0*Q0Uf^*4k0Gv;@IG^kQz~yA|MaoyaBTJk;>(&37Z0mtI8^4Gu314l$LAAztQ}^?xvq0`` zrh#pya07FD^qO6iN99)%Z&Fq3V>P!z>5bP0-g5$o%U*= z0&k7C-5MWA+p}(#?9X6soqdyyYIZ^`JfU_2R-Ae@vtDgZUy*`xUzHNR3cFaRF-6&! zGKZdiki`8I*8LPq0Gd-+%@}A&VYOhOHDzII%6A~i!<2}J*iq7z8lRQA9XM3VHjVo> z?9^wi#=lk*4)VC63BRF<1E4{}YS6%nHEP(68XF+-LgV>DqdjRe!{*!)>EUZPsV-Ta zY+ao!qDP;{ga2dyUEFnxaxS^g@wktj+Zy5o3~}PX3mN7F3}aw~6EK2-x14~t7|13) zvUTUSers+Y|Ha(kHg%S!)LK$%9R#d~rPlIN>l6TnJza)9;hid@o-U&p7!LFv4vYZb z`|&{UaSZHDOx&CJF1=%eirts;-M*Cn(M^>koqgbsLs7S>ah=1Ek zHbsu!TziLQ_`|g58NTEUANmMhlSr;f0%;e5kgt5_&n;SYn=(b-HVC+FAO{w-eyMXm z7S25_+$}9!8{LS$2-d(DU7w`Kx}#?g(qX#IhPU4{gyG1GjdesgocLxUcO1XP6 zP%Y(FV<1=NnJa_Q+%GHLE-ST1+3#h&yWa57_31a$K+XB^giW4w}feg|kgVZMJcVF&vbiDcwwLiYY z#%qVoEV|NE8`yYV#=vr0ujRH-;-anBMGQQ${of-djU9AazMA5BHAO=Y>7t)saEL?ocPQ7kNrIRpU^l&KPTVwy;Ep+Q2Lpu; zqCy9MQ2s_fxseaMX)d44#lR6hc?1K`Mcn5i7tph+oViubF2K%E?7=PeZ~+t7Ef4aR zhX{Z@p6+`*r2q_j62qQ+07gBDQ4CD_5tDvU;)Nga0s~9^$)*0Vl;!^9atsUya)$$9 z#l{1<;}|FiAqqnHV3bURawkGvK!`Ny1>Aq}1yC>3Y4F~T~w=Lf>Fu7yE0W*6e zgKT8j0`Q1IKEl8g2KfX7ZN_AqF^oW#XF}weK+9nPi!5M4iF@Y6J##2=Q%Kyz%wBy$ zq7MVZj>NDdG-_=Uk()#?4064QsK?A+BOb(v2Q&=ic@cSD5Y%{aYrGsl&#Cj`)?whT z4{_HACfOTA?!^|;TV&iVG6(t<-nhtqQF1nSaLEo=8dg3=afL2@$28;#+OZv?q@YsOy7z6hi%=-)*ATh{b z4r1ULgZT^t>4uDSLzw9?L&h;fOL_^2N;zf7I%Q}Nz*$4qSqwBF-5diGhKvbAOZtn4 z@_22?c#VN&M$Bcpt#u@kX~fLLz&a!5It=6*F>^6cWW+4O0P=dgq}x$Mncg*G-NnG5 z5o-_wZ;e=QF|c|DYqf3@66KLKgO!DW!WpbW3>=!lI)s7J8LUzaT%N(WJOj4J9wuWC z6N3Fr=6xvly5n8B-W|OETYLd91ZyuC;*0 z-mzfb!N7zCa{>c7mW&)rSnP5u#&RnNnynbk80fTObYh^xn$clxNvlNYS^BJ*ebzPr zOjeHzSlsiGW#xfeQlW z1q`$am~9y76EOQQ@KV5diIs9d$T%Q`#cp?GYBgwUK%X0<4+Ht` zjC^;PxuQH6o0-i)Uh zDDh#G_`oDDd>AhnlR88!sx@m(on|I zP?*QjP{z?vOL|R#iaiy|JQZpKKw~Jg5d#CEtbtH_Ah9-#xi$`2l$1qS4#i+nQ zV-&M7$_6BP9>shfWkW~kLV2{tGFoFTY1UTW zyqWowEqTfoCduHD89WH`dF*_i8~yb{#n$n{>v;2ko%soG))U^h0Iab4Vujt`0OZ>x z=G!d<;G!LQ(GHf~RQVLq za#KyoR8#2uvd+wLoml_~Np#H8{g|cJt}i`yXWM}${#~ji) z-KFeccQ_t*Fw0dZC!iBERK4N^yuv^_>5;CpT;(RY{Ae_5?ppy=&4$m?`>9-gkq_Gs_z(R0Ag%Kqf9_a|rS(X#PVvm5Lbyhf_hg<oeX=8Lh4#G}W#;{RUh{8_R6ABELn?IfDf<5xH0QrP>5Wv6I>Y($4CmkA&7R@> zeTI_;y+6+&htM;e{aQrNI+Vlwvna;1C`)>m6Aky;82j27@o$U&NEfE}yQdpvK2Kop zo32B;B111-aYT=vJ^uT5KHF<)cjKR^7E27dOAMjU{t6>F@gB(@uo+c9w3h&XMe zRa22f;|!uvx4cB){tV(i29_|1CA#${l32kcR$yQ?lUR*`OeT?ufptt`9R_llL@ow8 z+4h}mXe_^KPF~fmCs46f=42HH_F8cFVy^8kEx0c+aLCgAkfkqeOF(%Xwp@7FauEP$ zthr~b?ZMP|&YF7;1LfA-atvIw=3d0W3kTi{2k2^iSU?=s>71z8A^}l^fg=Lq2nIR? zLoHL6L{>Z5(2D~kanFgohk-sPZl9Aq zkQjC1k2*nj_P0*tTP#VoGnwrSGd<kPfw57`a!cVWTAVVchyKtx}G_ zMycaQsRVr1Hc1^fVIWuPn2Uj8DYsY(^Ee^pp1{CKDfc7>s-$FK_~V4yXgXw_-@RK?yGByzy)3tD5TRwClsj^Bf-6JA9sJ*p`XOi5`Bzc4WKdQDERcjr0zU7X7p7H+Mdz4Q9 znnAv1Xm#eV1D1YVA+l&u`Nnl&y&_;F?#Rw zWcPW|V7c1QOX}B!ltM{{cyU8K1(0~gQ$OR)0^lt#@-0sezy>?z2D@YcHrqvSwu=Lx zBQCHbE(GlA=EvLR$Mb1_t$+JWdCu1S+C&9Z+A9(6mH5yOGzjdMSnZcMS)ZSB%lhLA z4}mUMqRSQTVjgrO4!S{b+l{!5fgv|y2m{;PiEZvMNw+)Eje$)b#3m0Yan*ykih)`W zq80;_9>gRDN<4`YPnbuqC((<6?Ow!oYy{qBCtyLK3GExu-Btp(HDmjsHpnHMGiv+r;iIjjRG-aBjm%YP>CQy|LBR1PpubU{vVRq9v`qhKAEn}_tq!N ztNy#Fnff@^*x1$Bz>q=LZ0)YuLU7VvaMIonv|EY2pacWsDc0lI6&e#M))P8Vet!Q- ze(SkrYU&>-Mwa4N&=zpEO zvW0q2d%4DYxema}S!nA}Xbb&^iX9w^F^8f?jcudGn;svk(AnO@-=Aus;$Ed$%}cX_ zQP9h*{K~8nK}Cjb-ygR93AA&+Lr6b%g<2Z_$29(G7{IxZuP)?&L2DtXm?FM!5g*1{ zKF(Jj=YI+$t~-8w-SIa7UOIm8((z{ic8el*i{1gCL>yWohWDXej|{mUnL-=JQL(3@ z{7ywB(pGK+(&MD*akIgP{94@XYjKOf_o6K2qq3A=>85h}RKKeJU*Qj^K5~*IPLf(x z;~me|*$KZTp(C!yVI(~Gu`WI#0#y<1V4|6rGcb%tI7VI zUruNC%r*X*>q0R6hujl}+&>54pm)MSZwRUa)m4Gt(h`P~k5ASOYr7P4NR;hJlp}4lLuubcG2cY7X}cZ*Z=;xRF|b6=Tq1`OOXbX^7`PrSxE?J5cXiZ8 z^J}AB0C*WKei`jS>(;20SJA>((XIeYMSD&~YyH+A8BI2B`ej!u^>mZ=fs@#sVsm{C0%zKyFY%t(=n`w5N{S3hN=y}_t;l(SpaX;6_ZPKH2 zQ|(xA>&%%Y524 z!MfS)ln-u!b#Q@o0#G1!+W7CZi2*P4ijDsj46LyAUttR+Ds25LFfeNCKZ=0@o__%k zCaJdbueO6=tG)kLdk7j+yc<)rVSIo5zZ>>8;=kIdk82uJn1-F|IBywp-V(;F&5KOR zi~O2y`Y-ePj;DXQt(}F^mN@fEoPFqKMWD}_*XQg3K$=Trno9}*nJ&?pE^`1l?!r6n z0+W=ua7$byKw`{aH0B>b_g_>>dH_E?zz2YX$=rj<60mG*OXjsDdjQav%{w z2X_7)dLezlq=TC4a@D+CwKE+@1%Z6EAYbiH1A04@`d~cd8b9RnPuHaDf5h7%LZXG% z7KPXb=@Dy-BRb&Jc=C%KmFf=4Tyn~cb;=Cty0^{9+h%a%VhusAAs{%NWO6!5yK#l` zc$UO`mSnAc%=v<_Rd%0rP+hcNBk0$N>G}CzA8mWqIQi%FxPZ|>M4N!TKp4QAn?@UWrqIAGx%3QrjdUjRUNL}Yh__QD%f(-yhB zMV>@kC;QfXl2I+}=%o7Seq*ow#_;4%LA<0OUPiACgx?HXZ2zURlR5}lsKGaBnM877uA3NYQps^UxI0A_Qjr)M^)RakZz+urJ@-Bar z`x--X4Ys$K#o}gR(Wweq+(HbDbIEZo+)FF7<(AnB!7O^fo_7EXH8t+Q9mlrL3dFnu z-NJ)vGFNiHD-7{e=1P`f;HE2i69X&U$Q5od$q6^|1P1Q9k#{jL;zo{OV2L}q#2w~w zz@0pRfeY^B1q_V4lj9iJ=RxknVqi6Ta2q{@U;^y$;C5gj+mp=3f?{3pBrjlK(vzIT zz(FtaAZFsI@FFWP(BVaPU|@|mxyBnlT#+|fgn>G5vJM0Hym|M$;XcQdH#vn#WcrYq zJ}}cAKI9GzRQiyW7-;e#n=sJpL-t}|jW4;z7iL=SOO|6`wI8_}iwSnpk35Ni)&Asa zEaKN0fAS0ln*7No4D1i!?+<|cKAiz%X8^3nr6BTB5Cpg4xVLoYFHy(sisN=+U|$M% zUy6_(d>M<0)S%nPyD05+m3ho6b2zoFHji12fio77XDp(@T=&L4{*Ap>BQ^(w)4C9c2l!Y7MIN83TTZBfz#Z8(_A^d?EYlmrx)-4Fy2jl zAx8;fl+Z5fk?1QT_!R-I^sk86SA=#6UEK1;*3Asl9_m!cuz~%sfg>;=-8B^6HFTyA z0U(K7V_vQ?G!Ny?w9lLA2p%t!?VHJlXMva52$tEvAa$2TPM1Y+AGS*5RE2>Sv8Y8X z0ki)DvFHH?GJ{<*gFV1_EDp6V4ux9)uch{{rH){-Pm8cg(*=IyA!N|O*~CulcsQ>KekBXtWtPZ3C(q-DfXo)+Ijk^-Aj#I zLN{8WAx*{9x6m}W`-?j+!@tf;&fp#IiTl)~mpb>kCrq^`>@)Bg-XEsiANDB#^>SIg zd>;K>LP=`kVrt?R&?_7So~mR|RrBZ%Gy<xS*9Ph!sR|K|w?m6afpOvWtK_f_!HpQRPD z|0!Qycdd(hFFB!-o>0N`--Rk|p-K-%+Z|Qt9VDlClZw}*5&=b%O4g);CpTMFpS7yK z28veIFRdyVW1>y zfeq&JsDr~O;@NX@I`iaos3=!q<(4^=64+V&`xZ$zbw?ZQmo(U0VuY=DU3&e^QR(=f zYJhf}}@+!syM7ub1ym`S7Rp-PC<5RQVUGl%Qcvs+mnH z7c+?bVb5~6x^Zbn$o!0uA8GSs*{l$DR>&M0d_JFD7{IRYp(>f8icC?xLmTNHEuNLr z+}T52uQoWgHWFP?gq1iL>vu4Aj`_WUe0YkTcQAGl07qhH9zne9PQ*H#usGG(7JhFXSr^tz zyL8UfhvTB~tci-7$axBVKpBUYhtsjw*M z6b#g)-W)M~bA%Jv#LJGf&5p$AMbU2PZuqZ<_l-AVkCpWvtLakrcLjNoD_HNSFP}*) z-8bGf*O~8KXZ{R^c(K2{*nd8~B$3TL<3I0=|Bo~nuz%IY8MuEZG=F-_}vh z%?X7|3;L+Rav*|vAi^GOZ47CcLmD$!c5hwIiITJ9My>@Px4IVmw-r4a?>rp#f30rr zqvqdk4tqC81RlC~i>B=sIe}N2J)&uQ5Rm7`&hs;8a9(up;PAv+)g!95JHn%OgfF6R zal!=rtK8v~N8>{~G_W=_2K+MChMHi$}l*1-fMb6zOIZAvTJ1 zvLc-dQG8mY`-TS2XC`dBJFRlOoqZx{pU4=rIOJJdmH)?N4O`Yz+dE#8?sn zlEqju0*;EYqX;-A#*QK2Pcil<0?Nf$IRYxgSOo&ki?QAFDvXd4KFY0#XC8)BspyT>!QY z0cinP8Ul&}up$JU3cyYwpf~_4MnI|zOGSbNq{;9!8SM2E8CHTQN@Z9n0?x{?vj}LC zVND2VmSN2ZXpvzp2skXq56j_YkI3;O2*{V?`3UHhW4&_NL49(p4*`$l*dqk=%dvh0 zR4TAa1+;Nlfn7$x6$N$$0aXgD3IWv$tQrAV71&h-JdDC0MhU??PDd2pfq+AC_@Ou< z{a#Lvpu=(aVGHnZvzzaa@g7qv{4M(rZrQ^R*e{IEFN`Ys0c%H5rEp|&&SPqYPms?~ zkek0>=CIS2e;1win0l~!Bn@~ZjRGTfOvWCQnO|r~MT;V|MG;3UDp)^Y_=$nXcI033 zMK<{&5pd_J^A^^5Lt~G;g^v(WCYF_n-=U3>Hm>@FT=jwZR_;pe?@HloaH(Ggqtx$| z+24UI)E6M>3ormHw`F0sWgh~tSH6R>SAG%}%987{~}8^Y={{pqvFZTFu^QB>|pW$E|FR zTj9WS>y)eTl&c(gX%xH06kD=nlr5e0$xS!b0OgcXE8^9PlpyYCmq^ql3Ik67XQiyO zi0?|7lvRd+M**xy0noeUKp^Wtpc%}+>!|-m;5a}HsRTRU1iN_fEVJCA@wmwAaUP;t&>-Z=zpW1F_IzX3KnTo-h>t^nY* z>)h9tj3%2qkDlR4`UfZnxpIR;xnU+q3sY^FUTv5K(!x|5+^P*c0J;rxx((qoel%$Q zT%h}mY{P)TXTab`KmL+nyHT*+=mp+t@{KuXZ;x%k>%E(mRqleNgOMz7gbpQFD?14_}yPTS+V2SX|#85w1<0q z#YVqk<9l?K(w!DNb>$ZfQlFTHrg{%e4WdT?33_K?y@=0j?<{FA;xl`ZE53;M%(j!8 z=oS~*+Ml|vGmjUISLCo?$YDRYVvL2U#w_12=KcKo_hN&~5LJ=OOxa~7bl$CIzE{os z0o1IP{c$b(S1_;IILq2Ni7*n;u4u7O)I`^HjI3b@bwV2yzk1J7WLi0achNtr z?EbKF`@Aanr-bLPbW|InxP4K6^zZ6x@3i&oyMiawZS4?iI>aB*R!Q(6Q2!v%+`1)p zO;#&fettp?up6R)8=`l?TDw|oS}iuGZ6OuO;`n6ocfizD?$N4*QlI5DmQ=MCXMze*gcjH z-XwSugg*&l(>s79DA96Dw0v;-Vek6;%=4jq1IU*ivY#$!ySTO}@zd z;o11kifZu=)z9=uyfD8(*MF${8MQ^V-D>uBD{t^LlV=r^XEm3edxaNsBY$-Fcuw8U zyP>SRmPm@@;D7FhZfDRBNi)mbep0_0c}_hfwOUPWwZcI}-_(gSQYS)3-_(h5sYu4B zbCZJ4P14aHZkD!RsC}`*;RV(5Asfdb8(%Pvp4db`u~`Jba~sF!7KO*+saIxPh&~>*FQ^Bd64T5QlK}WvHJg~tCTEZu`L2m~*CYerp2={} z^br6ZCfg1Z2Y@bi){Cc*kDax<;`5>4V}^pyqN_ExarNjiGVyY zGfxbQ42YQn2*~zfX8S-zl@GHD0h=VuO%ka1Tf+Ps0Yehz5CRTKnTMpXNIe+|8p-~e z=F3d;g^GM%W#7VzR&TNxIMUKKYM`2DnOM8)134Lw} zC*2Zabmx90`DD45qWuLmM{9!JYl4HouHd!c=ZtH?>&=_D_XPW`6<1%3kGvL*Q;UWV zzBzSim|Yr-UUW!v-5O>$0-k9co@vAmZ)Lmu<#;5!QHIATLreD~sU2f@j9CEr+wwM&-P=fZ^zRHg9NtEk!IJ}Ky_coFp{UlBfhqsaJ{@)|nB`2k2PG9-k z%Ri_WgY8z5?N%~+?}G$o6J5&?*U@tmIp>h9L-kW;)=z=Kzz3%o2dBW3UENbXx{-|G zJySh;5Kw62T8Ma?7TLHKA>g!)$7vgwb^MYI=aLQFs;;!*R3hNAjp(wClztK++j84R zecL7)fJPgaMjPn$ddJ53jtvZ&-(=(5gn;^KzV*`rfsIqPx>L5X0F>Dp%WPrun{9>7 zws429&GzFq+ohmLvYj#64i;H&|J{1~pTSOfrhRCpJ@huNVJ)a(eGF{$u)gSFnLkpI ztsP;#H^TY|C`MWT8fAS7z!nF=76&l^84iLB1nhAL-Qxf+JLs@t(BXGbVCqG&`pMvI99}T!tyjEU8k+A{AulKg`1}X4&8rw82RQTzxh;{bz&I z4*+a*nzPX<0)XvKbGBOmS<_Qa+*3}ofnuMtbf2@Fegqlul`dy^`2)O9#bI}zR*~#(Qi5x>u=Sb5zW*cP1GC6oA#}m}!AV+tQ10zcAb~EmF zn+p_;ZlXpvDR9{CauanS;FX*3m74@8Qn>OIE<9G5$@R~Gw zfsJ9FdYBgtzyW^H0lo@=Klq-1@O=O{#TT67n`=tm+XsBV2gotgPX3%uegr7e#SiV` zM*{GgANHEB0bqn5GQw9QVCe||3jjv>oKZf^)RE{hC(#35rN~2E_vwd55~TWwQ+><|vMoD(9C!Ng zfMSazY>Pw#K(-_}TM`byS&8de2`utl;`$r`ZBo}ZDO7CoTe`{b3t(fX-_o53xZvk{ z!4DQ`^!upMZwV;U;rCGo0Y5M=k9<_A z{Hl;FD^;p5s}T42YL$1j3I;wn9`1EK+!uU&CTm$6 z)Xr6EE&vB3+z&1|Z9EiDoLEfGopMk3~nL_`3vRV&`A^{4k_$s(EBIhooB0GhR& zW-XsS(?BX-X*sX7d=NY$HAxk>RH~{K&$~r_* zrz7eR#SNYE2BNs3BW@syn>yu9L~&C`+(Z=hI%PegsMitoh@wHKY(NwZI-&tl+|ns; zA&Of%;ufN~tyA7c6t{K6ZA8(iQ#K-sMjg?JC|Y#EExK^fK|MOp9-R*W1G=aIodJMB zUDO}~hICOw2-qF-{_dDX;P6skjB8&E55(^pig6u6z;KM~umz9{XfolROz`+e+a_HZ zUF0|p{F8|Q1VkhgkqCGvnV5@!Pm+lx2>33U_#OcXp& zBB+5-A&U1Jh>sBPdjqkW0mxl~e;29hKZM{PLJSnGgl8+^gMgS;!URAY!EZxsv=RPo zgc2z35&V0I;vV6D4^gxe{B}gqPWZPYiu;7{J|O`{?-QZ-iAV&@zfXJ!KqtZPM2vP4 z{+)=?E`r~MD7py$E=18y@VgO3H{sunDEbINAJSukgmjRQgL(`Sa|elq2>5A`_zi$( zg!ePT4*{lU#5@Fi|BP6HfE|oy#BO*qM+m_PQj<|aIEwVy8$$GkkOC`j2-6$nI%9-j z46!jr$i|3ZP-LZEuu>04$V$Czr5-90^nwILk)W3)Ac|FbuT^?qV04vUwMwr=z=Bo! zj{#Vt7py^yuF=cZAV!n)f+R$dq?aWjie$YY8BrwbWyy#lRqvjv7lO-f)blp#%?AR> ziMdH1ut^^T6r1$#ZPLS8woUK5O&0^iV@6t9%qfhn1r-U5pdBJzvv2Wq;v3e4g`&ESfd;CP3m&Px)6}U#Z$QM;O%%97vF_|QZ81? zg*LMJtZYjPBC@r$e5@7;J6`007kNOVeL<`~BocLD2v&$hk**BEDiKfJ5m-wE2YCs>wrV-xog+00OGP}fGNZ6e1XM<0l~Ek0>L3GLI*GoRTA=xiI#n>-x zgs9Z-<5Iuh!E5Vr*-plBSuxa{kQq+M{tYx`fpg0Om(w82M(G^=&;JZlE`PmKrM*)_ z=n<=KeVska{^>Ba5q?MHcSjTl{9sdM^HODIBMbl6#pS}{?_N{VN|CWr^gcKN(ix`g zv;^4;`&!lLlyQArn$9w%v&@Iy$+Fq3xNO#!bOmxdZ+a)EPadJd#BQ*%-e6^$UFJG{ z@vCMkcX6VFBE?RtA706Xomo%Zlp_Zxfc4FYoUkX$^HjyFMG zLC)+!2*ZGI--ECU-=4FJ_7pH*8OVR zoHH*Hhj+Qwkk@^zwtlR(r6UHCU_fm>ptemvBOu!IOR0PvuOb~@MLN;$0_|VK?fsf0?ZRGTE*l zE8Vha;czMmGU8`vSi%5(`uW4%g}b~)sIvPe^Y%?v()XVPMSRa9{v3MUS#a(SJ3{6+ zLXFkFkbu6B7<#Fm`))&G?Y9vl)cN8*7gL`LJVA2Vb-`ui1j&f&+!4zO60%5DoNHBF z7(H1J4dcA)M@mMhyfFV*P5sA;1DsM*CeBEi=t&=-Aj_sqoS!oB3joR{smmtKqn%Po z#YuL^NwztA{SS@x!QQsj*(FHy zkn4QGb-pj%HDr;e{II8d9RMXtNr_TPPjuP~nDXF7<4=;m@3*whx3sSGF3aZDyj~y1 zhi|BgJm7ge;K7PK<$w8<{{wQF$P%TIz6@Dpdt}h|$VjljR%->-T3>pBUATPJ&Jcd_ z8|sFi5c!@E>A_<0w@CE2NJ9hZj3dWNb~U`AZg>%sUBonh;kq^HX=mR?<{0&t^ofD# z6Ls_*Fs;9Szs+m@_+K6d2R{rp(t~7P27m86>lTj9Aj{-VnwC2Wr^}Gw0hjfF%LdIF z;Iak~Fv4YxAYkom*4o*y$d1{p9SEqM&8kH}%WPH)0*ZO8Vji@yLBiS~F?(>570Z*d z@(@qbYz3B$IFom%unrX$?A;}VV+rK2x-u}vXg>7=Y2$o2b{+xM;nS+aaiB=oW9gO` z0J6yLIDB^;pPmv{`shJ+;lIW^^+D(t4?_P#dvKq*|NS!08=sF+=f4`Y(fDA<=g z@k?GD=+tcfd)fT|0`QQ(lJStA%>bw`=$*cxAHlY1OT>>Y5g80Hptfq`wrZEt1B#xV z)5!3PR~b{TGF)i)OtMb(3}rn-54QSR8N60TAef$Q3|<=pe#5-S@Vv(erdM!sx-?l$ zX|jSwS5KU_dZHT`J;x^Tj!l9_hbHlcCPAaEPM)n!W}{@qPDCqDM4Ky?d*j)PwJu+c zckrEf{ElUPq&HZZWb2w4w#^JKXkF1H+oDNa(3nSVGatE`PbiU>N%owXjAS6cB(%LG zi)_0~yvD{}q9S>nQ3 z@GIkGNA_h$^R%Uk2=G<`y#8YW`>}uzDwO8QPVJ927}X<4Kk_ zP|a53$p}bM<0%M8RpY4$Sf|Fx8W%sK8RkiQ3evNi>GRsC7YyvexDXks)l8X339 zmGur|!Yzw#!fo*HM)2<@@b6~u@0;M?q$&EprC)#gZz@8L{WOOuQ|)Y+)9x~CC$s(! DDcm%u literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_draw.cpp.5B7D6E505867D9EB.idx b/.cache/clangd/index/imgui_draw.cpp.5B7D6E505867D9EB.idx new file mode 100644 index 0000000000000000000000000000000000000000..b6277cec0359b6df4e36b44a068c4ddcb1d39eb1 GIT binary patch literal 110134 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�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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_impl_glfw.cpp.99B2D46074E7E29B.idx b/.cache/clangd/index/imgui_impl_glfw.cpp.99B2D46074E7E29B.idx new file mode 100644 index 0000000000000000000000000000000000000000..3d335e8df5dfb33ef55d2b647b6a3c46e267989f GIT binary patch literal 20496 zcmdsfXH*nR(Drsu&#=G_C_zAQU6MovK{24$teA5S3+AlXbj>+|3MitO$VreWU=}3^ zD1xYn0RKKK+vC#o-uwRg{=Mhhb3Aj(r>DBRx~jUmx`qkEhYl5w6Nv`RAHH$L zf^}|4Bob-ke{0vRSuzR}?|G4E!t)e{{@89yNDle}D} zsYInezqmEyg3$rfgKNZ|viPrV-D%fB^|iXyPco#wF5&Gy9w>2{-ywBGUVgth$2OiX z+Brj#F=_c|ExFzCl-=8=s0WoE|LfH-%@W6iyo%+_xi|Cm+v#enz#Y#pB(toEI8>=rKtxIe66H^Cic0_pOo)+I1vh z)v-N6Vb-ch<;ulN1}6ONo_j@QUtZUXA%@_^D8ofd=I(UsweIGN>Y)Y9%D=zm z8hqUI=!560%TLArj?cX34&1)yfO(I0^G6JMQ*>NCmW%Bjo%HvVnJY)4cFjp>vgOva ziPt;Dx49I$XU}KpW&MKM&AV6ln>1*@`?S3M>YuM}I)|jS)7YiC#O7VQtb=($YF^zo zezX5BF+VULrxQo z9`5jdX5z@|UwVDBz^~iXOnzL0Y4^>u-$lNPnsV4}@g1%C+#Rj_xqX}RH(X4080Yii z#E0=QI-6GvPpw*$sk3Cg%GlHAXRHlOpOxJBE-tg&+wS={uziu|`kbL5DLP)CBkb#E zJKdZ-+u^#|2#w9hSE_~&E4uHpWzLHq{alNt+D1v&8K+O4xk+#P+@H(qS8pww`0-w$ zN~3nly!0mzPBioFIt*HMr@_5o{Q#92tF!7}I&D6`Hb;F{T>65#%K@n=v*7#{WqaK2 zj=26o>KlFH`kotEheHzT*7B@Sy#hyD1kEyAK^xR(Q=yv&+*Z!D>%;pUf zs>|foWf6vRcMQmyv*Q_)?R(2Y$NFdAWr+p5eRhDIsf{DnZdx(-A}11^I4{x(nQOyH zMBPOqc``6c2EAGy*z^4PMSdKjS2!Vq)qtJ4iR3=O$Oo9WJV=v>nHg%1!#;{7q8^w_ zAj<}_#;%6?hTTM}Z5WH0vw$mG6ZUOHlg|h}4LB8Z2>?a`phwGtaB;k6@OJ6mu`&%f z12;JajE(`5mIrBKm$vxsn%b2ziKrj$=qkrv<&0cA>UXpgsq(yN{%qV)AHS(`&u-Zk znxZba$svFa0fUwY;gWPJvqvDm&q^Y4z)d0pPmw8)(;81CGWsIth$U zg3c`u(xg|;a~G>S<}R8dJIv(>Ku3Un%Y!s&yr*{OmYutcL}ZVfQ+NRs z57N=mDX-J}&Xc%GM9!GYA%;D~bad^Z-@`$q+D=u`5!3dvw^niTT#0A^Zt@(m&!LH{ zlfF}Tk*XF_G?_VLhKqC4JbGZ-M`_RI* zyMFiHB2{fp!DaBc-aCuy!xw4537AVDFbV|STONeV#NsR3i!M|xlZd)vF3Aj=%viWO z>N{dCIw}e-H&_4Mwr2WV4~fVLH%Uj5bkwosLAW?hNpzVwd54b%9D_SL0gO%n%a#Xe z($RK&s%5F0uLc~4n}h+QFks*EAWeELKCd=Z`?{}0Gy->&%Sv)t>y`&;BGIr_n|(I; zgr=x7ZgLo)!+50?57K1#Ec-TRHhny;0Vm^*f`L&mUL(bWG+A`eEk37aWS~Se4RhJg z>Fno>S{|gycocfBU=|9~fTJ)MKVaksy0kn<6VuU));^oSN3$YZ%*7qB?m(;ML7Gfi zqmlo;*S=_pXb^6a1tnRqOUr{aDb`PQY5&LrHyMbVBqKH%Ik?*C+YJ$^>Z@D+u|`pL zk+zM-3j3$U#Tk#2cu1gZ3>C(qw~~xMtn= zUP&5oJf2YyFbcwQQ9MYKz?6AS(IGdkOGGY0M^JJTc5Zo)CPg_pA7}pMc3UDEf}1?! z*k_!9YajhSwjxyniOrwk{x!1KRF4xR$2OV*fXn>-DUdw|tF~7B-d*t4jJc+13?#K_Z ze#p$#M&G7~NL3Rmbaz$WiB^V%SHFscSF&UQQxa4WMYUWRqrgTeaCQ zB%z^>&%nhcEny!qGTgdY;oP~6cQsQv~zv^+?Yb^D@iLFUBY647AXB#vd{SR+@d zzEsJ{R30$;#8EOu6XVi<*#an$e+B$kASK)-@^siE z9byep{6;Y|3bx<&hnPbJFs%TNL|w_Y*hcL?o1sLW1Eo38xz#%4`B0hwOBlpkTFM;PZ;*D4QWq=AfctIXw5j5LaIZrzgPml)|K#<|tE$?q}JdyF%gnndo7 zr0&SMb>-!!k@PfjZq+CGB_zFsoLfy(p1?{ISm#!`%JW!h9_!rdujF;Cw2pOd-S6Z# zIOz?}nQXN7L+j+K;bRyqp=f9l4Z9Et&C$WCv^+xq&M+!x7$Y*nEph#>{{GknVc;d( z<|Qj5T?Mb@)L*VLVli+5np~i)#4d5O-;TQ}0L8$j81y53kjMk7wZt35RY>G#S@Bu6 zEphLmx2(Kwt?bKT4!%(43#|!@j^5-;2*ns2# z912jT1346^Ob2l&NSO}iP_Qx`!l4jlI+R19%5)fq!j$Q74uvbz5gdw8rXx8NsZ5{e z(0OJ00*5Xr(=i;1p>&s9%V+1L^bli-<^VnibRlY~XW_<|C4pB|;JG!jhn%=L~Fc1s*SZF}r_!i_eIBM(xbqw4A{2icA zfVOQ`j2`&ooB*7GvNM#`zHKWIc3BYBje;Jw+z*O&2 z$WgU&0SEvn0JI}v^28;+C1=}YNbrQckeL_iLk&Ia0zH#0;a^-#hN))98~iZfGS{GMZKY3 zhW?cB+cg644p_aT2KwUg1%v4qdTL_eE3o(qdXRZlI-jyxbE!jH3|xdJ7pZv-?$>Ek z@+3JB!13v-( z6SOC9sE9UvEZMk60NkO>oi4oH>K_}s4i4AmBsjW9CxIFX{3Y^u#weaKA(KJ!I|$uD z?TF*T`wnYa!FC~om&h|Y-AvAqETXWe=2@H#p|`B~T#@vsTt~1a)U^=xrHr1Su1_ev zp=LIJ(Cs`UnyqL8eqg{gI778cH-2>mL$FAX7r`2pUtkZN9Am9zg(S8^Gb6T=CO8Ydaui z$WQ?YgnS^?SnYt>r#lTy7l3fchf`qdoSD7O?eEtf1CNpQW7LDp@|O&z7wn%d0QG>c z2gan2+HRu2PD`%}KnSN9!dZ~%TV1?2&;8k9LoC_XfPYOFT_XPkH9x^tuNAQ9+MEzK zeIv}F7Vx#S;mXNL%;)~61R#S$8C-j^XfIC%zp30euLA}GIn6-YXZM-=*1tWN+X45v zLuH3jlv0gGSEKo4%%#@b_D(5yD!d=W*#>d=D z+UE$sZK!=4%E%@*#_8yb!N(tT#69LCy?iP#>~Pt>+3ai*C-kmtp7(slbAiK0z<;D6 z6gi|z)LM?_R88)}L3iPB8W|lzT8EH6Rg1Go>nzeIY9NspAaMcGAWMdg4cYxf-6@^I z+NQ7$WJD7ABi8s4Ye7w=o}E_D&Y_XZSJwC|YeDIBu6;TuBdZ{+O2-V&oX|dsGmYaIes=slWt_<~QJf13e36uy$Y;qm-hmBAZ#GYq72D4o+K3+1Qxo1ajXY>t6UV3tXDp%+{D z?O>m6C#>y{0solxA#4S`njp^GD1KSF<*t@zR(8T1A~@R!4(EInzlOehDjRwn6o4nd z>ULup6w9cStwbWf#-MAAI^7rZ8I;ec)009bLYYXN*wcSi z5D%P17g=>`U->M`XVs~Fy=T#TR-M|DJBQpkb>c83^6MPB&Z(1CAbB?&tSgdkB*%cm zeYW=nb}Ttt@3TJi^yrsb3%n{{0RIJ;5Mc|>s>f|OiqPK0mGe6e?Qe-?oCdYipfM3; z)vaSsRv#!7fJ9C+k?vu08ipA9X?3^4KqIhh1ieT+LWG6~r%3maK7{aUh!lJ+ z+}|2Fo8?EX&7bm9Xn7PFA4N`NKA0s!!N`Dk-7%r7hCN)lxHImnh#geKjwLL~GGhrT zvYiBw$RBd(A!kO`*`Co|G=K0Hfx~Vn+fC=kt!N4@`u1G_E^xLNIGhMm{BCT&m>TD5 zV~2r4G^r3RB`VonuLi99HqBlb46rN$y~%Qw&b?Z6_-3cB7>MRJJ7_cxPvc$DJ1`;^U z1gb=<7}TkAV@-f!WAff&v=B6T0;bLSb<8>b0CGmtfd){|`9$u-x;75^51 zFlZb`C%E`^;`OD$b9-W-3GhwS!wxGmLw8Q*3cv+U^8#l~EcbWG+T|`~PXxdV%Dm`s zcLlT$)D9ih3j;Msw+7jhh1%Qx>*9^ut$Smj8t~P0=c}Gl>+JkwVQ;K=PXPbq-yXIK zpY6W1s%Y8v%T3sm_zxUaL%S^4i-s!qq2+z(Kwa~5 zX!;y>rmp!ABRj-cQ`hXzSo$*#)HO#kogx`~;$`rml(9@^9H?u~VT^Ma8|s=5AoByr zjvA>SGWSDv#7OZ5hh*uyI;)#!;*d@vuUuB!0wd zQCI$z6@O*5h{Q{Fy@@E;+uTPicwDpFunQH;ZRmCzE~9iBG)q&))1X_LGM*02(v|Ua z=$5XGXF#(IWjq7AWl)?Plkk~CaXCVR$#_N_&lpgNrZTcrrYn_bD$^^K8A0iEMv~5S zpmYVRQ^A@O2^i~#=hj|a^A}d&*GTs@Jq`sKq%8L;j{A!dw9>VnT}G9yku`5*@r{e( zCz0>u%y)9F^p&>zf}Fy2`(cjXf$ewj7jZc5tJMsDEa@)*xxhRZbR)pJD;`(IY}h0K zKalkg)Qc?d^Txy7pB(uv0I|>{mY#`CN93#-b8y-K40uDCH#Jl{Q3s>OzrG8=c~0{@ zHSNm}5BJL(#16zjIpE8wp{6jJ+YO3m2*5*N^$_$SqpB$$w7g*B76G^f`AgIjee>Je zp&L7S5C&?{=o)$)aUYk|&Ak6D0q}sl2lc3zCtA3<{PPD0g~p-OMeRE(ik$dPYcPHz zkh2Y>2N=(?fr%pwy@p`mAe0@X6SQBx#X>ynx&WMqCg-V0X8bd5#57PP0CAjV9A{0u zMdu^;MnrIxL;t+NpQrQiUmpL`T0=Sv`UGwDxO054W6>v8hgb!~I120Qe7de@k96sPWy;5&?LPM!rUKh$J5Fojq>J zwfb?`-fjTV4In0+V;>DfHn_0XKCj>KQ1t{cGR4Pfnd%ym|A)Fz8~FRnJt=*0dG##n=>M# z+U2b2lizmo3=A{@SrZL*>IN(|I=GeBKs1m>12b}KS66+*LV10IR+x3aU}I`H~k)lZ8a?rfI?tiNL@a;b7$0AZrbVob{A^ih1N6z zxs1e@ktw;hAzF_;iQ*cbEDKgp)aw|UNIc0DpF1Bq8*g^SUDp898hQ+Qw_9@Zjs6oi zyvP5B;@_|><=}yk2g0>viXV=o5jvzypF!x1GJO`Iv&wW9LRrdmB8w7PC4M(KbdzhP z@3{|Zc(>MmE;hqwfPbcZ+-ribcBHaT>yvM4w)EiK7-((~=89f>v+-5r5W-KY4!ALTc@lsZ&l9@z-WQ&x zX0% z0PX|+KHX~;j&`5!Jl{tE;vpYT9c%q>^C8_{BnrTLVEG>OC6iftX{6ehOLqkz9H4NZ zMXt9WUOO9Z;J0@v25zw88#GoRih|cbQD;Kez!MGkq-$^}z2T!~Zr*at;T|x#2U_n- zH)jmEer2U-1zsE<)X@hyP=SOZ6pHkS0In6k-V?s5copVw9LkPU7Z;JG-^TrA`_&kz z0DJ|F?M6nn_urpzPyl`b*)OV6m$tQClshI~0D=Ju2JJ|FiO6kDSMRqS*I-~T)E}nDsyMKL!!lZPXQ@KE~RKXk-*bZYm0l*E^9Gx3i78YuN&2s zH>$+r1t14l=FlCaCv!4lTwtmIJOE}7Xi}{~YCP7lKfewG_ksC+>PPnmeVk!l=dm6O z?+EI21ofxF^GC=ZNr~`oO^?_kVtm(Q4*Q{OKNViMhWfMwb+-)|I0jkwSINaa3iPvQ!kmth#~ z03)67a$Zb`08}8|3VNQa)EZcrzN=6Gf}wFR-AD$!=_<__BHM(4J&@l+Sxxd-xq5zG zHvx!-d^FYZCYybEvzw|m;ZdFdRwqC|QqCkqyN=h{;bfG-F`pggWh~r`IotupcR*KS z3~j-VcXQ`0I8;k#Ow$<~Vg$m)ez%uwAL1PadgM!XBB5V7 z_Bb`Y#Dg|I025Qmh zT6&&I?w%9#W7#_axW~1>M{fvXyyto}^!~mb17VzI7-vc1mPPxUkep%B!L=;`2+Iq+@gcDpf$YNT6DGcEEB>^7evZTDax z8+6JB-N`cL$QFJbc*R8kGC7q@PK#tgPL!+n%O7a77XvYnkD;pZ&+7e`r4#A}AP?|) zRCZMz65bdmO?*F{@q7nh=#Y&=>73zB1qt z7U^@qKc})5&Kd{tS>tr8BVBBKCx5qBpupoNF#So7cwI)l&Zu~GPXIEYb_Pv%r>_gT z7~H2?0HQeCD4JMxbwVRvnW!GdKpwEnqxsWRgZ_7t@7Nr{Ks+>wr^n}j#`}fn@EQRK zf_xA)8;8X1gVTI_cwr!nQ3+#oNN%g=4VTwm@149c@D}iIsWA_?wESJV+3^?#ej@Fk z$e66l{8Niplyp_`!9Xq2t);p>n(4kptISmZQlLo+9To9e8fqpyy}?=&r;3xb zY^P?{ft;oUdRQ|(T?zE!W_BIH&CsG7p9i)(jwNss@+YarkPZ|8b_z%wL(4^o!s0+iQKZ^x>+%8)70~h#Qmu*` zDr!_|HYAws9n6j)Ru;dxDQ~HcJFa{9Pi=#Cmgs7ypbsbUWXk|wMiUUz+qYGp&}YRd zY%G~fhfKzdjvDDf+C^z`|9u5#D((mzGk|FZbwhy4> z0kj|iKE5P?k^*Jg8;QM@3oC+RsE>Ty!voh00s_2*h0; zzLak73rE$tTEWW#5XRYt(S3vzjAIQ|REtv$^*~x4$c%ayUnKTL?J0c*X`Ml4)Z2t3 zaX4yE>10-t%u0#BTy?;3Mp&6g7#_hlVD*hE;@M@ZimMze!ZDB!%<^e?@cEvT&7>UP zNDQO{J{`1H=Mi^=Q!9${bz*M#d1aCHLrBU+toEO3c6=IvP7mNT zB48ZW!mcmjM3R@lXJw{~Co_)ZX7CouI0Z4&$WDnvbEaD~GlianFEdV;nQ1g^&SmU! zneoJxlUxr@y(voP$qdSD+x9*Bu-yebXAiEu2j@)O-Y2h%;wV3J0VrZEirC)7o4d4q zwR-V(jTj8nv+e8IzsQDj_-lvA4OMqzu%3Ma{3lw*BUCk6e1f>jUGbZkp1Hgx#UU1R zs0F6ARD~_=Uid-BJfFriVaad)T7!}3{f%EquO8(5jsuN40yZ5*f~;Q94~9}F0ls4C`Kuv%D4#2Kn4Xt;Wv&sb-gC3_M2YG14aMzS1}5)XU|b0uaqsH z9spl1yRUn619$x#SU#ucz~7@$#nAZQ(&kG^xEuKwh`zu!^k7kf zPzh>7w2SFqU1WOgjliJ_s#HM(;>Y(I|C%|X!_rg?xI^BZPB%WHjpu52cL8_{OyANS z_WD-iGtRDu1mFj-{z0=WI*SW$b{do_0LhR~rdgJW8#AUKOh11M1M!?@JdL@@7j8JD zP<-A-Bt=pl7*KrFMu2;%{a*Lknspnq^5<;*X?@$NWmn{ODTM-%08J9;VSRSu42=eH zO&SKiLc_1niiilz^YA#_T{Rs8d7Mff*M_)$4Vx2x8z!t1fGWUO(Q}Djd7Rx4ahL#v zL*sCo2OT-r;^3Fw@dDrtd2cGji&p!W>o^U{z(6H1t)xQy5qd%0Vq5=A4E#Xo2WtH^ zsH@ic4Od=l6Mzelzd(g(eoOw+0-O|pOVH#JonXnx<->+Qj>%*NTTscP9!02`R>@bU zvssmFR-4!cj!rn0Bb+wTd||^-IY;R$o6PhcjP$*Odn^SurSvljmzB>>Zt^`R040Dg zp;6kHO}WR$R4mTIfDe@U&@O^Dv&Qv{T}Ql^h0@lhnD@k21`6pZ@N&^{W8qiN=Sz>FzN=dbRVjYsqi@ZW%p zXqb@A>X89ks{!}05*_zLTe9(-f;r7#&YXxVW*!^rx`(@mfplP!POnbL#Vk%m{5J;s zcZ`zPE3~V3bH7~7;Q*8!pyC=)E~_>>xWeTso{G_ssl(s&KlnhOvA z0fGPu0_{jNM=n*cswh4hrmAur^*WBG5|jSiZ$rWdrxnjJ>qKahNIhV~gOeMs4mm3T z-&mt>Y*!N5_8SwD+d+2acTe*uD${cL9Hw?tp>uHoN<)y%T_AkUvJ% zwfz0Sx0{ECmSf-v8vX>y$t;)Os-2OPSX7QR>J~t^Kx>@^PE@jJ7p=n>d&2E~<%`7? zn8Q)XAEm6xi4w1s;#+SrO5t*K_z`-SeRab2Nin}x2^%qS{W0`QkM==z0#Jo? ztB@TTT;hub{` zDQ{j@V;~UU3ehmBVUoJksEQAO68$%IFya5X5N)(Ywx0A+bZldWQ;tH`l?N!+$SA@f($1nSLejvH&DO zlO*ar*x%pocb=9e0D%An0v%F2@y z-U0p{khM%h6gA_+mBy{#>oM?+?fH!zM%KK^=4wdyOGb?Fk4G1ezUxKeJ6@NQ{`mp0jd>nNo{u5_rQO7XkM7DdZ z=SUojIud%!)iR^2+suR>Dgj?f9ZA@usaxAsg$h6nG>M_B5wzps#bFQpK4TyS@+ow$ zeKr16zWIB@FBqsm!zxhgcd;(I4ezQL=P&p|=ijBK*(TSD!1Vy(jUuRCM6I`04Yd0^ zIa>gtARk5ha9ns)eVyaAuUPvVSxF;nLPoyAiI*~c42h2+L%IYZ z$TUQG2`(V<1!PE;;P4pj{O|g+zT*+>g|fX=!w&2}e7fz1Cn^zV;;|6f1yt*uTO z7d_*b&_e_?j-ZOSWAoJEDhp;bV!$8D{At&l^H$tK@B071z&R*8M*-r7u&-DAxecQA zI8b3UPSB)mhQW>$gV>7#hccvFMqRq`%_Xy{(Mti?3HhCL8_{s~D+-*h`wIh)fY~FO z7@m6Tnc)CO`(Jo1OOb9VP4);@vA&r-1c(+aU8&zDXPrtDW)THVqNvjPdrTSM-g9OX zW)aKT#?qKfsQR{zqcl9!PE)k*cN3QRB{2FDm`^vHEYvm&SyG3ei&S!zH-IcwC5vtV zBz(pPWW^sHA%beYefh%bE)LCjjNzPZIM@2k;F<-?-GoMK*UVe{CnMuZUJ;4@01C1- xyDbv67D0=ETt2Zu-p=NH9-8CoHdi!n#r);MMsA{RX4t`4YSeL_ti7q|e*ls=5-k7# literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_impl_glfw.h.45E4F0EDCF23819B.idx b/.cache/clangd/index/imgui_impl_glfw.h.45E4F0EDCF23819B.idx new file mode 100644 index 0000000000000000000000000000000000000000..572ad7d9a1ffbd890c6fc4f45c1f4ff451767c17 GIT binary patch literal 2434 zcmYk63s6+o9mWrP_W~C$$bzsJc9#VdkX>M9UuRbl0RxCwk(V-vZ5CV*4X7-MXzGwM zK@_Y;M3e>%@+bu2019n@22fstF-mEpJQ9nmW=LC<)R+L)^l5Kaetvon5 zHaYy?xTx$4CvNPY%d}#cowThP8Ka9NSR{ACWrRQ{9Xl88M zZ|&8wvDtS00WJIH6uk52idzR-!sq*@%M6)6r=*ero{iIbK~33VZFJd{j`W2FZ{1PA zFA$tAducWLUB}$|qV(&M{&??uuJ@+rW52g}aQEQO0k6GxSG0?b+ zf2hdW@}9%%eea&ZF1mObj!R<$o_q5OK~|NGg~Zq4=)Xd`z5lo(>2ONM*4@~-h=`{l zNy@1YI!qX=h2>#G!A0sLt|io~n(TyBIdLIN4LU-4OlCUpLE5 z{aUpkbo?n8)^`mlP7Zn;-xYcbKNDFK+gPq~nEm_SoV~fq&dO-LrmNf#aM?#V_cZ9c zH%BhLN&ClX#~b674YoCYi@&VNY>5l9d&3EcIe!=!j%KA^-~dp+yWd6;IcH6RH2|oq zaJ&llfC5UO1cW{2Dpy!QJkRU?^vO-rd`lokm0NlsWkopv)JOCngY4G6k(`-qcM?d^ z8l9GSr=<{zDX|O?wzfc{M3%OS0a#^Q=u3jt$kKq}4VV{HQc4XVc(}3PA8}OqP7>zP~0MXig-7H?q{>cpciTkP_0H1@vam?R?y~F^zni z1Z$C{1j9=(F@zM91Hzdwt%C7B|HxsgJ(UFh$TGpfCpex^O{rCYaNwFO8CR!2|8}HE zPXYXhv#+&>u!=@ z9a>}D5+Ap8fk9M|A0S*vH+qfWXl>#)n;vBGLY77xZ^Yf9h!UZ}*<+@uc=hU44If_n ziUfYha*2yy;;w;GN(uq7l4IIziin?BaU^%2?CMj3Wi@nQ;}10G!-XnA#KSmZ1S@h zh<`%@8L~9vcrz}9Zj_r@T0o9I-H?j@*V%Mh5T;BrGh!3TAxueRFbTC#8^#`lO(ps* zokhmPCJ|vee+*{RhcKNv2D6Dnn9do4*_0tnXN$pXvJj^8#9%f}2-6v2Fq
D(~* z6H&nv@?qddJQ50kz>cASghC)RQz9TTQ*OY`Oo@TmOi6&mOi6*%Ov!-EOv!=VOeuiE zOeulVOsRm%OsRp|OhEw6lm=+blon{sR1gSSR%}_&H0X*j=Vl9HOPGqmU^E+*U=te; zG#iz06MI)A6e&f)%dLPCjU;S2x=^Z-hAr0t$~VZvmop0$8x-N}t&mWuRc>a}LPFIB zRXBT*Bvh-_A?$6F5GvqCHWDP%C^f-soY?xJQ_-bmw$E-3VY;XcX7`0KT}}qGiAI<% zB!k&AAxxK$!R&Dmri;g5wsr{9Wn*xT{vF-Yry(cJz+d`+mKl#WmL=WvwLo{g1WeEW E0MljBn*aa+ literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_impl_vulkan.cpp.258735B3A169763B.idx b/.cache/clangd/index/imgui_impl_vulkan.cpp.258735B3A169763B.idx new file mode 100644 index 0000000000000000000000000000000000000000..81fa44203a006e87d0f2012457b17454dc230651 GIT binary patch literal 38136 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_impl_vulkan.h.E293D4932B27A48C.idx b/.cache/clangd/index/imgui_impl_vulkan.h.E293D4932B27A48C.idx new file mode 100644 index 0000000000000000000000000000000000000000..13a11dec5fe9c6099a2a955e58b01a02a3335e91 GIT binary patch literal 7970 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

V;Z3mXjO)t2)6BYkj!@U+f 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_tables.cpp.A43F5F055F44B37C.idx b/.cache/clangd/index/imgui_tables.cpp.A43F5F055F44B37C.idx new file mode 100644 index 0000000000000000000000000000000000000000..13568fbc31a0417ca3222a4c7ca471cfb60513dd GIT binary patch literal 105700 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 z zsR}E+%ULURmH<^&s)_(K&LgJ)Tcao5}?4&O{;iE5b(F??|~BIeY){J_~^@h)N&t`NcU0c1W5H$seUN&P@o$EM{G^3|px{|@)r9qwzafZ)ZXyc~GK&`+31uxh_+(cQI#WBnLO+_6!2Y#}(~ z2m;S-)N@-1&P9Zj?qdRdOo#*vrYe!D5<|c~$1Rb%C5C`~j(ZYyPYS`C7FhR{6|Qnh zS59$wZH`JW^3g>;9+!Uo`wcNi4(5I&n@M(1FYTa)V^IXs)Jz(Y|9qYyVxFNpwksfs zl?K;JLl^+djO;QZPxeY8MqL&G zmeAA^ngt7qMJB@{lLzZQ=xwu2RJMr)D~ls0!x57QD?>=4kZ~3=p`exDV=VUwC*s2v z>ac|c&3=NFnqXyt^(w<^$gp~_LWK(5Z|$<*ItXayl~$_K%HrKjByr73T_eCc8@0~H zV#^nj*lN>lwYk9|nj)L7$mYg-q)4LFrYj{ty^X53vAFvoiKTYkQo9={v=8jM2X;4J z%^-;t4tj;dhb^`UOmOy@;2ha!`q)OBW5QkprZ~G#aSjDGheT($MCTA-VBF-SHaS^f zgvfR3a-D7fik-S*ryFmOph6!z>BmkV07))7$>jrPipg$jvYQ1?;kHx| zU>Qd*<9whe)o^qT=fh(gy6trjb=}jv4nrWrOJ{g}cwvUX3Lmw?#{z@K4WI6Y&y9y6 z`O*El$I5QVlB~CT?(XpWC-SN&o-)QO;}Zf`Y2#Iz1G|jX3|~$7)7==9&7vv?9*BH5y#LjSXy2n&Zy6}(zC4&Tv*Hd%R_Z1 zHJ`}?)?xnL!~8hBeBU%R({rKB()3Hcr**w&2(A&l?Jc`p{396u5j=Rbb^pBo*UzFS zeuV>?l9ocDCVH#R;NFRw+oyxnQOQy0Trm~-chB%!aHl6(#fJ`@? z=?;MJo$2n7Nn}Nu=y6ZpL2pQ1Q^xQeS_0|Z-F+L!8_B_$B|uGCe{4|aUSG(*nz5Q$=x=QcFk8fGau7NJQ9P20C#B+% zR6HqFOwUF~8rQ+RX!D%y=Q)RgbzzuV?BawjTP1L-gpPQ_qv6z^u`efn^@aT9&IffpAJpvsJL~!O7QZ!aZF<=Qs%{4e zQ*xovQD_VRi^3@jbIRfaQ$?x7luABOm1WZCGU*cls--d2(id3WLHB}S=0;A&sp8$z70p?7=hrbUhqarZ6BJc?YMb5adroHU`Qf__QgT(DYaDQXG_-6>TNSSVQvNaxLG2rY zdg4AjpkD9)@U3fK%9`Qspboo(hJbcbMTb?prF>235TiKDs{TvvEzMiq=?6s5uw4%tb8>Bz4vQ!Bv0r zQHMLdGH3s*#joS!XWl2d_emmtL?v4)xCcxhgw^*Y&)+m5Bn#D+2Of-HQbV)rU)f3>Rg}-Tu0Im>a)%Ag@xmh8JG)+XB24+qu(tcE=7($W6Uj2~0M4;DwW?`Q>80?bna(Q;S1iW{I zE;;CO9CUerZ|7on=f&P z#^fpv>!^&~V)xx*Z)~_kV5QV@r4$CfDTdZ5hFl_{2uh^=yY$YVN>RN9lB(~`yw&`H9v3W3e8@VcLsOsI>nOxwLHqCh+R{}5%|PF6ZkGgoZJGCl0aCA654LWb9`y9*zJYi$a=>%TFn!B;Dt3lw=VAu z)_A=&-Y8JmBA*WO8$x-46;V!{D-sSDS8p z&g3~~YJ-o8wR`usK@0wy^S!KG>{7qCOZ_!ChI2||IYnd>Pqz7!ZEZoTPqw|DZ2JU& zGI!T9_Yg3GmAStvbN_+COG*E$IDPxwALP1O+zSpVPuwkL#uoupjx&) zBHP{(*a&7hEVCS5Ts5fBGXFPa{$Fvwp~wC4Z=tN`Px4F5bN*|d(}H!FjI-XSEjGXZ zlRWGk3i2N!qPngSnH6F%c9O=kyj(}sB3&(txQ;%_8!~tL*XsYupQ1sYgXmLx9A2P2ckx7|^~-)4fVF5PX1K?dQ2#7>+Dg`)4kS z%m_qwYQNm6ET7Ta?bMD(f5i6f9eUs$`gPoMkjx!=8v09W0ni33yTK{~E89lvdmF7^ z0+3+)Ho^8g7)%mu(^@3hl3DvO608A8I6NTnTz5PEN#-!EV`Jdc}o~k@g6~D@)*WcS#+Ir*9 za;uxkhtDJu*C+Gg$x5Dj>~$o!x|1&Yq|1xj)}D#~t=Km7-mh{W+Nic~B%)n!Rx_K` z0{F;9Zl=gB-~kq0a>G9!{mpK+qug(|vGizn@i!~_Z5PGtB3kA%t??PL1iGoS-_*g- zxhwR+D~Oz-tMv9&L;#Ir`Vq$nPn=`=?~dt-FeE4R(I*s33wn?v2Ky01Fj&`=7$Qpy zFx_XK$(3gc1JRJKnp{^+VQ*+heBuj^ID^VaWbBDVYRV+8brRPDOoJ(0Sc($V7hQ73 zA2{QO`LwS42VM6!C+d5&vDbg5hIIc;_5h33iLp8uGB{pmjVBCt@w%SzI+z-MvhJhF zM6i{O#+Dn6Fk){a(;<V7M&CFFI1@gpzePV#e zW8?LRS#2VZ@BB?3-im`Fi-Tay(&C`O#X-;wv)m9}Zs-oaZobixPlU%hZ|2XNLxI<6 zjk#kDk^8E~Jgmk%3P{8;OdR2tTE~Rf5i8$P3sXu&;V82(WdtaZ6k?CQjbSce*oeFOdJu(AU?pv6JS}O$Fe|wVAxq6V3sRUr%|En z0}<;39e_kdfT>U<;*I6woaS*l}U^7LVHRxq0QI(6S9?X(k@~miP^2+;0tdp7{iID|8faG*V%ar8e!L?JoCbv(lL0u^(V zYk8E51mGm+K1nRePjYQf{-0E*txLI%;F41A?NSa#W-Q~{m2usGL>V`vjC%uvj-A?U zdUA^SUGBN746ap%D6rtZYG`-WFc9~?w{PzIrOkz}f0xI!36yaHk?ySA#Fv{wzr({*kao{77>y88>?;l)iav4MGW1JSy#umi5JuYm266GG?-p$*=G9`Mt{ploZ& z@A5wWX)kr!>jN7O72b#nZyx|Ic?VwdzK>(Qp-Wc!sFgk+(1|Yjs7pQ{&}a7Yy1l#z zmiHHU-349*i}&S%Zn+?03p*86Iic(9b4~4Sy_;K4s|CX!glg)Ss!_fjqnr6F9{ zoP%(3`^YV6=>z_hTlx*V`-a^ID)^Rt=q>vQ0BY?6Ywbe;$Yp!wvV8%lXS>(4y#RR1 zwtPxNS9;36|CHGEn(JDIoZVMKXZK9*t4!`&pp=veA!R~1{?u*7$NA@bPx(`>uAM4s zr^*JFf4fz>-KysGpz7A#_-o^;n?$qyZLxMvxB&oTzgc@4Jy4Ytw19=y(OS!WO6Pm2moV=ZZH0M|{=nAa>X z^&0q>{1Pd4E`^A=dPwpdQanXBM${*F?eP0wa)-HVu-;Vy1b@A2MvvDfY-{@ErYiKN zl49`dcXOrmI~SUYpXKQ=i%94^&(nUMr+GXM8O8A(gKz&O8&V2X#sVTv#aUhVvqT!+ z>3aKgBIevaW5_-uj8}fq#awjxu&(|?$A#`LN4W8_gKwd#OQC82_*gl5cMf4A%rn~Z zh#a^}rFKiDop*#?Y2N!ggQvv!^z=OescQ+LOda-uAT+b_*jw^_r zmokUD%+V1~T^E*o+$HpD+XT6u=lD%?{C1qIC_AF|^wJCMC&(7q+#qLe5DZwoS|zSl z!6P=ADlwA)1*-4@B0&9h%66SdDiEjj#c5%-%Y8=MKBE^;Na(i3W_Ph!0&~bk3w=?E zV}vBeyDj709O(Vk9!s@{!yDU3;+$kTCvjk4&J6U(RMemMcl*yy&KWmB9$VJx!q@6x zoWSEc%W=Xayv!c4%pL>U_aveFB%%3?+VCk8EPa0Yx~VeLRJt^!RgL<}{MPf~=85uD zbz9xsAGfU4LkGL9ZDTolU>pa9eJ;J^0&4Y(L-n^&Y32epzIg z(_)b!rv+Aa3VvRiIdK6SD;t3?3#Q8gJlwNTv@I0Fft7u=Xk0A@unjAw{G(Uzm;4?p z_oY4hu6y)DfYIZW6mm-HggYkwK@OvusG$=z%}#6bwFccDL-YAf1a_KrJIx$u+)o|4 zrw$H>zd#a|E?uRI!-L9T!TaZXp3|{%RW4@UUCexgRi}YJoG@-XdOucfAP4Ba2k242 zS?VDD@aAdyjh39CCYP289bUm_e)CpCIm9nLo(DuvB3j@o$78om5g8r z>{55zMSy&@KVPwtAc=f+*L=m?hCq=zqDb8u6k4S2T0}^kP=}sSw*nF;)ZI=HcUhiRN$k`P+^J;yK;XDG z`Z#gPO>L)}N}z2daZ~%+P3=fV1XdY7U1j(TfEq*R z8bfoSZ6q<<*lD)0GoC3BSZR!2X>3j&hCr4vI*R~H=%^)hTig$jL@nK}mTr$Nj|kkM zr8{(h06p%|FA?D5J4()lrc3_3L;nRN?$VKW=_mq>ysMZz(IwfYsBBYP?5l{tJ`=ak zIUlhr7RA2GegJ(;t9BrgtIJoYWIZoM$}e zm8BE{_n2NM&Pg|>ae9P z)({bxV2zq!ZHqm^5U8}atF*SqmRba^Sw~&7js_sXHX_0HDgep0LCLnk0Iah`t+Tbo zPL!yOt+wc`whq{d5`iLHbdjwCcA`X})D~SzfO=b0y{#=)U6I66d-PI!2RyYS@W3AZ zz}^8*WC)}?x~D5~J`h;p=(IwKZ;C*c^W`jOAMDeCz$RzZCTCj!a-Cgrom~Mac19OF zJ75<{bV-$Sbd~ch04_R5Ty(xlfFCb9e*&P!>8Wvg0jPC`);e1O@Yvbuv9mJ(Nv=*w zuFlvI6Ww-)OW5HOu_prp2VDLGE&+QoATZe-HCc&fia?qB<1%GDN1(#}aRmXEa^06I zOEDy|jO(;a$$5&vajw^Kt~WMUAW*|~s^K~VkmY$T%QF&y>z?5ZDul+7lRp!SBf-OM|mMnJh1o@2HG-6jc_11KJM` zXkpyzZMrwN={~}PJd()M4b0QM0YIXDV4@y^1;!5+82^KZaCAwg^_NU*d<(GAv&s6_ zChMmF?6UfHS;GOi=8tP}%|Ew!52Mq7cI(cZ{Cu+ff}4Y^n}eLj&N1s+{`T@E1mc@Lxb1N53mi3%Qdsw5r9q#HlUX#3=S1i)Sq95x~}+Q+L3!L zr^xr4OodEVlo$jy(98x}!gc=(zpV79t-7i53g9G1o#ZUIO(RgoQDp=u6{%9uf}f=4 zKkT-*hI}wp-kMpiids%&0^6^O-mmHg7MP8y-x^huU;y~NdiP#p{km8G!(RPw;PdR& zCnI;KG`OL+*J#^ogc-NaS?F_whx|Vl`X2(Ex6tPauuKYBCPiZX7(IQ4BxVo}LaQZl zH37;cx?EzxC8s3%6ai)h=ve_4tmw)Eba{ZqE%($^>h-<*)=ZVpm@ZQZ%ZU7K`wilL zLnLT*6KTst8fNHtZ1z1ihvQifm2uPNyJ-sthQ!A<-(y=io;r|3qs!Om3df2q0-HGB zCN3O&-^(7~WluOBqL9P`!S_H2$D1<pM-3yLMS$dS`V0z8v zb-4uC$J6_G8<5E7>3jkd@N@wI_Ve_90u=IeAps8X^Z^C@+PA|ybAMYhO&;GC*o_P9 zUaTM?u*N=Yjr~0go_nW6Oxk2Z?hoiNNl=d|K?A{Tw>^m3PUJ?-Q&D*;m@M_Cp1P@L zz=*WZKM89ZrN9+t*{dkEQFEQAPkDfF^q7ozqbc0HX zs+1Ve4dw@^`2hxx&*+j10qR14!QCL?hxfYA{w!>|+{g~8m_vjwP^qD9sgf-q@0+;s-@^^sUxU?xIk1~pgjPy0wJ>k&3>`yZ8it|n*$Q4 zql!ROg>o80>oGn4CE?rY^7tFa2IAPBSX;?;hYJI8w>DKtmC&b37zkQXj`&lK_$z1< zx#C;7;(Gv8iq=Ze30lB4(RxjEf)nNlPhc-UlD5}n+UtUTom)B2 zR<1jqf0w+*wC-ZAm@b<>!R{Dk#dtluz`G(?^0J>x^-*GQL01UGi z_>LF&0l?(CT~1uOY+M)~A9QJ*O~7=Et}HxOEni!&9%j!t=zP{IM3=Q;1pm369J>OXd=6Ix*7EFz~hmeYi% za)lT)CUfaB(w~*sVhjhP?CE#z%AIk?N_AeMc!7-8N?~ zBR)`n`anIU`Jf+4Ogn4yj-^dM<(Rn~b8EbXG56fbYkxmKH%~q>c+g25bn3Ce34s$% z{)96CK)Ext+}Rp{3a75ZX#$|i8C>NICqTO@XGZ|4oxWf@Yoj& zK%>vl=wk>ls!<7vJIi&b>Xa>zDDMkDx5qrU!|kzZwo5hpGH$;}qLz)UB?A0CWZOMt zy8?-)EcaB||2{qBolp0ii%gW;@icYhG<9zxdo+=Ih$NnQnl^bvl%*oNO%dG(n<-Ek zTTJv8Qvh42W#IL1@x}ms_K7d-iQ*ai>)!MStvm-32O-hJK?4pe$10n^kB5VP0Z&+| zYE!8=!Xk-_sx}u1kf!ma5uNn0rsZS8H9u3^DwD{>a!~6(sJP~%LQiSioYKP7ELGZ& zDlI&n@m%}GbHawTRmX27B2cI6ed$D8x;=Vf58);`!Qh`@Xo)u}&}}OW-6{;d@EnT3 zS%dE^5iqXC;I1)1d&5RB3jN#XmXmjYs z*NqX^35i-`L@fdC8N=@pm)ti-+$SWS7$cq#pwy%KPy?6jXw>`*&93-4b zE->5$;+W-3t9_=`4Mwe5R?jS}06?OZPqYRAIA9GqV2uFd+F`5Xu+;;=39Iph)dIjJ ztM8Ik0)zBon|HBI1Yjx~Je3UxU3EGeJe>fgY@1Rd(04i8rknr^9G(RZ0jv$PoP3ru zfX(};K6bkJovr{jt0Pe4au>OHti2*I&&|yv+@vzyA(`$7JQ5>`HSW+ggd^z&cb5(B zmw-f-Tc{$^nbx?SHEu7EIL>*Fa{^YaP@!kJ@Ut8|RezUr-{p89(ZEGEaM1uHd7_g% z&8B%&Mw+L0nx{XI$nvz!^27j8FX-w86IgET7CY=FV&<=sB3BVUuo+UH3~3-(Ok_$u zGo`)&tdU%6h||`kl2ocB2t^O}NOC`tnthTHm=Fk=5P*l9mj&F*h*Kf!0^B+!yt+5^ zk1whh97yyboBD;Iffs@X;SmghC6s3gu@@an_l>3d;qHkf(6sT~-n>FXU;+Em0%ggD z0Gdat**>`SAyC79U&HyLowo^*cyO5sZ*ZR88-tY zaaW4ID|NtYN(43qx@`({$16qzN&>+pfe-?`RuUMA!KST)-Y}X=nx@#rF4tn0i1))* zKK%FlwiN$-Sqq4zjIl)Uohy3g3SmsIp^Y`P5AXS+ORmtvuFzuvn9dBE&b*BcQ#;OQ z#{cxy=kw*MeT~7hMhQnW*!{7&=;6uvvVOgt32Y~vqjxZY9Rw(5LW`LWxKwmWDaVv5 zfl%uHHQJio*ODYJQj&F+WE~7Kd!7k7Ph`ufuym=g48+SOY=@Lr*GqWQ5*|iWt>^iA zz8h9Yif$~5G-v*nEZ=5fkY!;|H?Vw&cZ`g8{EpM*Ac?;nlhEJ#<}lp2vC3=JX?}W| z--Y*D_SbyW@#fuK$uHI#*6Tz(o7r5*Y~o~Nj-Si%i`WlEzLCWJEs40f)OP6qwfNq@ zQe?%e+^8!zT5xYgAj?f>X7z zN7dnAd2~|EoFqW3*BL=xLAiG7&>{gT+B1Q?#gz6n5q)TTh{ zK!9Ea(f|x<9c#-%PA4poTjhAhFrHySXMSWcJW}@3#ExI21|2k~%KJa5W+s&gu5sFI zJZ*MB8;Ch@+S~?!RK}i49C=vC@Cymw`z4Hh2_ccj@M($!?!vMMT_bC1WP|ZbpaopU#Ly)gVqs=NT+ zNijRA0O(OG)%KO@P|#d6)U7fUXDoC{hI(X%`cojWT5VrVT(Z$-+-UP-*O9ytRcp_D zH*2BX2@eN_91d!OZ};7Xf)VE_N75!k}zVDFc$k4U>#dN6rW?JbIkCF z=?;r-hlK^#UT{$tTo&v(D8C&?9ac7&$1c5}J16FO(|eP#bpk6)@We#s#_ZN76I;QqV8!rgNk^p8TeT9Dgepa zR>@i-1)#R$7Hv-;5i9hJ6$av(`R&JzgO0tqd69ggXadDgphCbSml-0o335UTN18iKdwBDW(woR!Cx}tH(~)aALU^UF7Nq zmV1x5@JGtl1iECcS6J(94~;8+?-PF{ZrywOjr;ff(^Hqq^}SLRi~bU!XU8gO%qnRd zR&xe_BjlOBUbj?MNRAkL9Z^!eAW&lDN{nrQ-gCmpolwBNzf6&rk8E2izvv{&IEhF# zyw7ahXZGMR2uT!~{YBOn-U8nKpN<2}hG-8kzvZ%4H=Sap6G7Uq8HQamd;-4nRb%I?#^C^Dc^p}uHn>M;3I%1RVXvjj zE$n;{cb+)sv6vDTD+6lA`i_NDyX2?K)AJfCbPW{=hT63hvzBT;goGs4Q_Ok-Oj8@D zscnSG(>7hr0Wd=yF+<%RgTm9d-qtbStdI@L#Z;SO3MP5kqha?DR>C|Dn@4~GO>lt* z+5n4awn%ZvLWOSf`Zsx7gT9yT?U3#54Zs%ffGytF0ode=+~n&9_6zs;*gZZ8fG0l7 z6D1YKgP$kYuIM#&rCht!7DKhg3A#j$#ZaSwcS^R6d;Iv{t7ONeI4V4jYL2*rz)W4s znM5$61f4%YIZc5i7U)73DDK<{%+U*Ti1gIew6U7z!Me1<9$I1Vf-MN>lDX{Qx$GDK zCOIM}IeKC34@swf3Rp=p1wk~{BaAGeuE zM_22UYJCyF7@yD2Y?03=!7zUXd_2kxJ`192e8@E-73Xz6^g7=b?8s&aT{DC}cy!mS ztX+_Kpl+4i2_LAX2kN%?tj(4`Kf4w2YvU@pBTdoxrf9n0Ul4ev`Q({qJamc6baI)_ zj$q<>;EaCY9F8|2@HUzJt5|lp)sw*Lzj3$@m#TZi@7SU_H>4XL~Hdg)#~9M z)NQ@%k4Svvr>kO^y49)KDsEk-+RIITRkeF-?Og2UUuz_)JAOhZIG2OF> zlhfN7_jW>J2jkvBfMUj1Ohi&R!LTP7AJ9O`8Md4N^-TAA;x0EWoo`xtflhGG()k_% z?%F!swRHzRISsZB4YuyUCnv?;A;sPudmf?JpX+ElSMkI~pw2DUxx<c}ML7M?4y z^pM16E_gH7JP0CCD$u0@i#O2`C==+i{{y1Sgl<6MqS)>tk5km#?t@zfHH;BL|Lnd-9z$mb zS!V}z1m?soR(^~1W$gcb>Z`9af=7O{My}?iD&105bIkO_pDI?an6r0{oYLflaScyL ztNvFwu}8RNtvui_R<~cQ9tc*iOVlqdQNIqrQZ=(w9Rk2+BePiv7Ju7XykeXwcAf0{ zTIn@cdOg6gD9vY1^Lap(r2E|IzF-1;mhSr=fO4O++!p}TVYRPKwXX{RjlQmpzWxLl z*Xa8n06Y2UoqSKA4_EThmBazjbNrj<_z%HkS;fax@x1}q>>s?@|0Xb|Zbx~;{T~t_ zvBh@((iQ+M624p{{Dh}mR3m9Zr!-{_mcNk@SSmzeiTU5e6>C(hnkuh^ia;m+fG(M! z4oy&Z1%p$*Iusqw17D&@?Lvq1063wxqqBGbtkVReqjx}6zp3qs&cFeXs0&7C-~g!6 z_p8y524Jco7#&vw8@#nNkIsz&73vNheuwTsfN^)|KLNN)d(f#c0`$5|j|3py5r)nd zfiY?+7lw|`fLH+WJcEv`;Lm(6?B92;F0pTrm(S1b^m980G`3t;m&-Z;sAkz}Rs!HE z+vzId6;jKNt!2jniH9uxkcEXlVxu0h-GRhYb^*%czN!UANB7U(J#yQ@4f4#AtQ(W8 zgQr7Ej2%l9vjCDf&vZV|494R)?%=Y;eM*pPN)WUyUJ0VFC~j)Fk;6JY)Z`H)*q#b5u$h`8w$2gT;cYc^?RL?= zU33GmKr$^Ld^eW{+Aa;m0Og`4Ft8@@3O3b@3`_ZJsb$nAd3d~Oci*(bPm#yQ>+xpla|J*E3-6;BHnkg7t;I{tw^&fp=^P4`+4x?^|(F9&|r;*xe)B{lG($%?4 zzycmGQSp)VQxJ`BdJz}ATEhKEzMH;q<0M$CSnn=oUm}U>tJZMGJoR(=$ z4y4aWc3P61957mDIY(wW#{kuQi_@~j$pP@U)A6^{4?vF7l0!)Bbb59=TLOtAC?SWF z$LhUY-A=ZI07sqdQ39NBwmaeM3NEQ~epKZg2f#(=I~ScF6CkO@MQ2(IfNGpQYMlK6 zsCBllb#?>bnbZA@@XbqhxszQIFw!n?`4_la0g&n%nCf~JfW@xfi(P{OSmI`vxOuEA zqW4|uW|tCRxqI|-_Xj}YvYWk3NL+QhuMz>cvwV?RzD__7+vQ_-5$R6reU^G3ho@h3 z+sOhuS>UlYiw7Qge8~bY-?YI9%~lSNvxDf(TsoQ&$Oa%}-q;z;!=$ zod7rd)C~gE`E_-E8z^*%KrIo>SR+JtNfW3v0;~{pD+C*sSbogX)3~!JTb|Q%yt*8( z9b4v-Uf)sLv+tAa-sqRipnqis8P0!lpwqz<9FIUn5LFRm1{E=0Esa;V#=|s{NKywU z5kXVWr~_xzZNTCzQR7b}d=MY#g$MfPjMb=&zYWyi1~aJLM4C#Z&EWg~LsS3IW}x^@ zHBnPd=Be{P-v8<+F*i^dvrW`&lNs+YAdq5elVa+Ghd>0@ny9rVGjPt_VWM`J%vfPT z5{FH#4x8G84Xp}OXoaaAj^vCas!de2$qXW7-Z4>kOlG_Zf+U`rsHY|~&}g#ERF>I{ zPZl7F?PhAb*$g%Sj+s5j%)vnCd0=)wFbA-bhc20Ip=Mjmxn~A;*8FfX27y!ym1;4A zJ57PG6P#pxr-`yneixtZac|MO>&#T7&?=q zW^!hrif!bmjhq>473h-P9%{G83{v@)cv_Ws+GC3-lBn{8Re7Rue?_3)+rHl09f!t7 zV5(1;>I(;fY3BI+b9`Z7U@r3ci-=h7GkJO@?=0%r%sv%!`)T?I zECSQ~?rHu2b~8dC$=^E3-w_||N8p&>drS#s6Y|3woUy-oi+sGk&cN3x+7$u|*w6)p z185EVRt-BAf8^A5-v-AHe{YLCJgx9LR(Lz$*WG$PvZ2SDpKg(@;1zYvCR7 z;e$WF-y%;t^(te%$_GZn#Z1IvB@zgdXk?g1#qJxm=uT>`l(I#h7WSzk_NicAnM>yI zOXjwq*PpTsIAwVS^w>-(kVzblE0bO>BOE-drT*2@P`tN=?($4}6=sXg%dwPx*mFiXkM$ z02i(~23L;33nY#jxuZr{MlAhtEd4ooS0IsOW0P#nA=6P6CfmM9wlzDq zA&_EYQwWKR_Es0|tw9+L_QVzq_7v!^b>HE-?|?Uc#4V~*60ZN(Y66U#D z0#M`{UE~@Az$usOlnWLb>vqMuA(-a2PIJRbEOSplaZlplqg5bOL85HVmCeC&PH}-# zTqwLX7j}w+PHU4r5tBX8Tkg7N_;nBb!gU^JoyP;pxa;YA*E0}+r=H-ap62B@s_IGJ zK9ju7?q&$A^Sal0;Rnt33fW#*N%y?N?|I>EA9{@sy$rZZoX;5NgCNz%ruyI~FZH=g zeef>nJd@7Dk5$4CFX7=`7W(;xehJ)Vqks5DKa|+&AHJ0UaiTp=bb(9KMSD5{Hj3Ov z5q6{fBDbFabEMXDBIn)>4sgiPY0l#q$Xgm3BU)t z7O-Daz-9m4?>~6Z>Tg+%uQT+kGYkSc<2Ga4ZA#cGB++1hrNRCxo?)869}V^i&4xEb zo`{{Fh~2TP^o6l!ZvB1t%HJ;Z%QP``nivTtk{Ke8T)+WH5e4J|j-9#DB`M;V6!9Yf zQbhr|fD>Q@asdZmwb&84fMa)WRK{xYQ{(~;z!oudix>&)QaeNw@(Bmvq}Uqygo6of zgJj)6M84i2wcQ{!huTAT$&##Dgv4ekbTbibBwMm(6B2(**1rjmD>-tBqXcH8@XBXBUU!c-<;as$qyF_q{YSu@ zywUjkM&opNvge8L*%RR>VC35pcwo0OhudOXv>XoM48#)&Qt zD1*~oZKe}`QPW++rn|-f?QpIuX0EFb0p6MG`T~FjuJ#LDy#QG53SX{-j@oin^GWAH zb8=*zr-BQsP|lQm&@1k(nkCC~n$)4gL8A``y#;NTg|AN+Y)Ro@+ZkCo)()*M9n3 z`x($%Vs+uMM7GTY-LM4Ra3GPOdp|+<0RRhip$m1b2=M>9`VP1#uI>NlE^M3Koo%x_ zGwkf{jMAIZ?7bUX@-!w!<5Oc|5);$&5|ca|*eDhPO0i3^V#O{>Q4~~=CRhLk1+f#|Fhu09dQEuSH@g)++zARyh-)_+_o~ zR{(5NhHX>A%bIP_IgwK`)2EMBra2JXCYf$+$AX$bsVaz6^u0aYGl7fW-#-ECfOGTWt z(y-oXSRX)_d)m<8}HLy`5=Fy0Z)QAml zL?+jW&2Ge?C)*>;;}O#25jOl0(&P~~{Soqe>eT(}oH-o%UtFT`EYWxY^GK=2vlId9 zwFB$5gMdzR1n+(X?*SBsBl!3u_yhpt<7_?-Z5{b|zkIwuLNO^HpA3MDIC~LkauM%$ z5ouDQqe^r%P^HRr)-oM~0HI|_f|GJx&vIQaKvAJ%D|8$J^r_JG1;8yGbqg8gwvM`u z08#pZQTjo^=8~Zg%FqXk*YNy1J)!qHf#hY2Hua8nhNkfpS5015k>JX!rV&?7BY|9V z)imX*=_3G?n7m4mCMBj3B}kJJ)07gVNjlLho#+iNQZtCq3?d8w1;qFQv`hohuh3}% zOh zU>eySP^6Qy)1Ao_`H7{IOZdN~ovDy{XfH|ab!I^B07CbYVW5{1(prL8ze~ul60#>k zF}#Ev0f6hI^*YkzIvI8yX>y$$ejRC2Lw2trdw@P_$jLS2hXAN2J?jyFta>uE9+~L_ za^M4U5NPs_^nQoL!M-C$z9UBgiX3Wi4mAW=p$aMALd2)5kou&Mng%GYP(fFyU;va* zeM%62>=J5j3H2qQct-VjhQz_PQ`6h2PXR?cwV_KpmC#vWfd3Ft^!riHtd%^lhMv5J z{tz@;L(g7Ae+Ga!dSV>?J_5{&qhYa+1bRpUJrq!EqCeY2&jG+@+P0a7VZQflv+mhu z1C#q=`}oE72?+4hV*Aek2)9oSw|{~FKZM(VM1YlD!ug_&2(*odwnM1JZf&tMK$2~- zhqc&yB0xBwjwu4BW7_WU-0tuKs>4RM_eQo45Xg71p*s)>J%t^e!j1tHsjPo0%Ui81 zUww2l?N|QYr?T@?+3x_5#`>or6zS~O>Fhi}v6CIR6RE^=j`cr>G|6WDvk{<#9bdvu z0DY9QRH-vnBtMT*HmH;h1{7s1RfbTMvEF4!RpvUDsbg(G{itKZ>e!wDc*TakV#5Hi zi2Gm>_iq4%b5uA-BS26%*BLp>Pb`L`Vi1ZL&N~K)m)_1fwj=4O4shH7uB)h)@O@Nr zRHd^5K?e|2=`12N^kGkzBAFz1_0@J*(#=&Hg4 z&5W+unI-9yHC`jWpTGa8V2cfx5#h3d;F@Wfj97*M1?tcO^$hV6smH4~rQg%f)zVs_TDrlXx3aZxW_a0yT{@-ycA9j3* z*Am-nr`r!tTB^2{s=JCW(`;QZ`0ct?$As{Li>}6tt{m_lD{;k3Tx|e2Co`UtLET}Q z9AAdi9Da-8ZxJDXjlsCaz=4~m?FMYSfd;@CGj_&IgZrFS)~>6pLE;YJC$`RttwY=h z%dN(8r<35ViSbG2SG_nUtm zzHwe?h_@s{Oir9l9cS|qw^u*wn){Kz{dHVe@7Hzsb>xz6IfpOj+{NwOe!OX=%4^UG z;qK#t3csL&Ib~02@l!}((KW64nwAB^ccm7q)S3a%swG;r4j@S{)L{#CW&j-2;Rl^* z$?vjF4?o^F`-CvnC78<+3_2NA7+oriX7L65ui!0H=fCxY;A{NQRsYZ#D8K^^vOW#6 z&P4V+uuk4*og4zIRLm+>Utr!@gZ;1uTMd<-IPBv%>{p-|UD1!dqW=n9SEdrZQi%_N z$oiBHdrD6dZ<6?NSK67C_P*lbJ8@@#WW>iOPDuFssgo3W(wW192ToJIr>X9ufUw{} zg`+AX@MMRrM#5G~0z?x9S=slVmu~vW4qw_!l5jp6HUhd5L`%77#5fZ#9ThJfEjH&T zwO+c6|62|lt(WfRp*;}VA)UQL`Wa|eE$vo~SZu1LBdQULO||srYQ$nwEA^^HEH<^$ zF|~-rrdIk}En=~GCuQDAI}_OPvwbK1>Yem!Fpqcq)Za;0z`4GY?%<&m2rY8yw+Qi5 zeJdaLRt{}4Z{@|lP=tbxP;?f2;O7&e2#G+Vdm+7CK8u@NZ?2s`nyQAFwCORQ<|S{lN~bPyjI3BJ=x)#OUmhoFxt6FaEHBv%wHU9Z({0l%4i}#B~%q+3^N3qW2 zEd19!iu)f$0Zl&8u8B>@j0N6CcI}8Ql_&BpWcK-ra#7mA0zo@9^>yl#>av_ z9^=0}#(xFCOWge>@(N$#V_xF#0g6%`Rf<@uZtAF;{{Vt+>Vko_D@H#$Mn4AFvJM-& z9X7fHzn#OzfrpKQ08ni5Dn{&3#ikL(h#jifG^H4^LzS7l%8({yrV(XGlQPqkGNj1{ zB6I^{soFry+dzB+f_@XpjZ{HIDFLK2*%+;||7_#lz^H)xbdtl+_wuu&qh zg@?96sDPMNK+Fcs3W{mu0)em6ON;rX#ex8VFD=~xu#WUu_mA{x zo_i5n)LwG*UL;@VUUJG_XEIFwz3wId+DrZqn4a=UDj%`D<AWH6w(M>_5y7QVOS zzu%IR0L5E!!CUeV0OV3DyW~=W5owZ4hb1E^wTG4@_D_TC8aX^eda0Fv3@ zWHtm?6LztKcdIs zhcsDeViqEWQ8$=^`P6uzNxjLl{vRH8bNSSGfa0Es;Zx&@K=9uaH!p3;>6rUTa>7Y+ zj<}z`Og`dsilNT@&QG;K;#1(v?b88_C~)TVjY zSQkSq0>rtP<6KCf%M_~&#VQjJB(JFq*ASpYWhg;_GF4!ist0INp)yyfNC4bb1>9A2 z2S9_$+JH#O$1u-h7>q?ZgN2AV4l=$VGrW%#eowg;>8rYygk1+Ei1bB{_pZ^2s{~YP# z1!jJMP_$s?76e$OHmp*^_i$K`Rtrk$!Yt{At0D|hZAqd5o zdhJ*M+|#P>Ax-XSJ?TcdpjfKcE!7)gren0t~5Rhatd6b%>91GuykF?F%T{SX~>^M;rTo8w)RX7IC^od`_YN&6_e}Xryq_ zwV2Z_Mu2cm9nNV1ML0JgoEwAylftqZOJ5=0FgTm|%8KFb@YnqS=^e4hFzmbHH0?b{oYPuH(0lXw4FY!8NYf8dnAcP$kK@ zBpHkhy(M$pk_7>SUX0u;M&1ViSL9w-5Fi@!jK+F{qdm^JxJw-G3l5a=;}*~3mfk=^ z%(HmrS$c~aV$N@u=YGAzbXK@qtFTS3uuTV!x|OzZl{T23bG5ziYWq-;;)6?P{-|8< zI4gYOjW%+ljS&Upt*yqEs_##9JgL`N>YWZXMPr&C#0)c>6WWxigUZys#LFeoqA$#( zUgoGrG7+r7c6V8WWp`c-@E%JPo~N1yw@x1^}Bbw)DsomJWtl?brf zu3l|tMTKkMr5P?;y@q59duE*ru?|Tno5*=se)OGWXOzA zXd@IlupA;3eIx#1&z`YNF%wW^V2%vLVj7EgkHv$4%n^%^jYaHXC8V~5)QKtqKaUd9 zr-bwaz;#l49ig~R`dmjSUXjCIA>J;xZK~TgwfI8#PFw7{7P~=oY#8#7AehuVj<`I6~ibwx@0MoB*aK%%sFqI4nv z&M0S{QT_&IcpsZ}ANvh#+kJ2uHRK8_b0e$S$lAn( z$OGFs|LxoW@gxxCBjH}N!j4tgDe+$O*UJ-E{K*C73Ed~z=_ETRa$D2WkE6d|GBMAM zf0Cl;nu0jm9#!a$Doo&&oKshOdcyna0)Bor9u7D z(3#Cs5;9_$auU|Vq7Ze1s3T$rT07KcG{=ssG#Nk#=6#`@2|n7wZi+MKh~uW(o0B z;3=9}&t^6d082RcC7eG1ws4v)93k!_zR4EOa|;&;fUTUzRxUsUvQugL%x7umg)uaI{;2v%_kAy zwAFkX0kW*-ECe`b)t*B-y>0cqZ4Ck5FHKf+lhqD@7gqBN1juGm_>6`7pxBnfY~nK( zra>r|;Vrw&Itb-4NnP@o9S}OtwDBTfxCCTxDrNjjnJ^#%mNEWi2vE-Wmm@$0<6nUQ z8|?ub?L9?4_P?~J>nAMw?t<_|-b$&rQdVT3JdlYKnMfA!3pjJZnP9DU>+(GV>2CQ# zU~QFsNR@rG$Vqu%g~PGJ;R*D}ScfClSvi8INOd?;9iG6s@1VnR(BTQ3sxCMj7aX2I zu`F^piX5Imv3%qh`p7W`=#lZPCf@0X#gB584YYG>+P&{M%J!6Lg-~~JM1v>@+uUY+Tq)8h)pp6|4C{}R+t2kH{GLj33bOO2m zii(=@WPHAGer=WpZ$@f5mtkC))3|22H{-&>pqcrCc$g$HBuRRJ6Fo*15~KPz;7zMl z*42nxN(9k8f|vjz(GL?d4-*SJZ-oB!UCp5zvElgwJGmtBza;6jgRXrwc4*^)b@{?k zeoNwUO9D-mxA~+C5@?95mh|R}r-0?PR2|CKN)dTeRzcHAJ#)F^2S2nZ9wIU5OIeqt ztV&GRG7<~(OsHzl7Z&Yasd2Bf79bBCmEuRG4lv9Gxz`1GZ}B9(6O^cve%t4w@LsoA zh%FWxn8p)rM52ur1^xI_PsZIyS=m7v$#9X3A8>q&W4JiRPYf-OU)Hq!^QOU*>2(Fq+-D&2f4-G+!PK6~=-1OEEyR>zZE$~~9r40N))38t1BxbL}b$VC#u3RK<(f zj`7ktL%Q=Q;oIKf0`G9|iOH(+$K827W$KMWVKF9PSOVgESB=fB#ukW6ji{q%HD_uK-KL5@$hR^(dM3In#tKJ?~Z|{$Ij`nz9 z?}d1d`bIf|02@5vn0>0dqVNb90lxze z*?}ld?YOla4+Cvh=;;;u5MZ3Fw&<%ZzTloUo3v+>y~SO?PvjD%zeM?plXuik{!jZt zrdaq{EsUAYc0#Wh&wcD=HI{m=<`XjurH3g;Kz~b1DBP4 zmz6MI<`reY6(tOJJ5S^1X%@&)4K&+8cV=baJ8htcHqevB#oht@(m*foA|lO??tFgr zMqbCW)*2PHMk0KQ{q3A8 zvo{YsGakco4L*6S z-X~VyTLeV?KoF2GX|$I#4sj3jZO`K#=Mj&PwWMh+=^?I;l-wh_t|O+G3a{vm6ni7J z0QQ}w#j>;(;3*V^PmjXC1dDM8?!LnrkTvW3U332XTYjm)UE&p8;}N~9K>b;Pdah`d z`6~R^?Pn*|ly4tT;Yq3Ok5b#hPR`K&Lrmg>;4x*wWN%8k z-jw))$*xilUZoxl*h;jGiMF|egdQKZ0yEI?Ltj!c&$ zqr`)Yr^u6a&y#hYWIS+HMqHJ_)X-H5&nm=0FIumQ)>Gm~;+yO-==T_CknZNFfjDZg zfpjsiB1ywq%w!9a-Rmhm^C|t6xNZ1hm)K29oGG$Iy-)Z? z&sB=&&bvi^^+XctNWvQEST<4%G9FvoB_7+^X>F5Sd2_7x=g5w3cDVG|;nEpG{OfPb=>A{- z-O){*7Oz7}c*N>Q#OkMsFZTSuL+_rJ9q8z!($=@qHd{d{o{V z2sKjPjsH-u$F);1;ApWuc(J{w_&fONhuclzb`k*whTDe%AjZzbIE{nfb#L?kVB*ge zg8%e#iF~=l4CK<~68Ghj5CCLI24_enimB50CPyUWj!33~&DA7PH93=i@)Xss+GSIO-07b^rF8RO~^ z_>bzlSxA|Nr`-xA2yd_ za;a|Osl@{=_C784!J=`H2U1w~6r{lYAPd6gb6V=wHmAz;qxynwilLQQ3?pk zxi7EQ{&8J6uG&n@2K%-&v{d$P-%N7n_-wLrF*|BHcs z#QWsE_Q_!i{sVIB0l7E$Scl}+LkMt0Zasnk$K<}p6(3cFJfHxNyxB^2U0Ll%zasvZ^3Ikn%G}&dHvkOTy z{n$!Awz>ne#!H+3OIsiDHS+UVYJY#Jv-ng8Fn1{uCb-nToTvJ`^ORhb`1!kk?Tn}t zSny7z?@lGW>p6^Zhp|ro5?8)Ud2Q8m=NZvJT zW|bf&hx02ST(-Nju5|}=gomy{XcWBNzqiM?Ra5yVJ6ybXxWL3)=`P;s z2$1CxnB@`%nCBfA;~hleyWJjDY4{q^NU5BQ+juEvre%O<)!6%UlYo}a; zPa#0It0fzWle*vS<5avjLE#@lVo(fJ~AHHJ%4G0U(p)IgR0*h60%+ z3pM_Qnh;UW=R3{Ad*>nE^|x{3ZN&fnIUf8R?;{?6e3KTue~a@xmS_gth85JfS*(^S2GDzBZ6c{fNs=lWzGxS{vH zq3;eN?HcqG8uT9mAlfi0+AskC@rJ?ihEV|6ZSdS}2mpd}rolJU5CjD03kKf{h9JOt zOANjx2+(TqY&8Ukw+#HmHXD668-ql<3lF?B`o1&M5+*9|{Es_fAx~KB;JW_2#d6+a6R+!eie;qdGBNY;2(X0;+(Lzc>&_yoR}nQ(JVW?S>#4wcDhvSo=rQ|{*!o7gPa{1TNQJ3x zp{Z{D0I=QavEAwqE*KtIeIHnZz@fRAF)n6k07Ni85ljyNY_Z{6Y-UmQ<0tmQW_)3z z#WRfuUfQ^q&f6g#cy052Z3_}9hSaV4YD>T4Rf0hMM2bC;Li6f*`RMcV4*~CqRxIuk ztw02uqly7X74HF>EXBwy#Uud4D!a!jVI0{8Z1e^U1~=`;M()QZ0d|zGpOvnk4|ZFF zZGMC84{_4BN9`LqFLK-s!DO+)GSV;g-7#>PP9wG^iA4!KilFk5U-ec*I#|V%_SdxekQTxmq>;74neRpmMl0l(r za-r&L@mCoS7mstBEV(I+yHKWHDC-8!r&TiDDw!7mmYC@!<{;7khHnyQHpiI*fa^@6 zd3>Vza}ijSP=Aqi|LmqPZjx$#k}A6M%J$E-Gw1E<9ez`gYib-lYaC;M)7&Ojv&mV+ zmx^z(e(t9AMO}yJmxHdz>j;Gn|zs@A^yBCvcnR;`Olq> z8J%+RIfcYzw#hx(oYsH7?N#M={x1{C`@2=f-6{`oC@&(qEh2gXk#Yy&w}Ticz5>3N zc&bl4H4bo{M%%1LTW8{oHM)J{wn=5Tg*SdwW;%+9F9+1>18M^}t`jW*iIxz+D3{rJ zq0)Y`Qy3iBYP7!cop@VVF>Q>l&1nRA%=9=jFKkzJM~t!rdnnO>^`B3V%cm!ZRuR5! zF+IANeh*w6FSL3uwEBV+07=$>BqRmER;&9~t0!Q5*R0*IA&hU0&25d10Vb|e+k{fv z`{MrRhpn}F*V=qV;HR5g){tLeHNuZc#)l?5qwTi-ulJdijsN!~$zn;eaN;`=J=X*# z5-GzYWj^BmzaGx^Xr`*KTse(IpDQ8GQ$IB@s2NgQYi--0eG$=SwRXd&^uSpWr zBlEI+bwO_UCVDP(|Ty;&Z1OOJv@I^8c0G7*4%ViV*HpqHy zkio;@l#DoqEbR-jfD5u!s?sM_3EigB zlz5sF`qyMA@eBmmuf+Ewz)2;35&_OAr=C&H029ko;&}+gRV8s%35$DHDTAsIADtSd zZw-%JGw!H* z-cj`f6ycaI93udbjOmgQpbEpQkid!C7=0VFgC_T}srRuN0CaPKCSM76G9SF3HdbMZ0+7AHxG{im)4YZ6ToJ>Mo47TI`+mYMMJvg}sp=i+I z4bD>N{2SY;$9L*YKma+UCl2vf6;Ap8ik`Tlr-3Oc*61B;3;=TX4kNY0Xahj0saGiy zixfe4Mi74BOxQqV@DZ4YJMYQ)nIsW-5@7mtYZK{$AoI030ShI!sIlz$pSh zh4hg}nDPh;P+TO)iv$CJD+GB30ZIv7DM0{=SA_l*VFqDhsb+nu*$g;Vx>=u&0J&x& z7ZLc&%@fMalYo5x((Lon90-6!QkO^)Kv*v%$wDM~SS_imMJRG;Jcl-cBdVF6+)Pge zl6R~XkF}b>0d&sFokLEf0xMo%H332Uo~?#2SyA74eE#ud#jT>p-D`!@BcJrhN0O86 zV7+%B0fVnt?^kSy=&2zZqy?9-3<+N*s?#~}?NfT^{D#l&29)1bCEFF6ER#JR0~WCpq0o z&L+w|-~HY^|L1h-u5hrebir1-z?>H03OpQ1<`AKn7oqqcV0gmt>+i&TpsvVmC5FZyk8(dSWE9%U0}*-}?!fBvWMT|sm%aZn`=7P#w_I08!? zJpquz>T-~zwK3hTZz>&#^x5I!IG7b?QI-A=T+lpz*|Z7ys|N*9jA7 za0_X08wgS^9pmd%+5*L_M1zjcdiCR>^>s8)nxYt<1+A$3rt{KAe&VngI{Z-oZN`qL~WLnn-L&g zPNx3@2u+s{1|N8*+-E0ZO)Zz3%H<9~Q6cYNA%|IjYvjQ-@_~TjvE1|+X_BcJo~eKp z8rNXSe4s=+dSzldu66(!LqpzbgLyTQhb!6On{4O_Onu3Q50eeE z0I<swOq#eewAF2gRnOaUK0!ZkF)brdk?pLO**>)IEX z^RrziXS>b?zy;UI7Z9M>b$qewOh8dCb1#>{Tdhjj=t@~k0|&jhk)z52a+^``(>tz}kgyMyc83dm9;&C`GgwAjKd zwsZ$TlEov*(p%JH`4^OI>6YwFn9c(i$gUTV+^1Jb$5kYG#C9rRJ2e;>8_v`2=jmQR zYP#WOyWw;m=R3XW#@=)b5nT;=V3XCh$=U;~tvyz5k2Mqk&DL(s)}COoEoMR&Got_y z!HkPw{sVxs40V?A6SZ1?lsslZ9up0(wazpDJ&%&%bNCT3a_Ggp+r_&#>n9XsrP;_cbrQ=46$ zRJ^Jey!IZs;E$X>hdl7u1%HeHrE1?&bx*)m%G7w7+6@4kG{#LDJGejDXu~$zETVB& zTtu$?sqtE)ey!;&`6i67RXoyKfUnhQQgNEJ0J%AbROgUx04SzhiYcRbwEgY1W?<`m zmwSRA!DX50vJ5iX$1>An1XwTs_j)xy2 zPuej3_xr-Cej&kMNMNa@T8yl9){o{X8nDIuUkp6po?yl&m;>nL&v}~XfF{4oCC+D2 zcE=AYmv=3fce)Yt9hS08DGTcn)v;_H3#$!QX)=G#5wbI@|{+ziE4FMz`+wVc!GujyYhrqcS36e&V!YDk4k+AIJjF0T`OS& znsJpyS7ot@o+7cgznInIgXs-|>ewjpZ>W%`3{eK zM<@uCILG!lheWMsvpupA;3`X8WxI)9#(W<|EK!62ZLE8nQxE1TR&m5CuA8We@jxU; zL?XZe&f|bH$IUlCCaw77m#79o{Eahy9A}&%Zj*mc^1o9#%+V-3IlyWTu#`BFuDhe~ zqWY`b6&f-Fj4GXHS_R_UdQJ&3ho=b6{g)tKxvgiSB2zMyrD4N zKqzi0y5B-%iezPIGU6{>t8>)qe1QsbN2j}^a|1xLj&0WY0bq%J?hbOd0;)#a0M~NL(_d1@uB1gV*nt9Lvz5d7mo$CXEKSj#@BIS_lo|IcoA_1f) zl-^m0DfF(MxU08{8;y0LfA!*)HVGg6q)BnoM2ieE z@jr8j(Py*|g(uskf!m!abFxoLWySl~JQT*eC1-BQhk)@`sRpc4%?9FKD)DhD@t4Tu zms=Fy-1j`y(QCbFLcM8^Dd)-m2f+4~D+bAjo5 zg-o|X<_UnMX78ouLE@#~cl)obj#wanB(V8yM$24iGm3-=x(z zqSe?rMIKmU|8t2wx>E@~l&N|(C9c~e1OMopw8uGV5MVlYwB7F@<;BZ&Ou5bmR%N+v zT)A#M0Mc3SbR@BO3Colq5wfN1s8V(`*n6eyoKp640MxN#>)3IC8`QC1*0Em!pp9kQ zkie=o_LDYt8lYIjv5PnudKJ!%3g<=xig0dDIQKaKu5-+FrwNRIAC=swN@vtb2Qa6S z`&{G_KNCZ4XrKM~NLXL-iV^XO&J?{Rqx#2uVU2twtgnT#z=g6A;PbAO=~g1&d!^ZL zrFp1$=Oapjf*89-tzM(<0e=Jn*Dv+oOkSm@%xhhW%W9Hw=Rc{d>M&S~pP=Q?&uTsRT zv|!g<(){a^Mh1L($~0J+Mh}1^xcUfg0P@v&Tz4Kf1EAWds5WXrP(z%_E6x-kZa9AG zStdo6Neh5HlP=HHnP-`&Kr3hRKn|hJL9F0wNtd;x3W&seNte9{P)Ml?DV-=I^L<3q zrf8Z5K#W}-V}})oui8gnwNF_1DK_0Z_=8!(u&i4Sk}>8mXE@A};$n(Ba^AB|w&Ss| zWC~To3suk$;)O-=0?9qNlu|6EI>ma?lS<&UM>Wh*4KyS*S*%UYM26?^pNy7+Pde66 zq~xba$=WW!@1e%EYYlRfm#*%YuAV9`Nq%biHY(rNQ}k&*Gn<%s&4xb_R^)Zp3D;de z1M9rX)uqZ6-tnzbcVD9(4mvxk`PWemAamg~M zmEV>*ZX*E;jdH(6MEBpS@YQhpvW8cU3Z0d<>qYAic747m0>uCW}~Fyc$)o7~k{?rIp( zxta%_Xe>`O3@F0fsi*-cq4w#Dkrm=>vo1fq?;9K(oQpY+yt!fTt)o zTFQ+K*#FN6%QJ!jLicvFWxJUHhLs~0%Ml9$T*b~=Eaxl?a1|@HSV}F7D4g)4v|B9g z7FZ2FlCnfnjJQyEihY!2pEDsk51gVcr)UP)QSQ)|J2V4spYFR^?z=I-#{bOC^3079 zt-`{vZf@;vEO4P%Z?&vP>cwBQS}s}{QSs%QMA~?Tw^&)EXRo4 z&L<=Kr2R|msbE)rAbJ0RMTqlE4 zU5k8T3u3BhQ5aj0TdY;ekX1@}`EXe|@3L|MIOE!t6Wf(Dz>bbny%(pN3V<|KV47+G z0E$!-i&XILdkYrTWeb)B_3%BI{~iopx*x~z<4BC@3(dqAni*inM&c7A@fl$IEhfHL zO#A|_kmHGo@lMYw{xzo%^HZEsEDscszC~moaO}lW6Jx0vU{PP7eJ{{`z#6_v`(LH| z0VkiEHvFc|Cdw3ir?+ggZ`pnTK$FehWD5|1ckh*6{P^4#PX!$;SBmE%IRUaTuPkRF zy6NX9e>Axz;;A5AU6m-VIt7&VwZq#9k9Jq7RM=zFW?>d7Powh2jCrh z$shKTe}cD~LxtubS#obsU2iyxSB7@?*_t)=t7pOydQpj8MC=r+RU=ocCWGblL~DG4 z6s<@kJQAI5hj-fIBcHtf<(Z!EEL)0YBhikPQe&mm4t{Q>G_(>)?^G%MtWx?NptvEm z+(4=^+>rLZfuxAKA)R|e`V(kUEj3mnqf|>ntC3NvrJq$Jqtr?*wa6&7(%!YmD7DhL zwa6&#QvY@&Y3e&^EMG%mGpwO-$mny(7zR|KEEAXI6dqS>`eMqZ@5-O?i5|N|O57qP zK|m0Sm+0dqUH~{F(VdZag5Q}h@ynOMqM=bzB1-B9C}O2VECM7-?TJztII&6EbCYyD zXp%4QmoJA^Giy|q8pLBK86%UiKwz`Gjd8b;TxQR(uxCi_{%z{dwy75YvFoln=&pJ+ zkTQ1aEjxKH_y6zI65Qpxq*pJhp9vf9gh_G2)OozV@rjIbyXpR1c=AB{;RES>aqglm zR**ms8q^039-{4?CvBtEZM27Y^3BpzO8$G}*XIHYIIkwotHZ$5&RBb%v3?|4=fwlY zh3|EjL%quZP2Hy*iqj5@NWyoP?{LX?7=fj#h*K9iuQGq#S*}e@ki8Jb-J??PQ9<9S z)12uv$BC!YD5ANkUfTDCQ0b)H&86JUB;I!LK&6|a(v1{>L(g(1JXkdNh44x$DXx<8 z5S4=9<3Ij%^Pc-FDc|M-6?}mT5ufltGVPa4`vYJT-F*|?Lj#$csuSu%0 zNve4w56C|4yKTO-rsK&L?2|3n9C0nYuRC$6{;s-NX!C~Ay>U8n9!*nzJ>*&MX2F8+ z01tYAq}We1Vu?l;?2?nFpp&LiK%zQj3Oa=Vk4ys|nI?%8z6oJdZ+;Qbx%700L8dRG42PB#bSI!F_Qeh4EHEQD9Z2=We7zk;gv}Q0Hq+4 z7@0|o13&>0RzUOvKq1ksknjOO5fN5|P!tp0iV=!RqFd!ZfRUBRDEmmwKGFydk_V*Q z1H^~tG3oOdspR;U40(&3=vkC43kk{0r9yN6QG}2wp}LCej(?{m)O#gJtZpf#FQqJi zqJg3tCqUj0I^khJhKzk(6egH_MJrkYlpC5J^XIqBYdk=Hohn=CJJjG#d z)L|s+c^>DH$N7nSmZ!+$`sZ;&MPTYW*=HkiB3=vD&I}hk!v!j9hg|SO2#_WZOGE6T z?=b8gMv0q??;}%o$3l{AH?|63LAMe1w0;|0Y|E3K8 z3joUq^D@E{NQJqCXD-o8T&X{QpVhL!Bx@CXg|aJs6QA%v71_Va2@o-#{c+$- zYpY;1L3)LDVPx>;x3taAY1pw9iEE(j>FhjqroItKtUb?%u6 z#aSJDRu=#$3Uyeajs*77Mf$Ks`k^4gtkn?KY8VPs+;EZzCpqvgBS=RC3G+0bCI_A- z#{!BRa&`{+9RSYI>=`-$Nb*@Uoke?!z|9rZ*N(L(-w4Yr$;Fc70vU6%iz*rM|J&#y z+la(_uC>Y5+GyaWzttw&>P&WI*qS+A)|&N3*om1kQznvDqFAjiR-1tcP^#9Is%Zcu zYjnvP8r-~X()exCgaKfSjoxDGCSH(>N3-BoRqCcLb?eNp!;jzS=F;d!0Hr&fQKvJE z=vIYAMxVV{QvF6)_mQr~NLNm@K{ftXkNH%p+Jq~|%YOqpMvZ(_4H8_HshFCnmf!|tsc)jASAp2%Zy|Se+;I~aWtqm!i z_E=?jjMUP5V(@=r2nA-DgT|f*jRS!Y5koLBguBRR_)(&1pJ+M=oB+}EplBMF7fGN) z6X^bcB7vTfKz{*%Yj*WDy9t<7s~tYojv&$RLwNnH?;Rfz;mfKiQzL*!+mikt>_**LVUy3L$Epq=B#5Jv5>EEt|2E`(kf01f1_);%4 z{x39x5yjm<5+4lsP(0xuPYeccDwgt(MLcpI*?K&(4Hq?NM_^HUs#p43fy+jc)<`lC zZ1dNY|7)kN`PCzQTH;oHyRfS-%X}{*zEkCj$>oYK!77VZnxd6n0N7_x?=v`ni_&p} z?zo|wDC+;HO1|;a_@3>;hPdx)xbF#+l47!km47RvU|mY|sW7)Zo+A)^x<( zCSCn)x_Si+x!Fr}+lvHXHPh3Z>HmuQitrz>ghn8_Pj{f0IHV2PW0i5mnCy6FzObvF57&b*ldpZf8K**}H}|5MgQ z_;1HQU3g)`b@AN8qu*|kmUb!YQqkpK{J)+5lsf-^_BWr+)AA4A!^RC8F=piOQB_?= I4;}me0OWM!xc~qF literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imgui_widgets.cpp.2C6A8C974E4B792B.idx b/.cache/clangd/index/imgui_widgets.cpp.2C6A8C974E4B792B.idx new file mode 100644 index 0000000000000000000000000000000000000000..5d1b74860e517649919b8e07a5a3b493dfee3759 GIT binary patch literal 240552 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

#mh}V=OUQ@}vC;pm`c>Q|B>o)*Oy{42R5@lXf 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imstb_rectpack.h.426331EB29A2B17C.idx b/.cache/clangd/index/imstb_rectpack.h.426331EB29A2B17C.idx new file mode 100644 index 0000000000000000000000000000000000000000..e1f9fd89ab8c7e9193a5755cef5d82edfd927b98 GIT binary patch literal 8202 zcmY*e3tWuZ`+whaj;5J;o2jPhGAdo(E-JS~l65b%81J9axuvuuRIC_(B36Vf=^`PQU284=XPP;kcjxo*KE9skectDs=Q+>im@y?V5I8W* zgoRU9Esctuj~Iqg;6KY_mo3?3#4!6z8D>@PfzNJ#b7#0|YuTxdli~T{H=_NQ&il#r ze4qXc9D;wmxvnFlI@3M*@WgMXeAO7VZT&Nw8J-7jOkGeuY}?kej$dBBb9s7K$D{Ti zFWy<){^s1?jRi@Ar0-w7EW6dzd2xueZ{ECH-y5Z6t;{drh}0R%-r-fnypX6ulYT# zda`VN-0BM{i~5xPQ5IH{Jz<1&v?@8(?A;Ik&*rDNJ-z&_zs=auJ6%Sfjo7SwllLUj zF8<4Vk-2Z$-XAa0>?~Zh|IoLKt3I1`?P~X$|GWJD^lAS2(T9nz#t%!$Nxv4o#Qk8< zXA|bchK`(Towuf0@@#i&b9ZyB^i=1lypGkk{R;N;_uPv@w>}*B^&iKtR9RM5OZo?H z`RU1ore*h)9?}Z0+f=Q&-taAlE6xr(;L)}@}!=yGk!ou?3T|Z zSwXusi(T$Z`usk-s=r-~adbfaHPg$nHSD$>2R@k`F*0yu;)%+Bg$)BU9=0r86<$&$ zYm3NgZ#aG=S{*MsZJE>Y-4^f54;E-I28JYO1z!pzNI6$ea?@?IG;{k4(#46}r#jjN9CnZ0+1 zcVXN|2hy7ORg=xfejZ+C?zwMD^p=5Hanbv8#%hYb+dkfxF`lg1I8(_57H+kU*lVk) zo1XHn{?eNBK~BTJF#kEz;`ev^XN&E}jhPo?(JwK^A?M_xX6+9vf;O!v%pcM2=o#Mn zFg?PtJIJR~v8_uZxt*LhP;ogj@bzyozt3#lwc+s0Q$RGac53kw$%y@SX|lYh-hHnBhE4JNiz^8|PS57~}3S z=1@P=O?O_*{(4A-v1Q2OGX=*gUtX`67MPgkE(-B)HaVxMbko+161{fa95*d0c!e{x zx0`f$(icnC#MBLG4=MlRZNrRdN#@Ru?jvtX)mOB?k9}LSYW0*$GrzBua3hmjn4uQ; zH+~#&!DPvqd%v0g-`bCl9&Tx=Nwcb7dEv~|oA>K?ynVN7RX^t6TeeB9x%S_*)V~Q( z^PicfzFJ2%1XUAIiH}ZPRT}~-JYFEXUDUW6I+ZPV`^RfH1u#hvqdTna{ z<)vD!?%rInY*E35JL&Hh<%$}wjBxw%fThW;JJ}O8`zBu-u*WiZXybwT7hso9;RE;2 zkKBWK#;U#YS3!Gyr{3wi{m{ZG8Ak?(RQnuSxwIuX{f9gItUF>%G_8&WF@Im*Ui7g0 z(UpD!vs961yrvHu`0G~zX}ih?t^Bk;(tEyN_e&?I+rL@=ZY!EsH8T?=R;LDC9kIG< zVWaP%G4In(u6^5HSMoTg#P`;pOXZ`y*S?ExGfi5#A?ehq$L}Inm;{PeulVzRr|a@h z&!NI!)8=eV(_VR>x#&&nwXAP{yZEes&eEL6{MA28vP$22$A+2tt2TQ6dwXzlTkGkc z?%52oK6otju(|W!=8g5LsMs40amV)M`sEay+*e+6vh~28wPL>C*()KUk6U?T^wxFXi2Xb9SvA9xom|>+s;;!cDpSQP;+v57f?& z_Vl%hI~lcb)Q|1oMSt6P?Co#EV$H8?ag}~%vSN(&8Ler#!-b^x-aF%`8=pS%e!BOO zWsCiWzm5C(^nK57UdtwTHAf@{&vRRw+ESeS{#NWS&1-Fa3!6^7-Q};Cx?#_t$rVx8 zBGUTEqE_zz@&mK?e5+{r>ZJ>|_hFcV;9BqD{}%RNQQ*gM zFLP`;W*BByLz`+i+&~zy95LSgn0`;1uazYAagZ_Yz6^hY6`f#B420N`5gM`G!SNgx zvBe@)tf-3R41|cl@bk`hS6dB}F#%$RzX3!yfU$uPk%R+7@A$nOj70+Rg4Zk8TlPE% zdy-AdxMMd~jNlkQY-$paCjkdOk(DR14vg5e4>Qh@asKdm>bhv30FD`hMP9M;SF9(m zX60(ulM$N$3oHVc`|miu#cGm_@xUKhZNA!~=RrPlVq`?=Gph-h-5;;y6e~K#ni>cZ zaZj@FJ7BXfSjISGky=((%PI_ni2PNj9i8yu5EcpaXZUi~yc~<@2@!diyR$ITt{jWF zVl6eSyoR+l5F+x|%d)Aq%7Y$+IXUV4JtQlUi zj*#`#|1^g!F>Q&HF>ZM4|z!c zv+f2$_Efu}%=r48W!W;OFaAh1E3al%20}ja-Gze9!{)|ljF^#F%Mn&|1TR)kbXv^u z**dgD5#{*r!2L$dM_6QqXoXqNL&w&&oHq*YowOZwvyx-RVs-~B?_ha;CoA8{@{Cvp zEpen4_^TCczvvnnGXU@CQP%V*YhfV74hL0a=6I*3IvFzvZ&@QSZ3H$3LPS0+`S2p~ zh06t_p8Yt+ijH9|dP4T3Y0-o~0t2Si%Nc8I%U%}l#e1SBWKWE*kK+2Sx4kFrS@0~o zS#G@UK{TwJUhhBpTtGX=48a=I$Xbo_ke-k{G zRpK0^Cq!i2!QjlWM3eVY#v7Y?6M&mQVIX8B_6z-Io)GzB5uMvY?Lysp9)!ItYSHpt z*ncHywN@d|CqY)i2O0-DV~6(qS}lK3NV=9H9Ju#b<9noejFS=Pa{PL@UPc_n;qwp6Ss6(U9G?qRxxl42 zk@Huf>?*YBtwDSPlr;$P3uyWR_NDwXq$)!$#3ZaAsmduPa(FHJLPpn8iX2MON909P z(TpU9Iz|o&$8QJRc3@5|Yy)m>U_fvF)mN`@rfxu%#b*0IVJeaSQ0%0vrgRR9CSsW9mvPU3v!)cK`_y zV)3LS{W9Df^H+=fbu+n{F~^rHAVw0JJ$zkp6Jpflx{A&)ZTNw%8Q z^gfcFT290K5bAdbIS?_OE0yKQlHv;FRDql+u10~?XcEE1K|oA80Kp zD#`|2HZY@D4LG$B=KwC}zt}YgcvAUXz~u`0d4S6k;(Wm6Q%ss3oYVBJ4ylqHuLepr zu%wFffHF^rD}iYxFzanZ_)}ouDKLoQ)4=L9u%=iGe6(N~#SKv003{>_NoysN7#b?N zLur6Z6K=H@25Vs`d6DC;h)SkJv|5GlYM4h8FNz?W>4{C~j>VGzjsmC%s*J;KUfRG z;BaY>rXt)h>3`UtNUElo^qBZXeS=8_AG6ZOtUV3&RA8M-BaAo@zo>6J>5JV!(hZzR z%yaxcXtEEw5=W>0(mXi_)0P2Fh$<4CVr5t`P47R{Ml8 zR|;%PfgLd!CoEuD4y-6X4or^=W4Hz=YJdgh*Mk1Fz=Ptmp#Rzb;-P1O59QYZw>sc1 z#6#+UH^onZ=~G}vc7o%(!O(8tL-8i)u?c!ooDUWG(1KzORB2#eig!cX-O!HWV(3&1 z1s*7dKE-etIQcknvjj>_%`U&Caeq3A@^L!e*vX0AV=iMY=F!ud+TIfRT=Wy@h&;T7NE=L~av<{NV1s`Soyh3wQ0`N`%{-pkJ zxDyCU05d7h1Y9P~S!BN9B>K6g*WL(+hyi3-9IlE^iR>VfNbree2dLSRROwFEbtgNN z9?TBbrGxdR_!aB*iuI%NNx(4)45a!BflHw<8k@lICNP0UM>X`Vh7;*-Xiz^5@*?(; zDGVQc{Rl?SZx=hKi;bs-WJ9ZLIEpAHvl$K({a{A7F&%K}z=~Sf1x&htHF=Tap8(kt zU_-GQnyQ8Ip94*Egg6(P<-*rWEG@We%{RI3FnUg}4-0mI4(`bG5*^7PwJ-7C4_3;yU112V5xs z5>Q+crsa*$Vj~oU!4FXJ1GJ)HPyj6ppo-$%&~mpB?}4g4(4O*(piR+#)1+-N?CryG zX@-^+!uPL3`|Hq&9)!El{;m)=LuoUVQ+^wiw?Tyv+qMbY^LA+8E`0wHls*!^{|t(s z(KJgg2yjoPza1bR%^=-f%pDLx1x@(^2+#U$h^!eAHagm7XoTtFBY9N)z{cd?U6i0Zmz)pIb0*!xX# zX8N6rF%7sY{@D2A-Z`7&BbW%oRVTyf(jt?{3b2}G)Iz+IWp)a22g`H_@hg^jCB#X9 zNdf|W8pLQ&FD6;SM54bf>0L`A`)MK@P7gsf3#(ZZlH54lw!#|Lg!Hi-ZuDU-YeG7I z4$nB#lRy}KzYzEqf^qZ^>;iBXFrl~+z(!y~fm_RMA!L3CDTscKFGI?*{|>9l3L&mWZq>-0$dj8XtX6*?MeRudjtRh(me~xzWzdNV zH&MX64G6N;Q{eCvxDdyY`z+kO=r6Mje!zj=1Vl~3>US5wyFk$WRYOrV9b7p641{Nd zr?(V|OOcAKfYdFiM1LFB>+gsW5#xyv$EP828d6XBu2n$j?RM zT%;i3g|CZ{LoV{5I1h>Qg#0|@kSFA8kgo=fBlawd`g*?3(B;b_mrHej;Z1_gz3zG^ IUuBs82W~;wC;$Ke literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/imstb_textedit.h.421029FE98AFFA96.idx b/.cache/clangd/index/imstb_textedit.h.421029FE98AFFA96.idx new file mode 100644 index 0000000000000000000000000000000000000000..fa9d33e853236bf49ab581183cb432e1f3eefe9f GIT binary patch literal 18352 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx b/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx new file mode 100644 index 0000000000000000000000000000000000000000..eec1bf35796e1a5d66961f22ee46ceda019e5b01 GIT binary patch literal 2122 zcmZ8i3s4hB7`}u{qT%+E;}ViP2ni4%AtFgc0z_LCML|ZbrP9H+f;=inY$Z5NDHcVo zP#r^2YX!x&IxQg9imi{4htwB?+SZDqjyfo{SW%=}9JQnECeE%*Zzg;BzWw)q?|*ZX zlai*4B8bGCWS1kiIEy3*f`vb4aZ&yy`2I;i5Uy?8=X9iqEYz8P!%XVVv`)nvFYPB$Lx^up|QbRlfyR>A-a;yv*%rH?vGDpd*i#NG_Q$VL7R!ZNw=zZ zCnQBDydIU(dU{%|@Y*ZH=f{3oQZqQX@?y)<>}*ACef`eT>;aw|2&yBYSU3 zm!*pIlz9EuYuvFQkA!k=jN1j1aR=IS#9PJebbSy`mXgE??ZtI zCIZtGFffJ@$jBHm`kLbMyTTic(45#HjzU?bK#VL#f%Hn)dm>%c-AEXLTq36soxn`Y zNNOlEL0Te{u;^@9Puj_5%`PjXwY)YINuzoSrQSNa*7XjW>eQ=Vl=em131W-gv?E~A zP%3msVvv}TN(z`|W);%(AhP5{jrq_VC}d$QJfbOJRa=7*dV9;IoRy3AWk4@NBSS5S zUUKGCTEXKd88CAaog^3;n9;qPcRW5nUq}KKtzwWg33!^v=>DDl_o{-^7C@DDxgEPB z`nRbp%9N1>&xR|*am7dwql!@@P48c7yjs`2u>cC$)Ha-c64=dl?A-LhD|4@%byv8jVO)Nscd z5eXu=2wV&j7&yZSWaNw^kcGDdAO$3_23tcAvUY6SRr&YP3RoXj#cB{OTpHOcsvy@v zSK z|G9U*uZFZTOo_wXexbDI>8$uKpaUX<$b?K$AY2yihulGffRuXY!oX_EI`DLAA{+-) zERG(gUTPbWOnZpkj1*Kw-Pl3BnzHSGGa;#E&0yi(ydBUdk|d?b_}F)EoR#ih^d$-J zP_Rz~x;X#bY#S5R4l~1O7+kHo&Wr62*wdYmHuGlOQ(67G zigF9HJmqy3Wjj@#KeT7@J_67>_ZgpmoDLMwM7ATR5I}mgudk3CCGrs~Bmyb%A8e`R AuK)l5 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/model.h.C3ECCCBE7C04E4C8.idx b/.cache/clangd/index/model.h.C3ECCCBE7C04E4C8.idx new file mode 100644 index 0000000000000000000000000000000000000000..87ebf7cafe163223a815293366d4ff3e9753f455 GIT binary patch literal 436 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/texture.h.712506A996DB5236.idx b/.cache/clangd/index/texture.h.712506A996DB5236.idx new file mode 100644 index 0000000000000000000000000000000000000000..5c234698cfabb8ea5644a71a0f4acfc06eabd691 GIT binary patch literal 858 zcmWIYbaM-0W?*nm@vO*AElFfyU|9rFe3v)#hkelHgX+u5O8_ly~l3u zf~Ez%U2AjnCZ9g^Oz%zXqJMjZk_9(jERy@&C0Tq(_dC~PktrLUrq5;$dUI9Lw_sIH z^rs(9=Xzet$gh0MlBKm|%}2Y>&)?i#6B!VZJ^RKXu5JU?*;j5Z_%`+io*{!FR z`Ep&4l>ON4elok?Sbnd#GB?Qr=x(;7r`XsGr!WaIF$n;@0tDQALVRir91xCtds(12 z|2uvmCSI7dpqQW%Oj^?6r(V@&(_kScR+u!G5Eopx@$aV_mcKMh7Gh$9NplNvtHN}5 zi17){%+%{bgLPi3O&gQ;1Uorr)naa)McH z`x+r8W|%aG5Qhp(`un8tJjM23N0^xSAf931X60fPgBs6pn@#KV*^i3vgqV0>x_QNT z;f~Xhc3qa_k?~iEi5(`*BgCT)GrTA@t@tm{SXlTma)QGR<|SCT0fj-~1rvVG{Nv&T z{cnZE;a@u|9|a gi8>IAf&1i~FALvAhbJ*}a&a(lfyKZCG--j@0Q@-Sv;Y7A literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/volk.c.7B12D4D8A514BDD2.idx b/.cache/clangd/index/volk.c.7B12D4D8A514BDD2.idx new file mode 100644 index 0000000000000000000000000000000000000000..eb42a9d2cdc74feecd5880a0516961277936366c GIT binary patch literal 130984 zcmXtPg- z`t^A9-}78+ zeR7BUn@`lW_8T52rN%K&w5(%nI{KRG^BuOdKaVvx?l<$4u*|!u_ebg4SF}^6Brvja z#az=h4gpjY#>%JBnV-y@Gl$;ap@KOM_Bem)t>7y-;7-t7)i9_!?-}u^C@##=jXt(N zcxli?dhBz_vYnMj{wdZmYnSq^-kqlkcQ#M9a&vyMxeM2S_~>wW=sd4Vr(-7OGBZl#c6eL`0_;8OrG>N zY^Z2EUbkmivacS#n$YB*csCW70UK0!ICXU~yV8)t^H5cv>xC^;?)t6hZ+<_^I+{

?T&v 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

Eg9YDPU)l_TfR5> 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'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; + +static const char* GetDefaultCompressedFontDataTTFBase85() +{ + return proggy_clean_ttf_compressed_data_base85; +} + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_impl_glfw.cpp b/lib/imgui/imgui_impl_glfw.cpp new file mode 100644 index 0000000..afa2185 --- /dev/null +++ b/lib/imgui/imgui_impl_glfw.cpp @@ -0,0 +1,925 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ or GLFW 3.4+ for full feature support.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// About Emscripten support: +// - Emscripten provides its own GLFW (3.2.1) implementation (syntax: "-sUSE_GLFW=3"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.) +// - A third-party Emscripten GLFW (3.4.0) implementation (syntax: "--use-port=contrib.glfw3") fixes the Joystick issue and implements all relevant features for the browser. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: +// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn +// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn +// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn +// 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one. +// 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. +// 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) +// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. +// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) +// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) +// 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) +// 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) +// 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096) +// 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. +// 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) +// 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). +// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18. +// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX. +// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11. +// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend. +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. +// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). +// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. +// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). +// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. +// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. +// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_glfw.h" + +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + +// GLFW +#include + +#ifdef _WIN32 +#undef APIENTRY +#ifndef GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#endif +#include // for glfwGetWin32Window() +#endif +#ifdef __APPLE__ +#ifndef GLFW_EXPOSE_NATIVE_COCOA +#define GLFW_EXPOSE_NATIVE_COCOA +#endif +#include // for glfwGetCocoaWindow() +#endif +#ifndef _WIN32 +#include // for usleep() +#endif + +#ifdef __EMSCRIPTEN__ +#include +#include +#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 +#include +#else +#define EMSCRIPTEN_USE_EMBEDDED_GLFW3 +#endif +#endif + +// We gather version tests as define in order to easily see which features are version-dependent. +#define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#else +#define GLFW_HAS_NEW_CURSORS (0) +#endif +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api +#define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() +#define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() + +// GLFW data +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan, +}; + +struct ImGui_ImplGlfw_Data +{ + GLFWwindow* Window; + GlfwClientApi ClientApi; + double Time; + GLFWwindow* MouseWindow; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + ImVec2 LastValidMousePos; + bool InstalledCallbacks; + bool CallbacksChainForAllWindows; +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + const char* CanvasSelector; +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWwindowfocusfun PrevUserCallbackWindowFocus; + GLFWcursorposfun PrevUserCallbackCursorPos; + GLFWcursorenterfun PrevUserCallbackCursorEnter; + GLFWmousebuttonfun PrevUserCallbackMousebutton; + GLFWscrollfun PrevUserCallbackScroll; + GLFWkeyfun PrevUserCallbackKey; + GLFWcharfun PrevUserCallbackChar; + GLFWmonitorfun PrevUserCallbackMonitor; +#ifdef _WIN32 + WNDPROC PrevWndProc; +#endif + + ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks +// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. +// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +// Functions + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode); +ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) +{ + IM_UNUSED(scancode); + switch (keycode) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; + default: return ImGuiKey_None; + } +} + +// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW +// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 +static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); + io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); +} + +static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + return bd->CallbacksChainForAllWindows ? true : (window == bd->Window); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackMousebutton(window, button, action, mods); + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, action == GLFW_PRESS); +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackScroll(window, xoffset, yoffset); + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). + return; +#endif + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent((float)xoffset, (float)yoffset); +} + +// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()? +static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) +{ +#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) + // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. + // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) + // See https://github.com/glfw/glfw/issues/1502 for details. + // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). + // This won't cover edge cases but this is at least going to cover common cases. + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) + return key; + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + const char* key_name = glfwGetKeyName(key, scancode); + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + if (key_name && key_name[0] != 0 && key_name[1] == 0) + { + const char char_names[] = "`-=[]\\,;\'./"; + const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; + IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } + else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } + else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); } + else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } + } + // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#else + IM_UNUSED(scancode); +#endif + return key; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); + + if (action != GLFW_PRESS && action != GLFW_RELEASE) + return; + + ImGui_ImplGlfw_UpdateKeyModifiers(window); + + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); + + ImGuiIO& io = ImGui::GetIO(); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); + io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); + io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) +} + +void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackWindowFocus(window, focused); + + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(focused != 0); +} + +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorPos(window, x, y); + + ImGuiIO& io = ImGui::GetIO(); + io.AddMousePosEvent((float)x, (float)y); + bd->LastValidMousePos = ImVec2((float)x, (float)y); +} + +// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, +// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) +void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackCursorEnter(window, entered); + + ImGuiIO& io = ImGui::GetIO(); + if (entered) + { + bd->MouseWindow = window; + io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); + } + else if (!entered && bd->MouseWindow == window) + { + bd->LastValidMousePos = io.MousePos; + bd->MouseWindow = nullptr; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + bd->PrevUserCallbackChar(window, c); + + ImGuiIO& io = ImGui::GetIO(); + io.AddInputCharacter(c); +} + +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. +} + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 +static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) +{ + // Mimic Emscripten_HandleWheel() in SDL. + // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 + float multiplier = 0.0f; + if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step. + else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step. + else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps. + float wheel_x = ev->deltaX * -multiplier; + float wheel_y = ev->deltaY * -multiplier; + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseWheelEvent(wheel_x, wheel_y); + //IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y); + return EM_TRUE; +} +#endif + +#ifdef _WIN32 +// GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. +// Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} +static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + switch (msg) + { + case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: + ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); + break; + } + return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam); +} +#endif + +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); + IM_ASSERT(bd->Window == window); + + bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); + bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + bd->InstalledCallbacks = true; +} + +void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); + IM_ASSERT(bd->Window == window); + + glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); + glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(window, bd->PrevUserCallbackKey); + glfwSetCharCallback(window, bd->PrevUserCallbackChar); + glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); + bd->InstalledCallbacks = false; + bd->PrevUserCallbackWindowFocus = nullptr; + bd->PrevUserCallbackCursorEnter = nullptr; + bd->PrevUserCallbackCursorPos = nullptr; + bd->PrevUserCallbackMousebutton = nullptr; + bd->PrevUserCallbackScroll = nullptr; + bd->PrevUserCallbackKey = nullptr; + bd->PrevUserCallbackChar = nullptr; + bd->PrevUserCallbackMonitor = nullptr; +} + +// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user. +// This is 'false' by default meaning we only chain callbacks for the main viewport. +// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. +// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. +void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + bd->CallbacksChainForAllWindows = chain_for_all_windows; +} + +#ifdef __EMSCRIPTEN__ +#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 +void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); } +#else +EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); }); +#endif +#endif + +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + //printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED); + + // Setup backend capabilities flags + ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_glfw"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + bd->Window = window; + bd->Time = 0.0; + + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); }; + platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); }; +#ifdef __EMSCRIPTEN__ + platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; +#endif + + // Create mouse cursors + // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, + // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. + // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) + GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); +#if GLFW_HAS_NEW_CURSORS + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); +#else + bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); +#endif + glfwSetErrorCallback(prev_error_callback); +#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) + (void)glfwGetError(nullptr); +#endif + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + if (install_callbacks) + ImGui_ImplGlfw_InstallCallbacks(window); + + // Set platform dependent data in viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)bd->Window; +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); +#else + IM_UNUSED(main_viewport); +#endif + + // Windows: register a WndProc hook so we can intercept some messages. +#ifdef _WIN32 + bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); + IM_ASSERT(bd->PrevWndProc != nullptr); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); +#endif + + // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime + // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten). +#ifdef __EMSCRIPTEN__ +#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 + if (emscripten::glfw3::IsRuntimePlatformApple()) + { + ImGui::GetIO().ConfigMacOSXBehaviors = true; + + // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used. + // This means that Meta + V only registers a single key-press, even if the keys are held. + // This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself. + // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key + emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10); + } +#endif +#endif + + bd->ClientApi = client_api; + return true; +} + +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); +} + +bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); +} + +void ImGui_ImplGlfw_Shutdown() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + if (bd->InstalledCallbacks) + ImGui_ImplGlfw_RestoreCallbacks(bd->Window); +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + if (bd->CanvasSelector) + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); +#endif + + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + glfwDestroyCursor(bd->MouseCursors[cursor_n]); + + // Windows: restore our WndProc hook +#ifdef _WIN32 + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); + bd->PrevWndProc = nullptr; +#endif + + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + IM_DELETE(bd); +} + +static void ImGui_ImplGlfw_UpdateMouseData() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + const bool is_window_focused = true; +#else + const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; +#endif + if (is_window_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + glfwSetCursorPos(window, (double)io.MousePos.x, (double)io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) + if (bd->MouseWindow == nullptr) + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + } + } + } +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + { + GLFWwindow* window = bd->Window; + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } +} + +// Update gamepad inputs +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. + return; + + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) + GLFWgamepadstate gamepad; + if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) + return; + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#else + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + if (axes_count == 0 || buttons_count == 0) + return; + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) +#endif + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); + MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); + MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); + MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); + MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); + MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); + MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); + #undef MAP_BUTTON + #undef MAP_ANALOG +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(bd->Window, &w, &h); + glfwGetFramebufferSize(bd->Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + + // Setup time step + // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) + double current_time = glfwGetTime(); + if (current_time <= bd->Time) + current_time = bd->Time + 0.00001f; + io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); + bd->Time = current_time; + + ImGui_ImplGlfw_UpdateMouseData(); + ImGui_ImplGlfw_UpdateMouseCursor(); + + // Update game controllers (if enabled and available) + ImGui_ImplGlfw_UpdateGamepads(); +} + +// GLFW doesn't provide a portable sleep function +void ImGui_ImplGlfw_Sleep(int milliseconds) +{ +#ifdef _WIN32 + ::Sleep(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 +static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data) +{ + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; + double canvas_width, canvas_height; + emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height); + glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height); + return true; +} + +// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. +// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_selector) +{ + IM_ASSERT(canvas_selector != nullptr); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?"); + + bd->CanvasSelector = canvas_selector; + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback); + + // Change the size of the GLFW window according to the size of the canvas + ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); + + // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) + // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. + // FIXME: May break chaining in case user registered their own Emscripten callback? + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); +} +#elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) +// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call +// by invoking emscripten_glfw_make_canvas_resizable afterward. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector) +{ + GLFWwindow* w = (GLFWwindow*)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); + IM_ASSERT(window == w); // Sanity check + IM_UNUSED(w); + emscripten_glfw_make_canvas_resizable(window, "window", nullptr); +} +#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_impl_glfw.h b/lib/imgui/imgui_impl_glfw.h new file mode 100644 index 0000000..5d8940a --- /dev/null +++ b/lib/imgui/imgui_impl_glfw.h @@ -0,0 +1,63 @@ +// dear imgui: Platform Backend for GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Clipboard support. +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +struct GLFWwindow; +struct GLFWmonitor; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); +IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); + +// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL) +#ifdef __EMSCRIPTEN__ +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); +//static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0 +#endif + +// GLFW callbacks install +// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. +// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); +IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); + +// GFLW callbacks options: +// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user) +IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows); + +// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks) +IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 +IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); +IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); + +// GLFW helpers +IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds); + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_impl_vulkan.cpp b/lib/imgui/imgui_impl_vulkan.cpp new file mode 100644 index 0000000..d499ecb --- /dev/null +++ b/lib/imgui/imgui_impl_vulkan.cpp @@ -0,0 +1,1593 @@ +// dear imgui: Renderer Backend for Vulkan +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. + +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap. +// 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. +// 2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*. +// 2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) +// 2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering. +// 2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure. +// 2024-01-19: Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) +// 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189). +// 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) +// 2024-01-03: Vulkan: Stopped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. +// 2023-11-29: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075) +// 2023-11-10: *BREAKING CHANGE*: Removed parameter from ImGui_ImplVulkan_CreateFontsTexture(): backend now creates its own command-buffer to upload fonts. +// *BREAKING CHANGE*: Removed ImGui_ImplVulkan_DestroyFontUploadObjects() which is now unnecessary as we create and destroy those objects in the backend. +// ImGui_ImplVulkan_CreateFontsTexture() is automatically called by NewFrame() the first time. +// You can call ImGui_ImplVulkan_CreateFontsTexture() again to recreate the font atlas texture. +// Added ImGui_ImplVulkan_DestroyFontsTexture() but you probably never need to call this. +// 2023-07-04: Vulkan: Added optional support for VK_KHR_dynamic_rendering. User needs to set init_info->UseDynamicRendering = true and init_info->ColorAttachmentFormat. +// 2023-01-02: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored + removed a bunch of duplicate code. +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symmetry. (#914, #5738). +// 2022-01-20: Vulkan: Added support for ImTextureID as VkDescriptorSet. User need to call ImGui_ImplVulkan_AddTexture(). Building for 32-bit targets requires '#define ImTextureID ImU64'. (#914). +// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. +// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. +// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). +// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. +// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). +// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. +// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. +// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before. +// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). +// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. +// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. +// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). +// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. +// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. +// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. +// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. +// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. +// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex. +// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. +// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_vulkan.h" +#include +#ifndef IM_MAX +#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) +#endif + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#endif + +// Forward Declarations +struct ImGui_ImplVulkan_FrameRenderBuffers; +struct ImGui_ImplVulkan_WindowRenderBuffers; +bool ImGui_ImplVulkan_CreateDeviceObjects(); +void ImGui_ImplVulkan_DestroyDeviceObjects(); +void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); + +// Vulkan prototypes for use with custom loaders +// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h +#if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_) +#define IMGUI_IMPL_VULKAN_USE_LOADER +static bool g_FunctionsLoaded = false; +#else +static bool g_FunctionsLoaded = true; +#endif +#ifdef IMGUI_IMPL_VULKAN_USE_LOADER +#define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + +// Define function pointers +#define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; +IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF) +#undef IMGUI_VULKAN_FUNC_DEF +#endif // IMGUI_IMPL_VULKAN_USE_LOADER + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR; +static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR; +#endif + +// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() +// [Please zero-clear before use!] +struct ImGui_ImplVulkan_FrameRenderBuffers +{ + VkDeviceMemory VertexBufferMemory; + VkDeviceMemory IndexBufferMemory; + VkDeviceSize VertexBufferSize; + VkDeviceSize IndexBufferSize; + VkBuffer VertexBuffer; + VkBuffer IndexBuffer; +}; + +// Each viewport will hold 1 ImGui_ImplVulkanH_WindowRenderBuffers +// [Please zero-clear before use!] +struct ImGui_ImplVulkan_WindowRenderBuffers +{ + uint32_t Index; + uint32_t Count; + ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers; +}; + +// Vulkan data +struct ImGui_ImplVulkan_Data +{ + ImGui_ImplVulkan_InitInfo VulkanInitInfo; + VkDeviceSize BufferMemoryAlignment; + VkPipelineCreateFlags PipelineCreateFlags; + VkDescriptorSetLayout DescriptorSetLayout; + VkPipelineLayout PipelineLayout; + VkPipeline Pipeline; + VkShaderModule ShaderModuleVert; + VkShaderModule ShaderModuleFrag; + + // Font data + VkSampler FontSampler; + VkDeviceMemory FontMemory; + VkImage FontImage; + VkImageView FontView; + VkDescriptorSet FontDescriptorSet; + VkCommandPool FontCommandPool; + VkCommandBuffer FontCommandBuffer; + + // Render buffers for main window + ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; + + ImGui_ImplVulkan_Data() + { + memset((void*)this, 0, sizeof(*this)); + BufferMemoryAlignment = 256; + } +}; + +//----------------------------------------------------------------------------- +// SHADERS +//----------------------------------------------------------------------------- + +// backends/vulkan/glsl_shader.vert, compiled with: +// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert +/* +#version 450 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec4 aColor; +layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc; + +out gl_PerVertex { vec4 gl_Position; }; +layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; + +void main() +{ + Out.Color = aColor; + Out.UV = aUV; + gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1); +} +*/ +static uint32_t __glsl_shader_vert_spv[] = +{ + 0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015, + 0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43, + 0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f, + 0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005, + 0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000, + 0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00040005,0x0000001c, + 0x736f5061,0x00000000,0x00060005,0x0000001e,0x73755075,0x6e6f4368,0x6e617473,0x00000074, + 0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001, + 0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b, + 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015, + 0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047, + 0x00000019,0x00000002,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00050048,0x0000001e, + 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000001,0x00000023,0x00000008, + 0x00030047,0x0000001e,0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002, + 0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017, + 0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020, + 0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015, + 0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020, + 0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020, + 0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020, + 0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020, + 0x00000017,0x00000003,0x00000008,0x0003001e,0x00000019,0x00000007,0x00040020,0x0000001a, + 0x00000003,0x00000019,0x0004003b,0x0000001a,0x0000001b,0x00000003,0x0004003b,0x00000014, + 0x0000001c,0x00000001,0x0004001e,0x0000001e,0x00000008,0x00000008,0x00040020,0x0000001f, + 0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020,0x00000021, + 0x00000009,0x00000008,0x0004002b,0x00000006,0x00000028,0x00000000,0x0004002b,0x00000006, + 0x00000029,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, + 0x00000005,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012, + 0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016, + 0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018, + 0x00000016,0x0004003d,0x00000008,0x0000001d,0x0000001c,0x00050041,0x00000021,0x00000022, + 0x00000020,0x0000000d,0x0004003d,0x00000008,0x00000023,0x00000022,0x00050085,0x00000008, + 0x00000024,0x0000001d,0x00000023,0x00050041,0x00000021,0x00000025,0x00000020,0x00000013, + 0x0004003d,0x00000008,0x00000026,0x00000025,0x00050081,0x00000008,0x00000027,0x00000024, + 0x00000026,0x00050051,0x00000006,0x0000002a,0x00000027,0x00000000,0x00050051,0x00000006, + 0x0000002b,0x00000027,0x00000001,0x00070050,0x00000007,0x0000002c,0x0000002a,0x0000002b, + 0x00000028,0x00000029,0x00050041,0x00000011,0x0000002d,0x0000001b,0x0000000d,0x0003003e, + 0x0000002d,0x0000002c,0x000100fd,0x00010038 +}; + +// backends/vulkan/glsl_shader.frag, compiled with: +// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag +/* +#version 450 core +layout(location = 0) out vec4 fColor; +layout(set=0, binding=0) uniform sampler2D sTexture; +layout(location = 0) in struct { vec4 Color; vec2 UV; } In; +void main() +{ + fColor = In.Color * texture(sTexture, In.UV.st); +} +*/ +static uint32_t __glsl_shader_frag_spv[] = +{ + 0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, + 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, + 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, + 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574, + 0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e, + 0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021, + 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, + 0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003, + 0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006, + 0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001, + 0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020, + 0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001, + 0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000, + 0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000, + 0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018, + 0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004, + 0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d, + 0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017, + 0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a, + 0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085, + 0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd, + 0x00010038 +}; + +//----------------------------------------------------------------------------- +// FUNCTIONS +//----------------------------------------------------------------------------- + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not tested and probably dysfunctional in this backend. +static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkPhysicalDeviceMemoryProperties prop; + vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop); + for (uint32_t i = 0; i < prop.memoryTypeCount; i++) + if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1 << i)) + return i; + return 0xFFFFFFFF; // Unable to find memoryType +} + +static void check_vk_result(VkResult err) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (!bd) + return; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (v->CheckVkResultFn) + v->CheckVkResultFn(err); +} + +// Same as IM_MEMALIGN(). 'alignment' must be a power of two. +static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize alignment) +{ + return (size + alignment - 1) & ~(alignment - 1); +} + +static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, VkDeviceSize new_size, VkBufferUsageFlagBits usage) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + if (buffer != VK_NULL_HANDLE) + vkDestroyBuffer(v->Device, buffer, v->Allocator); + if (buffer_memory != VK_NULL_HANDLE) + vkFreeMemory(v->Device, buffer_memory, v->Allocator); + + VkDeviceSize buffer_size_aligned = AlignBufferSize(IM_MAX(v->MinAllocationSize, new_size), bd->BufferMemoryAlignment); + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = buffer_size_aligned; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &buffer); + check_vk_result(err); + + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, buffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = req.size; + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &buffer_memory); + check_vk_result(err); + + err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); + check_vk_result(err); + buffer_size = buffer_size_aligned; +} + +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + + // Bind pipeline: + { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + } + + // Bind Vertex And Index Buffer: + if (draw_data->TotalVtxCount > 0) + { + VkBuffer vertex_buffers[1] = { rb->VertexBuffer }; + VkDeviceSize vertex_offset[1] = { 0 }; + vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset); + vkCmdBindIndexBuffer(command_buffer, rb->IndexBuffer, 0, sizeof(ImDrawIdx) == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32); + } + + // Setup viewport: + { + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = (float)fb_width; + viewport.height = (float)fb_height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + } + + // Setup scale and translation: + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + { + float scale[2]; + scale[0] = 2.0f / draw_data->DisplaySize.x; + scale[1] = 2.0f / draw_data->DisplaySize.y; + float translate[2]; + translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; + translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + } +} + +// Render function +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (pipeline == VK_NULL_HANDLE) + pipeline = bd->Pipeline; + + // Allocate array to store enough vertex/index buffers + ImGui_ImplVulkan_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; + if (wrb->FrameRenderBuffers == nullptr) + { + wrb->Index = 0; + wrb->Count = v->ImageCount; + wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + memset((void*)wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + } + IM_ASSERT(wrb->Count == v->ImageCount); + wrb->Index = (wrb->Index + 1) % wrb->Count; + ImGui_ImplVulkan_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index]; + + if (draw_data->TotalVtxCount > 0) + { + // Create or resize the vertex/index buffers + VkDeviceSize vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment); + VkDeviceSize index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment); + if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) + CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) + CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + + // Upload vertex/index data into a single contiguous GPU buffer + ImDrawVert* vtx_dst = nullptr; + ImDrawIdx* idx_dst = nullptr; + VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)&vtx_dst); + check_vk_result(err); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst); + check_vk_result(err); + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* draw_list = draw_data->CmdLists[n]; + memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += draw_list->VtxBuffer.Size; + idx_dst += draw_list->IdxBuffer.Size; + } + VkMappedMemoryRange range[2] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = rb->VertexBufferMemory; + range[0].size = VK_WHOLE_SIZE; + range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[1].memory = rb->IndexBufferMemory; + range[1].size = VK_WHOLE_SIZE; + err = vkFlushMappedMemoryRanges(v->Device, 2, range); + check_vk_result(err); + vkUnmapMemory(v->Device, rb->VertexBufferMemory); + vkUnmapMemory(v->Device, rb->IndexBufferMemory); + } + + // Setup desired Vulkan state + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + + // Setup render state structure (for callbacks and custom texture bindings) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImGui_ImplVulkan_RenderState render_state; + render_state.CommandBuffer = command_buffer; + render_state.Pipeline = pipeline; + render_state.PipelineLayout = bd->PipelineLayout; + platform_io.Renderer_RenderState = &render_state; + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* draw_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != nullptr) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + else + pcmd->UserCallback(draw_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + + // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle + VkRect2D scissor; + scissor.offset.x = (int32_t)(clip_min.x); + scissor.offset.y = (int32_t)(clip_min.y); + scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x); + scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + // Bind DescriptorSet with font or user texture + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->GetTexID() }; + if (sizeof(ImTextureID) < sizeof(ImU64)) + { + // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. + IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet); + desc_set[0] = bd->FontDescriptorSet; + } + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr); + + // Draw + vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); + } + } + global_idx_offset += draw_list->IdxBuffer.Size; + global_vtx_offset += draw_list->VtxBuffer.Size; + } + platform_io.Renderer_RenderState = NULL; + + // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. + // Our last values will leak into user/application rendering IF: + // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state + // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitly set that state. + // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. + // In theory we should aim to backup/restore those values but I am not sure this is possible. + // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) + VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } }; + vkCmdSetScissor(command_buffer, 0, 1, &scissor); +} + +bool ImGui_ImplVulkan_CreateFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + + // Destroy existing texture (if any) + if (bd->FontView || bd->FontImage || bd->FontMemory || bd->FontDescriptorSet) + { + vkQueueWaitIdle(v->Queue); + ImGui_ImplVulkan_DestroyFontsTexture(); + } + + // Create command pool/buffer + if (bd->FontCommandPool == VK_NULL_HANDLE) + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = 0; + info.queueFamilyIndex = v->QueueFamily; + vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->FontCommandPool); + } + if (bd->FontCommandBuffer == VK_NULL_HANDLE) + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = bd->FontCommandPool; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(v->Device, &info, &bd->FontCommandBuffer); + check_vk_result(err); + } + + // Start command buffer + { + err = vkResetCommandPool(v->Device, bd->FontCommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(bd->FontCommandBuffer, &begin_info); + check_vk_result(err); + } + + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + size_t upload_size = width * height * 4 * sizeof(char); + + // Create the Image: + { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.extent.width = width; + info.extent.height = height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); + check_vk_result(err); + VkMemoryRequirements req; + vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); + check_vk_result(err); + err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); + check_vk_result(err); + } + + // Create the Image View: + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = bd->FontImage; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.levelCount = 1; + info.subresourceRange.layerCount = 1; + err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); + check_vk_result(err); + } + + // Create the Descriptor Set: + bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Create the Upload Buffer: + VkDeviceMemory upload_buffer_memory; + VkBuffer upload_buffer; + { + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = upload_size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer); + check_vk_result(err); + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory); + check_vk_result(err); + err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0); + check_vk_result(err); + } + + // Upload to Buffer: + { + char* map = nullptr; + err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map)); + check_vk_result(err); + memcpy(map, pixels, upload_size); + VkMappedMemoryRange range[1] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = upload_buffer_memory; + range[0].size = upload_size; + err = vkFlushMappedMemoryRanges(v->Device, 1, range); + check_vk_result(err); + vkUnmapMemory(v->Device, upload_buffer_memory); + } + + // Copy to Image: + { + VkImageMemoryBarrier copy_barrier[1] = {}; + copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].image = bd->FontImage; + copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barrier[0].subresourceRange.levelCount = 1; + copy_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = width; + region.imageExtent.height = height; + region.imageExtent.depth = 1; + vkCmdCopyBufferToImage(bd->FontCommandBuffer, upload_buffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier use_barrier[1] = {}; + use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].image = bd->FontImage; + use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + use_barrier[0].subresourceRange.levelCount = 1; + use_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(bd->FontCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); + } + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); + + // End command buffer + VkSubmitInfo end_info = {}; + end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + end_info.commandBufferCount = 1; + end_info.pCommandBuffers = &bd->FontCommandBuffer; + err = vkEndCommandBuffer(bd->FontCommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE); + check_vk_result(err); + + err = vkQueueWaitIdle(v->Queue); + check_vk_result(err); + + vkDestroyBuffer(v->Device, upload_buffer, v->Allocator); + vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator); + + return true; +} + +// You probably never need to call this, as it is called by ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_Shutdown(). +void ImGui_ImplVulkan_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + if (bd->FontDescriptorSet) + { + ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet); + bd->FontDescriptorSet = VK_NULL_HANDLE; + io.Fonts->SetTexID(0); + } + + if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } + if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } + if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } +} + +static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) +{ + // Create the shader modules + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->ShaderModuleVert == VK_NULL_HANDLE) + { + VkShaderModuleCreateInfo vert_info = {}; + vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vert_info.codeSize = sizeof(__glsl_shader_vert_spv); + vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; + VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); + check_vk_result(err); + } + if (bd->ShaderModuleFrag == VK_NULL_HANDLE) + { + VkShaderModuleCreateInfo frag_info = {}; + frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + frag_info.codeSize = sizeof(__glsl_shader_frag_spv); + frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); + check_vk_result(err); + } +} + +static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_CreateShaderModules(device, allocator); + + VkPipelineShaderStageCreateInfo stage[2] = {}; + stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stage[0].module = bd->ShaderModuleVert; + stage[0].pName = "main"; + stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stage[1].module = bd->ShaderModuleFrag; + stage[1].pName = "main"; + + VkVertexInputBindingDescription binding_desc[1] = {}; + binding_desc[0].stride = sizeof(ImDrawVert); + binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attribute_desc[3] = {}; + attribute_desc[0].location = 0; + attribute_desc[0].binding = binding_desc[0].binding; + attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[0].offset = offsetof(ImDrawVert, pos); + attribute_desc[1].location = 1; + attribute_desc[1].binding = binding_desc[0].binding; + attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[1].offset = offsetof(ImDrawVert, uv); + attribute_desc[2].location = 2; + attribute_desc[2].binding = binding_desc[0].binding; + attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; + attribute_desc[2].offset = offsetof(ImDrawVert, col); + + VkPipelineVertexInputStateCreateInfo vertex_info = {}; + vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_info.vertexBindingDescriptionCount = 1; + vertex_info.pVertexBindingDescriptions = binding_desc; + vertex_info.vertexAttributeDescriptionCount = 3; + vertex_info.pVertexAttributeDescriptions = attribute_desc; + + VkPipelineInputAssemblyStateCreateInfo ia_info = {}; + ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewport_info = {}; + viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_info.viewportCount = 1; + viewport_info.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo raster_info = {}; + raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + raster_info.polygonMode = VK_POLYGON_MODE_FILL; + raster_info.cullMode = VK_CULL_MODE_NONE; + raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster_info.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo ms_info = {}; + ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_attachment[1] = {}; + color_attachment[0].blendEnable = VK_TRUE; + color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineDepthStencilStateCreateInfo depth_info = {}; + depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + VkPipelineColorBlendStateCreateInfo blend_info = {}; + blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend_info.attachmentCount = 1; + blend_info.pAttachments = color_attachment; + + VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.flags = bd->PipelineCreateFlags; + info.stageCount = 2; + info.pStages = stage; + info.pVertexInputState = &vertex_info; + info.pInputAssemblyState = &ia_info; + info.pViewportState = &viewport_info; + info.pRasterizationState = &raster_info; + info.pMultisampleState = &ms_info; + info.pDepthStencilState = &depth_info; + info.pColorBlendState = &blend_info; + info.pDynamicState = &dynamic_state; + info.layout = bd->PipelineLayout; + info.renderPass = renderPass; + info.subpass = subpass; + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (bd->VulkanInitInfo.UseDynamicRendering) + { + IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); + IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be NULL"); + info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo; + info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. + } +#endif + + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + check_vk_result(err); +} + +bool ImGui_ImplVulkan_CreateDeviceObjects() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + + if (!bd->FontSampler) + { + // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + VkSamplerCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.minLod = -1000; + info.maxLod = 1000; + info.maxAnisotropy = 1.0f; + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); + check_vk_result(err); + } + + if (!bd->DescriptorSetLayout) + { + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = 1; + info.pBindings = binding; + err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout); + check_vk_result(err); + } + + if (!bd->PipelineLayout) + { + // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix + VkPushConstantRange push_constants[1] = {}; + push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constants[0].offset = sizeof(float) * 0; + push_constants[0].size = sizeof(float) * 4; + VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = set_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = push_constants; + err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout); + check_vk_result(err); + } + + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); + + return true; +} + +void ImGui_ImplVulkan_DestroyDeviceObjects() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkan_DestroyFontsTexture(); + + if (bd->FontCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; } + if (bd->FontCommandPool) { vkDestroyCommandPool(v->Device, bd->FontCommandPool, v->Allocator); bd->FontCommandPool = VK_NULL_HANDLE; } + if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } + if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } + if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } + if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } + if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } + if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } +} + +bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +{ + // Load function pointers + // You can use the default Vulkan loader using: + // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); + // But this would be roughly equivalent to not setting VK_NO_PROTOTYPES. +#ifdef IMGUI_IMPL_VULKAN_USE_LOADER +#define IMGUI_VULKAN_FUNC_LOAD(func) \ + func = reinterpret_cast(loader_func(#func, user_data)); \ + if (func == nullptr) \ + return false; + IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD) +#undef IMGUI_VULKAN_FUNC_LOAD + +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + // Manually load those two (see #5446) + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); +#endif +#else + IM_UNUSED(loader_func); + IM_UNUSED(user_data); +#endif + + g_FunctionsLoaded = true; + return true; +} + +bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + + if (info->UseDynamicRendering) + { +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +#ifndef IMGUI_IMPL_VULKAN_USE_LOADER + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdBeginRenderingKHR")); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdEndRenderingKHR")); +#endif + IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr); + IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr); +#else + IM_ASSERT(0 && "Can't use dynamic rendering when neither VK_VERSION_1_3 or VK_KHR_dynamic_rendering is defined."); +#endif + } + + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_vulkan"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + IM_ASSERT(info->Instance != VK_NULL_HANDLE); + IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); + IM_ASSERT(info->Device != VK_NULL_HANDLE); + IM_ASSERT(info->Queue != VK_NULL_HANDLE); + IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); + IM_ASSERT(info->MinImageCount >= 2); + IM_ASSERT(info->ImageCount >= info->MinImageCount); + if (info->UseDynamicRendering == false) + IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); + + bd->VulkanInitInfo = *info; + + ImGui_ImplVulkan_CreateDeviceObjects(); + + return true; +} + +void ImGui_ImplVulkan_Shutdown() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplVulkan_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + IM_DELETE(bd); +} + +void ImGui_ImplVulkan_NewFrame() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?"); + + if (!bd->FontDescriptorSet) + ImGui_ImplVulkan_CreateFontsTexture(); +} + +void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(min_image_count >= 2); + if (bd->VulkanInitInfo.MinImageCount == min_image_count) + return; + + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err = vkDeviceWaitIdle(v->Device); + check_vk_result(err); + ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + bd->VulkanInitInfo.MinImageCount = min_image_count; +} + +// Register a texture +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + // Create Descriptor Set: + VkDescriptorSet descriptor_set; + { + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &bd->DescriptorSetLayout; + VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); + check_vk_result(err); + } + + // Update the Descriptor Set: + { + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = sampler; + desc_image[0].imageView = image_view; + desc_image[0].imageLayout = image_layout; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = descriptor_set; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, nullptr); + } + return descriptor_set; +} + +void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set); +} + +void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; } + if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; } + if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; } + if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; } + buffers->VertexBufferSize = 0; + buffers->IndexBufferSize = 0; +} + +void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + for (uint32_t n = 0; n < buffers->Count; n++) + ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); + IM_FREE(buffers->FrameRenderBuffers); + buffers->FrameRenderBuffers = nullptr; + buffers->Index = 0; + buffers->Count = 0; +} + +//------------------------------------------------------------------------- +// Internal / Miscellaneous Vulkan Helpers +// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) +//------------------------------------------------------------------------- +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the upcoming multi-viewport feature will need them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// +// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) +//------------------------------------------------------------------------- + +VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + IM_ASSERT(request_formats != nullptr); + IM_ASSERT(request_formats_count > 0); + + // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation + // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format + // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. + uint32_t avail_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, nullptr); + ImVector avail_format; + avail_format.resize((int)avail_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, avail_format.Data); + + // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + if (avail_count == 1) + { + if (avail_format[0].format == VK_FORMAT_UNDEFINED) + { + VkSurfaceFormatKHR ret; + ret.format = request_formats[0]; + ret.colorSpace = request_color_space; + return ret; + } + else + { + // No point in searching another format + return avail_format[0]; + } + } + else + { + // Request several formats, the first found will be used + for (int request_i = 0; request_i < request_formats_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (avail_format[avail_i].format == request_formats[request_i] && avail_format[avail_i].colorSpace == request_color_space) + return avail_format[avail_i]; + + // If none of the requested image formats could be found, use the first available + return avail_format[0]; + } +} + +VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + IM_ASSERT(request_modes != nullptr); + IM_ASSERT(request_modes_count > 0); + + // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory + uint32_t avail_count = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, nullptr); + ImVector avail_modes; + avail_modes.resize((int)avail_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, avail_modes.Data); + //for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + // printf("[vulkan] avail_modes[%d] = %d\n", avail_i, avail_modes[avail_i]); + + for (int request_i = 0; request_i < request_modes_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (request_modes[request_i] == avail_modes[avail_i]) + return request_modes[request_i]; + + return VK_PRESENT_MODE_FIFO_KHR; // Always available +} + +void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator) +{ + IM_ASSERT(physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE); + IM_UNUSED(physical_device); + + // Create Command Buffers + VkResult err; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = 0; + info.queueFamilyIndex = queue_family; + err = vkCreateCommandPool(device, &info, allocator, &fd->CommandPool); + check_vk_result(err); + } + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = fd->CommandPool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(device, &info, &fd->CommandBuffer); + check_vk_result(err); + } + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + err = vkCreateFence(device, &info, allocator, &fd->Fence); + check_vk_result(err); + } + } + + for (uint32_t i = 0; i < wd->SemaphoreCount; i++) + { + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[i]; + { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + err = vkCreateSemaphore(device, &info, allocator, &fsd->ImageAcquiredSemaphore); + check_vk_result(err); + err = vkCreateSemaphore(device, &info, allocator, &fsd->RenderCompleteSemaphore); + check_vk_result(err); + } + } +} + +int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode) +{ + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + return 3; + if (present_mode == VK_PRESENT_MODE_FIFO_KHR || present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) + return 2; + if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) + return 1; + IM_ASSERT(0); + return 1; +} + +// Also destroy old swap chain and in-flight frames data, if any. +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +{ + VkResult err; + VkSwapchainKHR old_swapchain = wd->Swapchain; + wd->Swapchain = VK_NULL_HANDLE; + err = vkDeviceWaitIdle(device); + check_vk_result(err); + + // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one. + // Destroy old Framebuffer + for (uint32_t i = 0; i < wd->ImageCount; i++) + ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); + for (uint32_t i = 0; i < wd->SemaphoreCount; i++) + ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + IM_FREE(wd->Frames); + IM_FREE(wd->FrameSemaphores); + wd->Frames = nullptr; + wd->FrameSemaphores = nullptr; + wd->ImageCount = 0; + if (wd->RenderPass) + vkDestroyRenderPass(device, wd->RenderPass, allocator); + if (wd->Pipeline) + vkDestroyPipeline(device, wd->Pipeline, allocator); + + // If min image count was not specified, request different count of images dependent on selected present mode + if (min_image_count == 0) + min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode); + + // Create Swapchain + { + VkSwapchainCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = wd->Surface; + info.minImageCount = min_image_count; + info.imageFormat = wd->SurfaceFormat.format; + info.imageColorSpace = wd->SurfaceFormat.colorSpace; + info.imageArrayLayers = 1; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family + info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.presentMode = wd->PresentMode; + info.clipped = VK_TRUE; + info.oldSwapchain = old_swapchain; + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); + check_vk_result(err); + if (info.minImageCount < cap.minImageCount) + info.minImageCount = cap.minImageCount; + else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) + info.minImageCount = cap.maxImageCount; + + if (cap.currentExtent.width == 0xffffffff) + { + info.imageExtent.width = wd->Width = w; + info.imageExtent.height = wd->Height = h; + } + else + { + info.imageExtent.width = wd->Width = cap.currentExtent.width; + info.imageExtent.height = wd->Height = cap.currentExtent.height; + } + err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, nullptr); + check_vk_result(err); + VkImage backbuffers[16] = {}; + IM_ASSERT(wd->ImageCount >= min_image_count); + IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers)); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); + check_vk_result(err); + + IM_ASSERT(wd->Frames == nullptr && wd->FrameSemaphores == nullptr); + wd->SemaphoreCount = wd->ImageCount + 1; + wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); + wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); + memset((void*)wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); + memset((void*)wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); + for (uint32_t i = 0; i < wd->ImageCount; i++) + wd->Frames[i].Backbuffer = backbuffers[i]; + } + if (old_swapchain) + vkDestroySwapchainKHR(device, old_swapchain, allocator); + + // Create the Render Pass + if (wd->UseDynamicRendering == false) + { + VkAttachmentDescription attachment = {}; + attachment.format = wd->SurfaceFormat.format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = 1; + info.pDependencies = &dependency; + err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); + check_vk_result(err); + + // We do not create a pipeline by default as this is also used by examples' main.cpp, + // but secondary viewport in multi-viewport mode may want to create one with: + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, v->Subpass); + } + + // Create The Image Views + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = wd->SurfaceFormat.format; + info.components.r = VK_COMPONENT_SWIZZLE_R; + info.components.g = VK_COMPONENT_SWIZZLE_G; + info.components.b = VK_COMPONENT_SWIZZLE_B; + info.components.a = VK_COMPONENT_SWIZZLE_A; + VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + info.subresourceRange = image_range; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + info.image = fd->Backbuffer; + err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView); + check_vk_result(err); + } + } + + // Create Framebuffer + if (wd->UseDynamicRendering == false) + { + VkImageView attachment[1]; + VkFramebufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = wd->RenderPass; + info.attachmentCount = 1; + info.pAttachments = attachment; + info.width = wd->Width; + info.height = wd->Height; + info.layers = 1; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + attachment[0] = fd->BackbufferView; + err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer); + check_vk_result(err); + } + } +} + +// Create or resize window +void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + (void)instance; + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); +} + +void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) +{ + vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) + //vkQueueWaitIdle(bd->Queue); + + for (uint32_t i = 0; i < wd->ImageCount; i++) + ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); + for (uint32_t i = 0; i < wd->SemaphoreCount; i++) + ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + IM_FREE(wd->Frames); + IM_FREE(wd->FrameSemaphores); + wd->Frames = nullptr; + wd->FrameSemaphores = nullptr; + vkDestroyPipeline(device, wd->Pipeline, allocator); + vkDestroyRenderPass(device, wd->RenderPass, allocator); + vkDestroySwapchainKHR(device, wd->Swapchain, allocator); + vkDestroySurfaceKHR(instance, wd->Surface, allocator); + + *wd = ImGui_ImplVulkanH_Window(); +} + +void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) +{ + vkDestroyFence(device, fd->Fence, allocator); + vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(device, fd->CommandPool, allocator); + fd->Fence = VK_NULL_HANDLE; + fd->CommandBuffer = VK_NULL_HANDLE; + fd->CommandPool = VK_NULL_HANDLE; + + vkDestroyImageView(device, fd->BackbufferView, allocator); + vkDestroyFramebuffer(device, fd->Framebuffer, allocator); +} + +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator) +{ + vkDestroySemaphore(device, fsd->ImageAcquiredSemaphore, allocator); + vkDestroySemaphore(device, fsd->RenderCompleteSemaphore, allocator); + fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE; +} + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_impl_vulkan.h b/lib/imgui/imgui_impl_vulkan.h new file mode 100644 index 0000000..ca5b4db --- /dev/null +++ b/lib/imgui/imgui_impl_vulkan.h @@ -0,0 +1,203 @@ +// dear imgui: Renderer Backend for Vulkan +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. + +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +#pragma once +#ifndef IMGUI_DISABLE +#include "imgui.h" // IMGUI_IMPL_API + +// [Configuration] in order to use a custom Vulkan function loader: +// (1) You'll need to disable default Vulkan function prototypes. +// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag. +// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit: +// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file +// - Or as a compilation flag in your build system +// - Or uncomment here (not recommended because you'd be modifying imgui sources!) +// - Do not simply add it in a .cpp file! +// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function. +// If you have no idea what this is, leave it alone! +//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES + +// Convenience support for Volk +// (you can also technically use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) +//#define IMGUI_IMPL_VULKAN_USE_VOLK + +#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) +#define VK_NO_PROTOTYPES +#endif +#if defined(VK_USE_PLATFORM_WIN32_KHR) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +// Vulkan includes +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#include +#else +#include +#endif +#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering) +#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +#endif + +// Initialization data, for ImGui_ImplVulkan_Init() +// - VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, +// and must contain a pool size large enough to hold an ImGui VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor. +// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure. +// [Please zero-clear before use!] +struct ImGui_ImplVulkan_InitInfo +{ + VkInstance Instance; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; + VkDescriptorPool DescriptorPool; // See requirements in note above + VkRenderPass RenderPass; // Ignored if using dynamic rendering + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT + + // (Optional) + VkPipelineCache PipelineCache; + uint32_t Subpass; + + // (Optional) Dynamic Rendering + // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. + bool UseDynamicRendering; +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; +#endif + + // (Optional) Allocation, Debugging + const VkAllocationCallbacks* Allocator; + void (*CheckVkResultFn)(VkResult err); + VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. +}; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); +IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); +IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) + +// Register a texture (VkDescriptorSet == ImTextureID) +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem +// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); +IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set); + +// Optional: load Vulkan functions with a custom function loader +// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); + +// [BETA] Selected render state data shared with callbacks. +// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call. +// (Please open an issue if you feel you need access to more data) +struct ImGui_ImplVulkan_RenderState +{ + VkCommandBuffer CommandBuffer; + VkPipeline Pipeline; + VkPipelineLayout PipelineLayout; +}; + +//------------------------------------------------------------------------- +// Internal / Miscellaneous Vulkan Helpers +// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) +//------------------------------------------------------------------------- +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the multi-viewport / platform window implementation needs them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// +// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) +//------------------------------------------------------------------------- + +struct ImGui_ImplVulkanH_Frame; +struct ImGui_ImplVulkanH_Window; + +// Helpers +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator); +IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); +IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); + +// Helper structure to hold the data needed by one rendering frame +// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) +// [Please zero-clear before use!] +struct ImGui_ImplVulkanH_Frame +{ + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkImage Backbuffer; + VkImageView BackbufferView; + VkFramebuffer Framebuffer; +}; + +struct ImGui_ImplVulkanH_FrameSemaphores +{ + VkSemaphore ImageAcquiredSemaphore; + VkSemaphore RenderCompleteSemaphore; +}; + +// Helper structure to hold the data needed by one rendering context into one OS window +// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) +struct ImGui_ImplVulkanH_Window +{ + int Width; + int Height; + VkSwapchainKHR Swapchain; + VkSurfaceKHR Surface; + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkRenderPass RenderPass; + VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + bool UseDynamicRendering; + bool ClearEnable; + VkClearValue ClearValue; + uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) + uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) + uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR + uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) + ImGui_ImplVulkanH_Frame* Frames; + ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + + ImGui_ImplVulkanH_Window() + { + memset((void*)this, 0, sizeof(*this)); + PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + ClearEnable = true; + } +}; + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_internal.h b/lib/imgui/imgui_internal.h new file mode 100644 index 0000000..49452ab --- /dev/null +++ b/lib/imgui/imgui_internal.h @@ -0,0 +1,3578 @@ +// dear imgui, v1.91.5 +// (internal structures/api) + +// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. + +/* + +Index of this file: + +// [SECTION] Header mess +// [SECTION] Forward declarations +// [SECTION] Context pointer +// [SECTION] STB libraries includes +// [SECTION] Macros +// [SECTION] Generic helpers +// [SECTION] ImDrawList support +// [SECTION] Data types support +// [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Popup support +// [SECTION] Inputs support +// [SECTION] Clipper support +// [SECTION] Navigation support +// [SECTION] Typing-select support +// [SECTION] Columns support +// [SECTION] Box-select support +// [SECTION] Multi-select support +// [SECTION] Docking support +// [SECTION] Viewport support +// [SECTION] Settings support +// [SECTION] Localization support +// [SECTION] Error handling, State recovery support +// [SECTION] Metrics, Debug tools +// [SECTION] Generic context hooks +// [SECTION] ImGuiContext (main imgui context) +// [SECTION] ImGuiWindowTempData, ImGuiWindow +// [SECTION] Tab bar, Tab item support +// [SECTION] Table support +// [SECTION] ImGui internal API +// [SECTION] ImFontAtlas internal API +// [SECTION] Test Engine specific hooks (imgui_test_engine) + +*/ + +#pragma once +#ifndef IMGUI_DISABLE + +//----------------------------------------------------------------------------- +// [SECTION] Header mess +//----------------------------------------------------------------------------- + +#ifndef IMGUI_VERSION +#include "imgui.h" +#endif + +#include // FILE*, sscanf +#include // NULL, malloc, free, qsort, atoi, atof +#include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf +#include // INT_MIN, INT_MAX + +// Enable SSE intrinsics if available +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) +#define IMGUI_ENABLE_SSE +#include +#endif + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#if __has_warning("-Wunknown-warning-option") +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor() +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access +#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#endif + +// In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h +// As they are frequently requested, we do not want to encourage to many people using imgui_internal.h +#if defined(IMGUI_DEFINE_MATH_OPERATORS) && !defined(IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED) +#error Please '#define IMGUI_DEFINE_MATH_OPERATORS' _BEFORE_ including imgui.h! +#endif + +// Legacy defines +#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74 +#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS +#endif +#ifdef IMGUI_DISABLE_MATH_FUNCTIONS // Renamed in 1.74 +#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS +#endif + +// Enable stb_truetype by default unless FreeType is enabled. +// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together. +#ifndef IMGUI_ENABLE_FREETYPE +#define IMGUI_ENABLE_STB_TRUETYPE +#endif + +//----------------------------------------------------------------------------- +// [SECTION] Forward declarations +//----------------------------------------------------------------------------- + +struct ImBitVector; // Store 1-bit per value +struct ImRect; // An axis-aligned rectangle (2 points) +struct ImDrawDataBuilder; // Helper to build a ImDrawData instance +struct ImDrawListSharedData; // Data shared between all ImDrawList instances +struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others) +struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it +struct ImGuiContext; // Main Dear ImGui context +struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine +struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum) +struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum +struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery +struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() +struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box +struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id +struct ImGuiLastItemData; // Status storage for last submitted items +struct ImGuiLocEntry; // A localization entry. +struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only +struct ImGuiMultiSelectState; // Multi-selection persistent state (for focused selection). +struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing). +struct ImGuiNavItemData; // Result of a keyboard/gamepad directional navigation move query result +struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions +struct ImGuiNextWindowData; // Storage for SetNextWindow** functions +struct ImGuiNextItemData; // Storage for SetNextItem** functions +struct ImGuiOldColumnData; // Storage data for a single column for legacy Columns() api +struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api +struct ImGuiPopupData; // Storage for current popup stack +struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file +struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it +struct ImGuiTabBar; // Storage for a tab bar +struct ImGuiTabItem; // Storage for a tab item (within a tab bar) +struct ImGuiTable; // Storage for a table +struct ImGuiTableHeaderData; // Storage for TableAngledHeadersRow() +struct ImGuiTableColumn; // Storage for one column of a table +struct ImGuiTableInstanceData; // Storage for one instance of a same table +struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. +struct ImGuiTableSettings; // Storage for a table .ini settings +struct ImGuiTableColumnsSettings; // Storage for a column .ini settings +struct ImGuiTreeNodeStackData; // Temporary storage for TreeNode(). +struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() +struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) +struct ImGuiWindow; // Storage for one window +struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) +struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) + +// Enumerations +// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. +enum ImGuiLocKey : int; // -> enum ImGuiLocKey // Enum: a localization entry for translation. +typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical + +// Flags +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) +typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags +typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() +typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags +typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() +typedef int ImGuiLogFlags; // -> enum ImGuiLogFlags_ // Flags: for LogBegin() text capturing function +typedef int ImGuiNavRenderCursorFlags; // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor() +typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests +typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions +typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests +typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() +typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() +typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() +typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() +typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() + +//----------------------------------------------------------------------------- +// [SECTION] Context pointer +// See implementation of this variable in imgui.cpp for comments and details. +//----------------------------------------------------------------------------- + +#ifndef GImGui +extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer +#endif + +//----------------------------------------------------------------------------- +// [SECTION] Macros +//----------------------------------------------------------------------------- + +// Debug Printing Into TTY +// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename) +#ifndef IMGUI_DEBUG_PRINTF +#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS +#define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) +#else +#define IMGUI_DEBUG_PRINTF(_FMT,...) ((void)0) +#endif +#endif + +// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. +#ifndef IMGUI_DISABLE_DEBUG_TOOLS +#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__) +#else +#define IMGUI_DEBUG_LOG(...) ((void)0) +#endif +#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0) +#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) + +// Static Asserts +#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") + +// "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much. +// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code. +//#define IMGUI_DEBUG_PARANOID +#ifdef IMGUI_DEBUG_PARANOID +#define IM_ASSERT_PARANOID(_EXPR) IM_ASSERT(_EXPR) +#else +#define IM_ASSERT_PARANOID(_EXPR) +#endif + +// Misc Macros +#define IM_PI 3.14159265358979323846f +#ifdef _WIN32 +#define IM_NEWLINE "\r\n" // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!) +#else +#define IM_NEWLINE "\n" +#endif +#ifndef IM_TABSIZE // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override +#define IM_TABSIZE (4) +#endif +#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 +#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose +#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 +#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds +#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // +#define IM_STRINGIFY_HELPER(_X) #_X +#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IM_FLOOR IM_TRUNC +#endif + +// Hint for branch prediction +#if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L)) +#define IM_LIKELY [[likely]] +#define IM_UNLIKELY [[unlikely]] +#else +#define IM_LIKELY +#define IM_UNLIKELY +#endif + +// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall +#ifdef _MSC_VER +#define IMGUI_CDECL __cdecl +#else +#define IMGUI_CDECL +#endif + +// Warnings +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) +#else +#define IM_MSVC_WARNING_SUPPRESS(XXXX) +#endif + +// Debug Tools +// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. +// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. +#ifndef IM_DEBUG_BREAK +#if defined (_MSC_VER) +#define IM_DEBUG_BREAK() __debugbreak() +#elif defined(__clang__) +#define IM_DEBUG_BREAK() __builtin_debugtrap() +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define IM_DEBUG_BREAK() __asm__ volatile("int3;nop") +#elif defined(__GNUC__) && defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") +#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0") +#else +#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! +#endif +#endif // #ifndef IM_DEBUG_BREAK + +// Format specifiers, printing 64-bit hasn't been decently standardized... +// In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_PRId64 "I64d" +#define IM_PRIu64 "I64u" +#define IM_PRIX64 "I64X" +#else +#define IM_PRId64 "lld" +#define IM_PRIu64 "llu" +#define IM_PRIX64 "llX" +#endif + +//----------------------------------------------------------------------------- +// [SECTION] Generic helpers +// Note that the ImXXX helpers functions are lower-level than ImGui functions. +// ImGui functions or the ImGui context are never called/used from other ImXXX functions. +//----------------------------------------------------------------------------- +// - Helpers: Hashing +// - Helpers: Sorting +// - Helpers: Bit manipulation +// - Helpers: String +// - Helpers: Formatting +// - Helpers: UTF-8 <> wchar conversions +// - Helpers: ImVec2/ImVec4 operators +// - Helpers: Maths +// - Helpers: Geometry +// - Helper: ImVec1 +// - Helper: ImVec2ih +// - Helper: ImRect +// - Helper: ImBitArray +// - Helper: ImBitVector +// - Helper: ImSpan<>, ImSpanAllocator<> +// - Helper: ImPool<> +// - Helper: ImChunkStream<> +// - Helper: ImGuiTextIndex +// - Helper: ImGuiStorage +//----------------------------------------------------------------------------- + +// Helpers: Hashing +IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed = 0); +IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0); + +// Helpers: Sorting +#ifndef ImQsort +static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +#endif + +// Helpers: Color Blending +IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); + +// Helpers: Bit manipulation +static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } +static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } +static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } + +// Helpers: String +IMGUI_API int ImStricmp(const char* str1, const char* str2); // Case insensitive compare. +IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count. +IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't). +IMGUI_API char* ImStrdup(const char* str); // Duplicate a string. +IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed. +IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range. +IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line +IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); // Find a substring in a string range. +IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer. +IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character. +IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) +IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line +IM_MSVC_RUNTIME_CHECKS_OFF +static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } +static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } +static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } +IM_MSVC_RUNTIME_CHECKS_RESTORE + +// Helpers: Formatting +IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); +IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); +IMGUI_API void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3); +IMGUI_API void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3); +IMGUI_API const char* ImParseFormatFindStart(const char* format); +IMGUI_API const char* ImParseFormatFindEnd(const char* format); +IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); +IMGUI_API void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size); +IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size); +IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); + +// Helpers: UTF-8 <> wchar conversions +IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf +IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 +IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point. +IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line. + +// Helpers: File System +#ifdef IMGUI_DISABLE_FILE_FUNCTIONS +#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS +typedef void* ImFileHandle; +static inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; } +static inline bool ImFileClose(ImFileHandle) { return false; } +static inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; } +static inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; } +static inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; } +#endif +#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS +typedef FILE* ImFileHandle; +IMGUI_API ImFileHandle ImFileOpen(const char* filename, const char* mode); +IMGUI_API bool ImFileClose(ImFileHandle file); +IMGUI_API ImU64 ImFileGetSize(ImFileHandle file); +IMGUI_API ImU64 ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file); +IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file); +#else +#define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions +#endif +IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); + +// Helpers: Maths +IM_MSVC_RUNTIME_CHECKS_OFF +// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) +#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS +#define ImFabs(X) fabsf(X) +#define ImSqrt(X) sqrtf(X) +#define ImFmod(X, Y) fmodf((X), (Y)) +#define ImCos(X) cosf(X) +#define ImSin(X) sinf(X) +#define ImAcos(X) acosf(X) +#define ImAtan2(Y, X) atan2f((Y), (X)) +#define ImAtof(STR) atof(STR) +#define ImCeil(X) ceilf(X) +static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision +static inline double ImPow(double x, double y) { return pow(x, y); } +static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision +static inline double ImLog(double x) { return log(x); } +static inline int ImAbs(int x) { return x < 0 ? -x : x; } +static inline float ImAbs(float x) { return fabsf(x); } +static inline double ImAbs(double x) { return fabs(x); } +static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } +#ifdef IMGUI_ENABLE_SSE +static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +#endif +static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } +#endif +// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double +// (Exceptionally using templates here but we could also redefine them for those types) +template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } +template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } +template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } +template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } +template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } +// - Misc maths helpers +static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } +static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } +static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } +static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } +static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } +static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } +static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } +static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } +static inline float ImTrunc(float f) { return (float)(int)(f); } +static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } +static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() +static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } +static inline int ImModPositive(int a, int b) { return (a + b) % b; } +static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } +static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } +static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } +static inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; } +static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; } +IM_MSVC_RUNTIME_CHECKS_RESTORE + +// Helpers: Geometry +IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); +IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments +IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol +IMGUI_API ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); +IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); +IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); +IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); +IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); +inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } +inline bool ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; } + +// Helper: ImVec1 (1D vector) +// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +IM_MSVC_RUNTIME_CHECKS_OFF +struct ImVec1 +{ + float x; + constexpr ImVec1() : x(0.0f) { } + constexpr ImVec1(float _x) : x(_x) { } +}; + +// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage) +struct ImVec2ih +{ + short x, y; + constexpr ImVec2ih() : x(0), y(0) {} + constexpr ImVec2ih(short _x, short _y) : x(_x), y(_y) {} + constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {} +}; + +// Helper: ImRect (2D axis aligned bounding-box) +// NB: we can't rely on ImVec2 math operators being available here! +struct IMGUI_API ImRect +{ + ImVec2 Min; // Upper-left + ImVec2 Max; // Lower-right + + constexpr ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {} + constexpr ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} + constexpr ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} + constexpr ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} + + ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); } + ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); } + float GetWidth() const { return Max.x - Min.x; } + float GetHeight() const { return Max.y - Min.y; } + float GetArea() const { return (Max.x - Min.x) * (Max.y - Min.y); } + ImVec2 GetTL() const { return Min; } // Top-left + ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right + ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left + ImVec2 GetBR() const { return Max; } // Bottom-right + bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } + bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } + bool ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; } + bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } + void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } + void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } + void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } + void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } + void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } + void TranslateX(float dx) { Min.x += dx; Max.x += dx; } + void TranslateY(float dy) { Min.y += dy; Max.y += dy; } + void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. + void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. + void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); } + bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } + ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } +}; + +// Helper: ImBitArray +#define IM_BITARRAY_TESTBIT(_ARRAY, _N) ((_ARRAY[(_N) >> 5] & ((ImU32)1 << ((_N) & 31))) != 0) // Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly! +#define IM_BITARRAY_CLEARBIT(_ARRAY, _N) ((_ARRAY[(_N) >> 5] &= ~((ImU32)1 << ((_N) & 31)))) // Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly! +inline size_t ImBitArrayGetStorageSizeInBytes(int bitcount) { return (size_t)((bitcount + 31) >> 5) << 2; } +inline void ImBitArrayClearAllBits(ImU32* arr, int bitcount){ memset(arr, 0, ImBitArrayGetStorageSizeInBytes(bitcount)); } +inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } +inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; } +inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; } +inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2) +{ + n2--; + while (n <= n2) + { + int a_mod = (n & 31); + int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1; + ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1); + arr[n >> 5] |= mask; + n = (n + 32) & ~31; + } +} + +typedef ImU32* ImBitArrayPtr; // Name for use in structs + +// Helper: ImBitArray class (wrapper over ImBitArray functions) +// Store 1-bit per value. +template +struct ImBitArray +{ + ImU32 Storage[(BITCOUNT + 31) >> 5]; + ImBitArray() { ClearAllBits(); } + void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } + void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } + bool TestBit(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } + void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } + void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } + void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) + bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); } +}; + +// Helper: ImBitVector +// Store 1-bit per value. +struct IMGUI_API ImBitVector +{ + ImVector Storage; + void Create(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } + void Clear() { Storage.clear(); } + bool TestBit(int n) const { IM_ASSERT(n < (Storage.Size << 5)); return IM_BITARRAY_TESTBIT(Storage.Data, n); } + void SetBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); } + void ClearBit(int n) { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); } +}; +IM_MSVC_RUNTIME_CHECKS_RESTORE + +// Helper: ImSpan<> +// Pointing to a span of data we don't own. +template +struct ImSpan +{ + T* Data; + T* DataEnd; + + // Constructors, destructor + inline ImSpan() { Data = DataEnd = NULL; } + inline ImSpan(T* data, int size) { Data = data; DataEnd = data + size; } + inline ImSpan(T* data, T* data_end) { Data = data; DataEnd = data_end; } + + inline void set(T* data, int size) { Data = data; DataEnd = data + size; } + inline void set(T* data, T* data_end) { Data = data; DataEnd = data_end; } + inline int size() const { return (int)(ptrdiff_t)(DataEnd - Data); } + inline int size_in_bytes() const { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); } + inline T& operator[](int i) { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } + inline const T& operator[](int i) const { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; } + + inline T* begin() { return Data; } + inline const T* begin() const { return Data; } + inline T* end() { return DataEnd; } + inline const T* end() const { return DataEnd; } + + // Utilities + inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; } +}; + +// Helper: ImSpanAllocator<> +// Facilitate storing multiple chunks into a single large block (the "arena") +// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges. +template +struct ImSpanAllocator +{ + char* BasePtr; + int CurrOff; + int CurrIdx; + int Offsets[CHUNKS]; + int Sizes[CHUNKS]; + + ImSpanAllocator() { memset(this, 0, sizeof(*this)); } + inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } + inline int GetArenaSizeInBytes() { return CurrOff; } + inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } + inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); } + inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); } + template + inline void GetSpan(int n, ImSpan* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); } +}; + +// Helper: ImPool<> +// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer, +// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. +typedef int ImPoolIdx; +template +struct ImPool +{ + ImVector Buf; // Contiguous data + ImGuiStorage Map; // ID->Index + ImPoolIdx FreeIdx; // Next free idx to use + ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) + + ImPool() { FreeIdx = AliveCount = 0; } + ~ImPool() { Clear(); } + T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } + T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } + ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } + T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } + bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } + T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } + void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } + void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } + void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } + + // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } + // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() + int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) + int GetBufSize() const { return Buf.Size; } + int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere + T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } +}; + +// Helper: ImChunkStream<> +// Build and iterate a contiguous stream of variable-sized structures. +// This is used by Settings to store persistent data while reducing allocation count. +// We store the chunk size first, and align the final size on 4 bytes boundaries. +// The tedious/zealous amount of casting is to avoid -Wcast-align warnings. +template +struct ImChunkStream +{ + ImVector Buf; + + void clear() { Buf.clear(); } + bool empty() const { return Buf.Size == 0; } + int size() const { return Buf.Size; } + T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } + T* begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); } + T* next_chunk(T* p) { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; } + int chunk_size(const T* p) { return ((const int*)p)[-1]; } + T* end() { return (T*)(void*)(Buf.Data + Buf.Size); } + int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; } + T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); } + void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); } +}; + +// Helper: ImGuiTextIndex +// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API. +struct ImGuiTextIndex +{ + ImVector LineOffsets; + int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?) + + void clear() { LineOffsets.clear(); EndOffset = 0; } + int size() { return LineOffsets.Size; } + const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; } + const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); } + void append(const char* base, int old_size, int new_size); +}; + +// Helper: ImGuiStorage +IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key); +//----------------------------------------------------------------------------- +// [SECTION] ImDrawList support +//----------------------------------------------------------------------------- + +// ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value. +// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693 +// Number of segments (N) is calculated using equation: +// N = ceil ( pi / acos(1 - error / r) ) where r > 0, error <= r +// Our equation is significantly simpler that one in the post thanks for choosing segment that is +// perpendicular to X axis. Follow steps in the article from this starting condition and you will +// will get this result. +// +// Rendering circles with an odd number of segments, while mathematically correct will produce +// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.) +#define IM_ROUNDUP_TO_EVEN(_V) ((((_V) + 1) / 2) * 2) +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 4 +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512 +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) + +// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'. +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR) ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI)))) +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD) ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD)) + +// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle. +#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE +#define IM_DRAWLIST_ARCFAST_TABLE_SIZE 48 // Number of samples in lookup table. +#endif +#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle. + +// Data shared between all ImDrawList instances +// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. +struct IMGUI_API ImDrawListSharedData +{ + ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas + ImFont* Font; // Current/default font (optional, for simplified AddText overload) + float FontSize; // Current/default font size (optional, for simplified AddText overload) + float FontScale; // Current/default font scale (== FontSize / Font->FontSize) + float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() + float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc + ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() + ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) + + // [Internal] Temp write buffer + ImVector TempBuffer; + + // [Internal] Lookup tables + ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. + float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo() + ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) + const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas + + ImDrawListSharedData(); + void SetCircleTessellationMaxError(float max_error); +}; + +struct ImDrawDataBuilder +{ + ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. + ImVector LayerData1; + + ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Data types support +//----------------------------------------------------------------------------- + +struct ImGuiDataVarInfo +{ + ImGuiDataType Type; + ImU32 Count; // 1+ + ImU32 Offset; // Offset in parent structure + void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } +}; + +struct ImGuiDataTypeStorage +{ + ImU8 Data[8]; // Opaque storage to fit any data up to ImGuiDataType_COUNT +}; + +// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). +struct ImGuiDataTypeInfo +{ + size_t Size; // Size in bytes + const char* Name; // Short descriptive name for the type, for debugging + const char* PrintFmt; // Default printf format for the type + const char* ScanFmt; // Default scanf format for the type +}; + +// Extend ImGuiDataType_ +enum ImGuiDataTypePrivate_ +{ + ImGuiDataType_String = ImGuiDataType_COUNT + 1, + ImGuiDataType_Pointer, + ImGuiDataType_ID, +}; + +//----------------------------------------------------------------------------- +// [SECTION] Widgets support: flags, enums, data structures +//----------------------------------------------------------------------------- + +// Extend ImGuiItemFlags +// - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too. +// - output: stored in g.LastItemData.ItemFlags +enum ImGuiItemFlagsPrivate_ +{ + // Controlled by user + ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211). + ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. + ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false). + ImGuiItemFlags_NoMarkEdited = 1 << 16, // false // Skip calling MarkItemEdited() + + // Controlled by widget code + ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() + ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData() + + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead. + + // Obsolete + //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior +}; + +// Status flags for an already submitted item +// - output: stored in g.LastItemData.StatusFlags +enum ImGuiItemStatusFlags_ +{ + ImGuiItemStatusFlags_None = 0, + ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid + ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. + ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. + ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. + ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). + ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid. + ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd(). + + // Additional status + semantic for ImGuiTestEngine +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode) + ImGuiItemStatusFlags_Opened = 1 << 21, // Opened status + ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem) + ImGuiItemStatusFlags_Checked = 1 << 23, // Checked status + ImGuiItemStatusFlags_Inputable = 1 << 24, // Item is a text-inputable (e.g. InputText, SliderXXX, DragXXX) +#endif +}; + +// Extend ImGuiHoveredFlags_ +enum ImGuiHoveredFlagsPrivate_ +{ + ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, + ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, + ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, +}; + +// Extend ImGuiInputTextFlags_ +enum ImGuiInputTextFlagsPrivate_ +{ + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. + ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28, // For internal use by InputScalar() and TempInputScalar() +}; + +// Extend ImGuiButtonFlags_ +enum ImGuiButtonFlagsPrivate_ +{ + ImGuiButtonFlags_PressedOnClick = 1 << 4, // return true on click (mouse down event) + ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, // [Default] return true on click + release on same item <-- this is what the majority of Button are using + ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item + ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release) + ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, // return true on double-click (default requires click+release) + ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) + //ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat + ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping + ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable. + //ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press + //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled + ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine + ImGuiButtonFlags_NoKeyModsAllowed = 1 << 16, // disable mouse interaction if a key modifier is held + ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) + ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags) + ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item + ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, + ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, +}; + +// Extend ImGuiComboFlags_ +enum ImGuiComboFlagsPrivate_ +{ + ImGuiComboFlags_CustomPreview = 1 << 20, // enable BeginComboPreview() +}; + +// Extend ImGuiSliderFlags_ +enum ImGuiSliderFlagsPrivate_ +{ + ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically? + ImGuiSliderFlags_ReadOnly = 1 << 21, // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead. +}; + +// Extend ImGuiSelectableFlags_ +enum ImGuiSelectableFlagsPrivate_ +{ + // NB: need to be in sync with last value of ImGuiSelectableFlags_ + ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, + ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. + ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26, // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) +}; + +// Extend ImGuiTreeNodeFlags_ +enum ImGuiTreeNodeFlagsPrivate_ +{ + ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader() + ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517) + ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow, +}; + +enum ImGuiSeparatorFlags_ +{ + ImGuiSeparatorFlags_None = 0, + ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar + ImGuiSeparatorFlags_Vertical = 1 << 1, + ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, // Make separator cover all columns of a legacy Columns() set. +}; + +// Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags. +// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf() +// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in. +enum ImGuiFocusRequestFlags_ +{ + ImGuiFocusRequestFlags_None = 0, + ImGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0, // Find last focused child (if any) and focus it instead. + ImGuiFocusRequestFlags_UnlessBelowModal = 1 << 1, // Do not set focus if the window is below a modal. +}; + +enum ImGuiTextFlags_ +{ + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0, +}; + +enum ImGuiTooltipFlags_ +{ + ImGuiTooltipFlags_None = 0, + ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append) +}; + +// FIXME: this is in development, not exposed/functional as a generic feature yet. +// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 +enum ImGuiLayoutType_ +{ + ImGuiLayoutType_Horizontal = 0, + ImGuiLayoutType_Vertical = 1 +}; + +// Flags for LogBegin() text capturing function +enum ImGuiLogFlags_ +{ + ImGuiLogFlags_None = 0, + + ImGuiLogFlags_OutputTTY = 1 << 0, + ImGuiLogFlags_OutputFile = 1 << 1, + ImGuiLogFlags_OutputBuffer = 1 << 2, + ImGuiLogFlags_OutputClipboard = 1 << 3, + ImGuiLogFlags_OutputMask_ = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard, +}; + +// X/Y enums are fixed to 0/1 so they may be used to index ImVec2 +enum ImGuiAxis +{ + ImGuiAxis_None = -1, + ImGuiAxis_X = 0, + ImGuiAxis_Y = 1 +}; + +enum ImGuiPlotType +{ + ImGuiPlotType_Lines, + ImGuiPlotType_Histogram, +}; + +// Stacked color modifier, backup of modified data so we can restore it +struct ImGuiColorMod +{ + ImGuiCol Col; + ImVec4 BackupValue; +}; + +// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. +struct ImGuiStyleMod +{ + ImGuiStyleVar VarIdx; + union { int BackupInt[2]; float BackupFloat[2]; }; + ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } +}; + +// Storage data for BeginComboPreview()/EndComboPreview() +struct IMGUI_API ImGuiComboPreviewData +{ + ImRect PreviewRect; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + float BackupPrevLineTextBaseOffset; + ImGuiLayoutType BackupLayout; + + ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } +}; + +// Stacked storage data for BeginGroup()/EndGroup() +struct IMGUI_API ImGuiGroupData +{ + ImGuiID WindowID; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + ImVec1 BackupIndent; + ImVec1 BackupGroupOffset; + ImVec2 BackupCurrLineSize; + float BackupCurrLineTextBaseOffset; + ImGuiID BackupActiveIdIsAlive; + bool BackupActiveIdPreviousFrameIsAlive; + bool BackupHoveredIdIsAlive; + bool BackupIsSameLine; + bool EmitItem; +}; + +// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. +struct IMGUI_API ImGuiMenuColumns +{ + ImU32 TotalWidth; + ImU32 NextTotalWidth; + ImU16 Spacing; + ImU16 OffsetIcon; // Always zero for now + ImU16 OffsetLabel; // Offsets are locked in Update() + ImU16 OffsetShortcut; + ImU16 OffsetMark; + ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) + + ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); } + void Update(float spacing, bool window_reappearing); + float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); + void CalcNextTotalWidth(bool update_offsets); +}; + +// Internal temporary state for deactivating InputText() instances. +struct IMGUI_API ImGuiInputTextDeactivatedState +{ + ImGuiID ID; // widget id owning the text state (which just got deactivated) + ImVector TextA; // text buffer + + ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); } + void ClearFreeMemory() { ID = 0; TextA.clear(); } +}; + +// Forward declare imstb_textedit.h structure + make its main configuration define accessible +#undef IMSTB_TEXTEDIT_STRING +#undef IMSTB_TEXTEDIT_CHARTYPE +#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState +#define IMSTB_TEXTEDIT_CHARTYPE char +#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) +#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 +#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 +namespace ImStb { struct STB_TexteditState; } +typedef ImStb::STB_TexteditState ImStbTexteditState; + +// Internal state of the currently focused/edited text input box +// For a given item ID, access with ImGui::GetInputTextState() +struct IMGUI_API ImGuiInputTextState +{ + ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent). + ImStbTexteditState* Stb; // State for stb_textedit.h + ImGuiID ID; // widget id owning the text state + int TextLen; // UTF-8 length of the string in TextA (in bytes) + ImVector TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1). + ImVector TextToRevertTo; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) + ImVector CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack + int BufCapacity; // end-user buffer capacity (include zero terminator) + ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) + float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately + bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection + bool Edited; // edited this frame + ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. + bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. + int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet. + int ReloadSelectionEnd; + + ImGuiInputTextState(); + ~ImGuiInputTextState(); + void ClearText() { TextLen = 0; TextA[0] = 0; CursorClamp(); } + void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); } + void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation + void OnCharPressed(unsigned int c); + + // Cursor & Selection + void CursorAnimReset(); + void CursorClamp(); + bool HasSelection() const; + void ClearSelection(); + int GetCursorPos() const; + int GetSelectionStart() const; + int GetSelectionEnd() const; + void SelectAll(); + + // Reload user buf (WIP #2890) + // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) + // strcpy(my_buf, "hello"); + // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item + // state->ReloadUserBufAndSelectAll(); + void ReloadUserBufAndSelectAll(); + void ReloadUserBufAndKeepSelection(); + void ReloadUserBufAndMoveToEnd(); +}; + +enum ImGuiWindowRefreshFlags_ +{ + ImGuiWindowRefreshFlags_None = 0, + ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0, // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND. + ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1, // [EXPERIMENTAL] Always refresh on hover + ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2, // [EXPERIMENTAL] Always refresh on focus + // Refresh policy/frequency, Load Balancing etc. +}; + +enum ImGuiNextWindowDataFlags_ +{ + ImGuiNextWindowDataFlags_None = 0, + ImGuiNextWindowDataFlags_HasPos = 1 << 0, + ImGuiNextWindowDataFlags_HasSize = 1 << 1, + ImGuiNextWindowDataFlags_HasContentSize = 1 << 2, + ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3, + ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, + ImGuiNextWindowDataFlags_HasFocus = 1 << 5, + ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, + ImGuiNextWindowDataFlags_HasScroll = 1 << 7, + ImGuiNextWindowDataFlags_HasChildFlags = 1 << 8, + ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 9, +}; + +// Storage for SetNexWindow** functions +struct ImGuiNextWindowData +{ + ImGuiNextWindowDataFlags Flags; + ImGuiCond PosCond; + ImGuiCond SizeCond; + ImGuiCond CollapsedCond; + ImVec2 PosVal; + ImVec2 PosPivotVal; + ImVec2 SizeVal; + ImVec2 ContentSizeVal; + ImVec2 ScrollVal; + ImGuiChildFlags ChildFlags; + bool CollapsedVal; + ImRect SizeConstraintRect; + ImGuiSizeCallback SizeCallback; + void* SizeCallbackUserData; + float BgAlphaVal; // Override background alpha + ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) + ImGuiWindowRefreshFlags RefreshFlagsVal; + + ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } + inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } +}; + +enum ImGuiNextItemDataFlags_ +{ + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_HasShortcut = 1 << 2, + ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasStorageID = 1 << 4, +}; + +struct ImGuiNextItemData +{ + ImGuiNextItemDataFlags HasFlags; // Called HasFlags instead of Flags to avoid mistaking this + ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData. + // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem() + ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() + ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) + float Width; // Set by SetNextItemWidth() + ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() + ImGuiInputFlags ShortcutFlags; // Set by SetNextItemShortcut() + bool OpenVal; // Set by SetNextItemOpen() + ImU8 OpenCond; // Set by SetNextItemOpen() + ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal + ImGuiID StorageId; // Set by SetNextItemStorageID() + + ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } + inline void ClearFlags() { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! +}; + +// Status storage for the last submitted item +struct ImGuiLastItemData +{ + ImGuiID ID; + ImGuiItemFlags ItemFlags; // See ImGuiItemFlags_ + ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ + ImRect Rect; // Full rectangle + ImRect NavRect; // Navigation scoring rectangle (not displayed) + // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags ar set. + ImRect DisplayRect; // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set. + ImRect ClipRect; // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set.. + ImGuiKeyChord Shortcut; // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set.. + + ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } +}; + +// Store data emitted by TreeNode() for usage by TreePop() +// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data +// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult(). +// Only stored when the node is a potential candidate for landing on a Left arrow jump. +struct ImGuiTreeNodeStackData +{ + ImGuiID ID; + ImGuiTreeNodeFlags TreeFlags; + ImGuiItemFlags ItemFlags; // Used for nav landing + ImRect NavRect; // Used for nav landing +}; + +// sizeof() = 20 +struct IMGUI_API ImGuiErrorRecoveryState +{ + short SizeOfWindowStack; + short SizeOfIDStack; + short SizeOfTreeStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); } +}; + +// Data saved for each window pushed into the stack +struct ImGuiWindowStackData +{ + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting + bool DisabledOverrideReenable; // Non-child window override disabled flag +}; + +struct ImGuiShrinkWidthItem +{ + int Index; + float Width; + float InitialWidth; +}; + +struct ImGuiPtrOrIndex +{ + void* Ptr; // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool. + int Index; // Usually index in a main pool. + + ImGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; } + ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Popup support +//----------------------------------------------------------------------------- + +enum ImGuiPopupPositionPolicy +{ + ImGuiPopupPositionPolicy_Default, + ImGuiPopupPositionPolicy_ComboBox, + ImGuiPopupPositionPolicy_Tooltip, +}; + +// Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack) +struct ImGuiPopupData +{ + ImGuiID PopupId; // Set on OpenPopup() + ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() + ImGuiWindow* RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close + int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value + int OpenFrameCount; // Set on OpenPopup() + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) + ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup + + ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Inputs support +//----------------------------------------------------------------------------- + +// Bit array for named keys +typedef ImBitArray ImBitArrayForNamedKeys; + +// [Internal] Key ranges +#define ImGuiKey_LegacyNativeKey_BEGIN 0 +#define ImGuiKey_LegacyNativeKey_END 512 +#define ImGuiKey_Keyboard_BEGIN (ImGuiKey_NamedKey_BEGIN) +#define ImGuiKey_Keyboard_END (ImGuiKey_GamepadStart) +#define ImGuiKey_Gamepad_BEGIN (ImGuiKey_GamepadStart) +#define ImGuiKey_Gamepad_END (ImGuiKey_GamepadRStickDown + 1) +#define ImGuiKey_Mouse_BEGIN (ImGuiKey_MouseLeft) +#define ImGuiKey_Mouse_END (ImGuiKey_MouseWheelY + 1) +#define ImGuiKey_Aliases_BEGIN (ImGuiKey_Mouse_BEGIN) +#define ImGuiKey_Aliases_END (ImGuiKey_Mouse_END) + +// [Internal] Named shortcuts for Navigation +#define ImGuiKey_NavKeyboardTweakSlow ImGuiMod_Ctrl +#define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift +#define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1 +#define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1 +#define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown) +#define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight) +#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft +#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp + +enum ImGuiInputEventType +{ + ImGuiInputEventType_None = 0, + ImGuiInputEventType_MousePos, + ImGuiInputEventType_MouseWheel, + ImGuiInputEventType_MouseButton, + ImGuiInputEventType_Key, + ImGuiInputEventType_Text, + ImGuiInputEventType_Focus, + ImGuiInputEventType_COUNT +}; + +enum ImGuiInputSource +{ + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them. + ImGuiInputSource_Keyboard, + ImGuiInputSource_Gamepad, + ImGuiInputSource_COUNT +}; + +// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? +// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' +struct ImGuiInputEventMousePos { float PosX, PosY; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventMouseWheel { float WheelX, WheelY; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventMouseButton { int Button; bool Down; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; +struct ImGuiInputEventText { unsigned int Char; }; +struct ImGuiInputEventAppFocused { bool Focused; }; + +struct ImGuiInputEvent +{ + ImGuiInputEventType Type; + ImGuiInputSource Source; + ImU32 EventId; // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data). + union + { + ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos + ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel + ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key + ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text + ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus + }; + bool AddedByTestEngine; + + ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } +}; + +// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior. +#define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. +#define ImGuiKeyOwner_NoOwner ((ImGuiID)-1) // Require key to have no owner. +//#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit. + +typedef ImS16 ImGuiKeyRoutingIndex; + +// Routing table entry (sizeof() == 16 bytes) +struct ImGuiKeyRoutingData +{ + ImGuiKeyRoutingIndex NextEntryIndex; + ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. + ImU8 RoutingCurrScore; // [DEBUG] For debug display + ImU8 RoutingNextScore; // Lower is better (0: perfect score) + ImGuiID RoutingCurr; + ImGuiID RoutingNext; + + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } +}; + +// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. +// Stored in main context (1 instance) +struct ImGuiKeyRoutingTable +{ + ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[] + ImVector Entries; + ImVector EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer) + + ImGuiKeyRoutingTable() { Clear(); } + void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } +}; + +// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features) +// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData. +struct ImGuiKeyOwnerData +{ + ImGuiID OwnerCurr; + ImGuiID OwnerNext; + bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame. + bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well. + + ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; } +}; + +// Extend ImGuiInputFlags_ +// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() +// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) +enum ImGuiInputFlagsPrivate_ +{ + // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut() + // - Repeat mode: Repeat rate selection + ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) + ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast + ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster + // - Repeat mode: Specify when repeating key pressed can be interrupted. + // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in. + ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior. + ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut) + ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod. + ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat + + // Flags for SetKeyOwner(), SetItemKeyOwner() + // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary. + ImGuiInputFlags_LockThisFrame = 1 << 20, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. + ImGuiInputFlags_LockUntilRelease = 1 << 21, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. + + // - Condition for SetItemKeyOwner() + ImGuiInputFlags_CondHovered = 1 << 22, // Only set if item is hovered (default to both) + ImGuiInputFlags_CondActive = 1 << 23, // Only set if item is active (default to both) + ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + + // [Internal] Mask of which function support which flags + ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, + ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress, + ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_, + ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways, + ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow, + ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_, + ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat, + ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_, + ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip, + ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, + ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, +}; + +//----------------------------------------------------------------------------- +// [SECTION] Clipper support +//----------------------------------------------------------------------------- + +// Note that Max is exclusive, so perhaps should be using a Begin/End convention. +struct ImGuiListClipperRange +{ + int Min; + int Max; + bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) + ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices + ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices + + static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } + static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } +}; + +// Temporary clipper data, buffers shared/reused between instances +struct ImGuiListClipperData +{ + ImGuiListClipper* ListClipper; + float LossynessOffset; + int StepNo; + int ItemsFrozen; + ImVector Ranges; + + ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Navigation support +//----------------------------------------------------------------------------- + +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used. + ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) + ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request + ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function. +}; + +// Early work-in-progress API for ScrollToItem() +enum ImGuiScrollFlags_ +{ + ImGuiScrollFlags_None = 0, + ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] + ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] + ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] + ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis + ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] + ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) + ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). + ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY, +}; + +enum ImGuiNavRenderCursorFlags_ +{ + ImGuiNavRenderCursorFlags_None = 0, + ImGuiNavRenderCursorFlags_Compact = 1 << 1, // Compact highlight, no padding/distance from focused item + ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse. + ImGuiNavRenderCursorFlags_NoRounding = 1 << 3, +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiNavHighlightFlags_None = ImGuiNavRenderCursorFlags_None, // Renamed in 1.91.4 + ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4 + ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4 + ImGuiNavHighlightFlags_NoRounding = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4 +#endif +}; + +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY, + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary + ImGuiNavMoveFlags_Forwarded = 1 << 7, + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) + ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request. + ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. + ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo + ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14, // Do not alter the nav cursor visible state + ImGuiNavMoveFlags_NoClearActiveId = 1 << 15, // (Experimental) Do not clear active id when applying move result +}; + +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt) + ImGuiNavLayer_COUNT +}; + +// Storage for navigation query/results +struct ImGuiNavItemData +{ + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags ItemFlags; // ????,Move // Best candidate item flags + float DistBox; // Move // Best candidate box distance to current NavId + float DistCenter; // Move // Best candidate center distance to current NavId + float DistAxial; // Move // Best candidate axial distance to current NavId + ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags_HasSelectionUserData) + + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + +// Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[] +struct ImGuiFocusScopeData +{ + ImGuiID ID; + ImGuiID WindowID; +}; + +//----------------------------------------------------------------------------- +// [SECTION] Typing-select support +//----------------------------------------------------------------------------- + +// Flags for GetTypingSelectRequest() +enum ImGuiTypingSelectFlags_ +{ + ImGuiTypingSelectFlags_None = 0, + ImGuiTypingSelectFlags_AllowBackspace = 1 << 0, // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state) + ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1, // Allow "single char" search mode which is activated when pressing the same character multiple times. +}; + +// Returned by GetTypingSelectRequest(), designed to eventually be public. +struct IMGUI_API ImGuiTypingSelectRequest +{ + ImGuiTypingSelectFlags Flags; // Flags passed to GetTypingSelectRequest() + int SearchBufferLen; + const char* SearchBuffer; // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize). + bool SelectRequest; // Set when buffer was modified this frame, requesting a selection. + bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication. + ImS8 SingleCharSize; // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input. +}; + +// Storage for GetTypingSelectRequest() +struct IMGUI_API ImGuiTypingSelectState +{ + ImGuiTypingSelectRequest Request; // User-facing data + char SearchBuffer[64]; // Search buffer: no need to make dynamic as this search is very transient. + ImGuiID FocusScope; + int LastRequestFrame = 0; + float LastRequestTime = 0.0f; + bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing. + + ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); } + void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging +}; + +//----------------------------------------------------------------------------- +// [SECTION] Columns support +//----------------------------------------------------------------------------- + +// Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays! +enum ImGuiOldColumnFlags_ +{ + ImGuiOldColumnFlags_None = 0, + ImGuiOldColumnFlags_NoBorder = 1 << 0, // Disable column dividers + ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers + ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns + ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window + ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. + + // Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + //ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, + //ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, + //ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, + //ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, + //ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, + //ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize, +#endif +}; + +struct ImGuiOldColumnData +{ + float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) + float OffsetNormBeforeResize; + ImGuiOldColumnFlags Flags; // Not exposed + ImRect ClipRect; + + ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); } +}; + +struct ImGuiOldColumns +{ + ImGuiID ID; + ImGuiOldColumnFlags Flags; + bool IsFirstFrame; + bool IsBeingResized; + int Current; + int Count; + float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x + float LineMinY, LineMaxY; + float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns() + float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns() + ImRect HostInitialClipRect; // Backup of ClipRect at the time of BeginColumns() + ImRect HostBackupClipRect; // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground() + ImRect HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns() + ImVector Columns; + ImDrawListSplitter Splitter; + + ImGuiOldColumns() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Box-select support +//----------------------------------------------------------------------------- + +struct ImGuiBoxSelectState +{ + // Active box-selection data (persistent, 1 active at a time) + ImGuiID ID; + bool IsActive; + bool IsStarting; + bool IsStartedFromVoid; // Starting click was not from an item. + bool IsStartedSetNavIdOnce; + bool RequestClear; + ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic. + ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling) + ImVec2 EndPosRel; // End position in window-contents relative space + ImVec2 ScrollAccum; // Scrolling accumulator (to behave at high-frame spaces) + ImGuiWindow* Window; + + // Temporary/Transient data + bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select. + ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets. + ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos) + ImRect BoxSelectRectCurr; + + ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Multi-select support +//----------------------------------------------------------------------------- + +// We always assume that -1 is an invalid value (which works for indices and pointers) +#define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) + +// Temporary storage for multi-select +struct IMGUI_API ImGuiMultiSelectTempData +{ + ImGuiMultiSelectIO IO; // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. + ImGuiMultiSelectState* Storage; + ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiMultiSelectFlags Flags; + ImVec2 ScopeRectMin; + ImVec2 BackupCursorMaxPos; + ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges. + ImGuiID BoxSelectId; + ImGuiKeyChord KeyMods; + ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all. + bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. + bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. + bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. + bool NavIdPassedBy; + bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. + bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. + + ImGuiMultiSelectTempData() { Clear(); } + void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation. + void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; } +}; + +// Persistent storage for multi-select (as long as selection is alive) +struct IMGUI_API ImGuiMultiSelectState +{ + ImGuiWindow* Window; + ImGuiID ID; + int LastFrameActive; // Last used frame-count, for GC. + int LastSelectionSize; // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown. + ImS8 RangeSelected; // -1 (don't have) or true/false + ImS8 NavIdSelected; // -1 (don't have) or true/false + ImGuiSelectionUserData RangeSrcItem; // + ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) + + ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Docking support +//----------------------------------------------------------------------------- + +#ifdef IMGUI_HAS_DOCK +// +#endif // #ifdef IMGUI_HAS_DOCK + +//----------------------------------------------------------------------------- +// [SECTION] Viewport support +//----------------------------------------------------------------------------- + +// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) +// Every instance of ImGuiViewport is in fact a ImGuiViewportP. +struct ImGuiViewportP : public ImGuiViewport +{ + int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + ImDrawData DrawDataP; + ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData + + // Per-viewport work area + // - Insets are >= 0.0f values, distance from viewport corners to work area. + // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. + // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. + ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect(). + ImVec2 WorkInsetMax; // " + ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset + ImVec2 BuildWorkInsetMax; // " + + ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } + ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } + + // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) + ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } + ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields + + // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Settings support +//----------------------------------------------------------------------------- + +// Windows data saved in imgui.ini file +// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily. +// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure) +struct ImGuiWindowSettings +{ + ImGuiID ID; + ImVec2ih Pos; + ImVec2ih Size; + bool Collapsed; + bool IsChild; + bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) + bool WantDelete; // Set to invalidate/delete the settings entry + + ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); } + char* GetName() { return (char*)(this + 1); } +}; + +struct ImGuiSettingsHandler +{ + const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' + ImGuiID TypeHash; // == ImHashStr(TypeName) + void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Clear all settings data + void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called before reading (in registration order) + void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" + void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry + void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) + void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' + void* UserData; + + ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Localization support +//----------------------------------------------------------------------------- + +// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. +enum ImGuiLocKey : int +{ + ImGuiLocKey_VersionStr, + ImGuiLocKey_TableSizeOne, + ImGuiLocKey_TableSizeAllFit, + ImGuiLocKey_TableSizeAllDefault, + ImGuiLocKey_TableResetOrder, + ImGuiLocKey_WindowingMainMenuBar, + ImGuiLocKey_WindowingPopup, + ImGuiLocKey_WindowingUntitled, + ImGuiLocKey_OpenLink_s, + ImGuiLocKey_CopyLink, + ImGuiLocKey_COUNT +}; + +struct ImGuiLocEntry +{ + ImGuiLocKey Key; + const char* Text; +}; + +//----------------------------------------------------------------------------- +// [SECTION] Error handling, State recovery support +//----------------------------------------------------------------------------- + +// Macros used by Recoverable Error handling +// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro). +// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler. +// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling. +// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling. +#ifndef IM_ASSERT_USER_ERROR +#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error +#endif + +// The error callback is currently not public, as it is expected that only advanced users will rely on it. +typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback + +//----------------------------------------------------------------------------- +// [SECTION] Metrics, Debug Tools +//----------------------------------------------------------------------------- + +enum ImGuiDebugLogFlags_ +{ + // Event types + ImGuiDebugLogFlags_None = 0, + ImGuiDebugLogFlags_EventError = 1 << 0, // Error submitted by IM_ASSERT_USER_ERROR() + ImGuiDebugLogFlags_EventActiveId = 1 << 1, + ImGuiDebugLogFlags_EventFocus = 1 << 2, + ImGuiDebugLogFlags_EventPopup = 1 << 3, + ImGuiDebugLogFlags_EventNav = 1 << 4, + ImGuiDebugLogFlags_EventClipper = 1 << 5, + ImGuiDebugLogFlags_EventSelection = 1 << 6, + ImGuiDebugLogFlags_EventIO = 1 << 7, + ImGuiDebugLogFlags_EventInputRouting = 1 << 8, + ImGuiDebugLogFlags_EventDocking = 1 << 9, // Unused in this branch + ImGuiDebugLogFlags_EventViewport = 1 << 10, // Unused in this branch + + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, + ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY + ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine +}; + +struct ImGuiDebugAllocEntry +{ + int FrameCount; + ImS16 AllocCount; + ImS16 FreeCount; +}; + +struct ImGuiDebugAllocInfo +{ + int TotalAllocCount; // Number of call to MemAlloc(). + int TotalFreeCount; + ImS16 LastEntriesIdx; // Current index in buffer + ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations + + ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); } +}; + +struct ImGuiMetricsConfig +{ + bool ShowDebugLog = false; + bool ShowIDStackTool = false; + bool ShowWindowsRects = false; + bool ShowWindowsBeginOrder = false; + bool ShowTablesRects = false; + bool ShowDrawCmdMesh = true; + bool ShowDrawCmdBoundingBoxes = true; + bool ShowTextEncodingViewer = false; + bool ShowAtlasTintedWithTextColor = false; + int ShowWindowsRectsType = -1; + int ShowTablesRectsType = -1; + int HighlightMonitorIdx = -1; + ImGuiID HighlightViewportID = 0; +}; + +struct ImGuiStackLevelInfo +{ + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + ImGuiDataType DataType : 8; + char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed. + + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for ID Stack tool queries +struct ImGuiIDStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + bool CopyToClipboardOnCtrlC; + float CopyToClipboardLastTime; + + ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Generic context hooks +//----------------------------------------------------------------------------- + +typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); +enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }; + +struct ImGuiContextHook +{ + ImGuiID HookId; // A unique ID assigned by AddContextHook() + ImGuiContextHookType Type; + ImGuiID Owner; + ImGuiContextHookCallback Callback; + void* UserData; + + ImGuiContextHook() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] ImGuiContext (main Dear ImGui context) +//----------------------------------------------------------------------------- + +struct ImGuiContext +{ + bool Initialized; + bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. + ImGuiIO IO; + ImGuiPlatformIO PlatformIO; + ImGuiStyle Style; + ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() + float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. + float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. + float FontScale; // == FontSize / Font->FontSize + float CurrentDpiScale; // Current window/viewport DpiScale + ImDrawListSharedData DrawListSharedData; + double Time; + int FrameCount; + int FrameCountEnded; + int FrameCountRendered; + bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() + bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed + bool WithinEndChild; // Set within EndChild() + bool GcCompactAll; // Request full GC + bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() + void* TestEngine; // Test engine user data + char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups) + + // Inputs + ImVector InputEventsQueue; // Input events which will be trickled/written into IO structure. + ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. + ImGuiMouseSource InputEventsNextMouseSource; + ImU32 InputEventsNextEventId; + + // Windows state + ImVector Windows; // Windows, sorted in display order, back to front + ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. + ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child + ImVector CurrentWindowStack; + ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* + int WindowsActiveCount; // Number of unique windows submitted by frame + ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING). + ImGuiID DebugBreakInWindow; // Set to break in Begin() call. + ImGuiWindow* CurrentWindow; // Window being drawn into + ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. + ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. + ImGuiWindow* HoveredWindowBeforeClear; // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors. + ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. + ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. + ImVec2 WheelingWindowRefMousePos; + int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL + int WheelingWindowScrolledFrame; + float WheelingWindowReleaseTimer; + ImVec2 WheelingWindowWheelRemainder; + ImVec2 WheelingAxisAvg; + + // Item/widgets state and tracking information + ImGuiID DebugDrawIdConflicts; // Set when we detect multiple items with the same identifier + ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] + ImGuiID HoveredId; // Hovered widget, filled during the frame + ImGuiID HoveredIdPreviousFrame; + int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id + float HoveredIdTimer; // Measure contiguous hovering time + float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active + bool HoveredIdAllowOverlap; + bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. + bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled + ImGuiID ActiveId; // Active widget + ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) + float ActiveIdTimer; + bool ActiveIdIsJustActivated; // Set at the time of activation for one frame + bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) + bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused. + bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. + bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. + bool ActiveIdHasBeenEditedThisFrame; + bool ActiveIdFromShortcut; + int ActiveIdMouseButton : 8; + ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) + ImGuiWindow* ActiveIdWindow; + ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad + ImGuiID ActiveIdPreviousFrame; + bool ActiveIdPreviousFrameIsAlive; + bool ActiveIdPreviousFrameHasBeenEditedBefore; + ImGuiWindow* ActiveIdPreviousFrameWindow; + ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. + float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. + + // Key/Input Ownership + Shortcut Routing system + // - The idea is that instead of "eating" a given key, we can link to an owner. + // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. + // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame(). + double LastKeyModsChangeTime; // Record the last time key mods changed (affect repeat delay when using shortcut logic) + double LastKeyModsChangeFromNoneTime; // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) + double LastKeyboardKeyPressTime; // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed. + ImBitArrayForNamedKeys KeysMayBeCharInput; // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes + ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; + ImGuiKeyRoutingTable KeysRoutingTable; + ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) + bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations) + ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls. + //ImU32 ActiveIdUsingNavInputMask; // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' + + // Next window/item data + ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId. + ImGuiItemFlags CurrentItemFlags; // Value for currently appending items == g.ItemFlagsStack.back() + ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location + ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions + ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) + ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + bool DebugShowGroupRects; + + // Shared stacks + ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) + ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() + ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() + ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() + ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() + ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() + ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() + ImVector OpenPopupStack; // Which popups are open (persistent) + ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + ImVectorTreeNodeStack; // Stack for TreeNode() + + // Viewports + ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. + + // Keyboard/Gamepad Navigation + bool NavCursorVisible; // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move. + bool NavHighlightItemUnderNav; // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item. + //bool NavDisableHighlight; // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible) + //bool NavDisableMouseHover; // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again. + bool NavMousePosDirty; // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default) + bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid + ImGuiID NavId; // Focused item for navigation + ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' + ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) + ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer) + ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 + ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) + ImGuiActivateFlags NavActivateFlags; + ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. + ImGuiID NavHighlightActivatedId; + float NavHighlightActivatedTimer; + ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse + ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. + ImS8 NavCursorHideFrames; + + // Navigation: Init & Move Requests + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() + bool NavInitRequest; // Init request for appearing window to select first item + bool NavInitRequestFromMove; + ImGuiNavItemData NavInitResult; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) + bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() + bool NavMoveScoringItems; // Move request submitted, still scoring incoming items + bool NavMoveForwardToNextFrame; + ImGuiNavMoveFlags NavMoveFlags; + ImGuiScrollFlags NavMoveScrollFlags; + ImGuiKeyChord NavMoveKeyMods; + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirForDebug; + ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted + int NavScoringDebugCount; // Metrics for debugging + int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id + int NavTabbingCounter; // >0 when counting items for tabbing + ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy + + // Navigation: record of last move request + ImGuiID NavJustMovedFromFocusScopeId; // Just navigated from this focus scope id (result of a successfully MoveRequest). + ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). + ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). + ImGuiKeyChord NavJustMovedToKeyMods; + bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. + bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData. + + // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) + ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828) + ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X) + ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! + ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. + ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents + float NavWindowingTimer; + float NavWindowingHighlightAlpha; + bool NavWindowingToggleLayer; + ImGuiKey NavWindowingToggleKey; + ImVec2 NavWindowingAccumDeltaPos; + ImVec2 NavWindowingAccumDeltaSize; + + // Render + float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) + + // Drag and Drop + bool DragDropActive; + bool DragDropWithinSource; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source. + bool DragDropWithinTarget; // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target. + ImGuiDragDropFlags DragDropSourceFlags; + int DragDropSourceFrameCount; + int DragDropMouseButton; + ImGuiPayload DragDropPayload; + ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping) + ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing + ImGuiID DragDropTargetId; + ImGuiDragDropFlags DragDropAcceptFlags; + float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) + ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) + ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) + int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source + ImGuiID DragDropHoldJustPressedId; // Set when holding a payload just made ButtonBehavior() return a press. + ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size + unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads + + // Clipper + int ClipperTempDataStacked; + ImVector ClipperTempData; + + // Tables + ImGuiTable* CurrentTable; + ImGuiID DebugBreakInTable; // Set to break in BeginTable() call. + int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) + ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) + ImPool Tables; // Persistent table data + ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) + ImVector DrawChannelsTempMergeBuffer; + + // Tab bars + ImGuiTabBar* CurrentTabBar; + ImPool TabBars; + ImVector CurrentTabBarStack; + ImVector ShrinkWidthBuffer; + + // Multi-Select state + ImGuiBoxSelectState BoxSelectState; + ImGuiMultiSelectTempData* CurrentMultiSelect; + int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size) + ImVector MultiSelectTempData; + ImPool MultiSelectStorage; + + // Hover Delay system + ImGuiID HoverItemDelayId; + ImGuiID HoverItemDelayIdPreviousFrame; + float HoverItemDelayTimer; // Currently used by IsItemHovered() + float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item. + ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window. + + // Mouse state + ImGuiMouseCursor MouseCursor; + float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic) + ImVec2 MouseLastValidPos; + + // Widget state + ImGuiInputTextState InputTextState; + ImGuiInputTextDeactivatedState InputTextDeactivatedState; + ImFont InputTextPasswordFont; + ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types + int BeginMenuDepth; + int BeginComboDepth; + ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets + ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others). + ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for + float ColorEditSavedHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditSavedSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditSavedColor; // RGB value with alpha set to 0. + ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. + ImGuiComboPreviewData ComboPreviewData; + ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving + bool WindowResizeRelativeMode; + short ScrollbarSeekMode; // 0: scroll to clicked location, -1/+1: prev/next page. + float ScrollbarClickDeltaToGrabCenter; // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space. + float SliderGrabClickOffset; + float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. + bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? + bool DragCurrentAccumDirty; + float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings + float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; + ImGuiWindow* TooltipPreviousWindow; // Window of last tooltip submitted during the frame + ImVector ClipboardHandlerData; // If no custom clipboard handler is defined + ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once + ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest() + + // Platform support + ImGuiPlatformImeData PlatformImeData; // Data updated by current frame + ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler. + + // Settings + bool SettingsLoaded; + float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero + ImGuiTextBuffer SettingsIniData; // In memory .ini settings + ImVector SettingsHandlers; // List of .ini settings handlers + ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries + ImChunkStream SettingsTables; // ImGuiTable .ini settings entries + ImVector Hooks; // Hooks for extensions (e.g. test engine) + ImGuiID HookIdNext; // Next available HookId + + // Localization + const char* LocalizationTable[ImGuiLocKey_COUNT]; + + // Capture/Logging + bool LogEnabled; // Currently capturing + ImGuiLogFlags LogFlags; // Capture flags/type + ImGuiWindow* LogWindow; + ImFileHandle LogFile; // If != NULL log to stdout/ file + ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + const char* LogNextPrefix; + const char* LogNextSuffix; + float LogLinePosY; + bool LogLineFirstItem; + int LogDepthRef; + int LogDepthToExpand; + int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. + + // Error Handling + ImGuiErrorCallback ErrorCallback; // = NULL. May be exposed in public API eventually. + void* ErrorCallbackUserData; // = NULL + ImVec2 ErrorTooltipLockedPos; + bool ErrorFirst; + int ErrorCountCurrentFrame; // [Internal] Number of errors submitted this frame. + ImGuiErrorRecoveryState StackSizesInNewFrame; // [Internal] + ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; // [Internal] + + // Debug Tools + // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) + int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL) + ImGuiDebugLogFlags DebugLogFlags; + ImGuiTextBuffer DebugLogBuf; + ImGuiTextIndex DebugLogIndex; + int DebugLogSkippedErrors; + ImGuiDebugLogFlags DebugLogAutoDisableFlags; + ImU8 DebugLogAutoDisableFrames; + ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. + bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId. + ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause + ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around. + bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) + ImU8 DebugItemPickerMouseButton; + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID + float DebugFlashStyleColorTime; + ImVec4 DebugFlashStyleColorBackup; + ImGuiMetricsConfig DebugMetricsConfig; + ImGuiIDStackTool DebugIDStackTool; + ImGuiDebugAllocInfo DebugAllocInfo; + + // Misc + float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. + int FramerateSecPerFrameIdx; + int FramerateSecPerFrameCount; + float FramerateSecPerFrameAccum; + int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1. + int WantCaptureKeyboardNextFrame; // " + int WantTextInputNextFrame; + ImVector TempBuffer; // Temporary text buffer + char TempKeychordName[64]; + + ImGuiContext(ImFontAtlas* shared_font_atlas); +}; + +//----------------------------------------------------------------------------- +// [SECTION] ImGuiWindowTempData, ImGuiWindow +//----------------------------------------------------------------------------- + +// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. +// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..) +// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin) +struct IMGUI_API ImGuiWindowTempData +{ + // Layout + ImVec2 CursorPos; // Current emitting position, in absolute coordinates. + ImVec2 CursorPosPrevLine; + ImVec2 CursorStartPos; // Initial position after Begin(), generally ~ window position + WindowPadding. + ImVec2 CursorMaxPos; // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame. + ImVec2 IdealMaxPos; // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame. + ImVec2 CurrLineSize; + ImVec2 PrevLineSize; + float CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added). + float PrevLineTextBaseOffset; + bool IsSameLine; + bool IsSetPos; + ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) + ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. + ImVec1 GroupOffset; + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area. + + // Keyboard/Gamepad navigation + ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + short NavLayersActiveMask; // Which layers have been written to (result from previous frame) + short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) + bool NavIsScrollPushableX; // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column. + bool NavHideHighlightOneFrame; + bool NavWindowHasScrollY; // Set per window when scrolling can be used (== ScrollMax.y > 0.0f) + + // Miscellaneous + bool MenuBarAppending; // FIXME: Remove this + ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. + ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement + int TreeDepth; // Current tree depth. + ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary. + ImVector ChildWindows; + ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state) + ImGuiOldColumns* CurrentColumns; // Current columns set + int CurrentTableIdx; // Current table index (into g.Tables) + ImGuiLayoutType LayoutType; + ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + ImU32 ModalDimBgColor; + + // Local parameters stacks + // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. + float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). + float TextWrapPos; // Current text wrap pos. + ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) + ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) +}; + +// Storage for one window +struct IMGUI_API ImGuiWindow +{ + ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent). + char* Name; // Window name, owned by the window. + ImGuiID ID; // == ImHashStr(Name) + ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + ImGuiChildFlags ChildFlags; // Set when window is a child window. See enum ImGuiChildFlags_ + ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. + ImVec2 Pos; // Position (always rounded-up to nearest pixel) + ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) + ImVec2 SizeFull; // Size when non collapsed + ImVec2 ContentSize; // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding. + ImVec2 ContentSizeIdeal; + ImVec2 ContentSizeExplicit; // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize(). + ImVec2 WindowPadding; // Window padding at the time of Begin(). + float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. + float WindowBorderSize; // Window border size at the time of Begin(). + float TitleBarHeight, MenuBarHeight; // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight. + float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). + float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). + float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). + int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! + ImGuiID MoveId; // == window->GetID("#MOVE") + ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) + ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) + ImVec2 Scroll; + ImVec2 ScrollMax; + ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) + ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered + ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold + ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. + bool ScrollbarX, ScrollbarY; // Are scrollbars visible? + bool Active; // Set to true on Begin(), unless Collapsed + bool WasActive; + bool WriteAccessed; // Set to true when any widget access the current window + bool Collapsed; // Set when collapsing window to become only title-bar + bool WantCollapseToggle; + bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) + bool SkipRefresh; // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false. + bool Appearing; // Set during the frame where the window is appearing (or re-appearing) + bool Hidden; // Do not display (== HiddenFrames*** > 0) + bool IsFallbackWindow; // Set on the "Debug##Default" window. + bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() + bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + signed char ResizeBorderHovered; // Current border being hovered for resize (-1: none, otherwise 0-3) + signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) + short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + short BeginCountPreviousFrame; // Number of Begin() during the previous frame + short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. + short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. + ImS8 AutoFitFramesX, AutoFitFramesY; + bool AutoFitOnlyGrows; + ImGuiDir AutoPosLastDirection; + ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames + ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size + ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only + ImS8 DisableInputsFrames; // Disable window interactions for N frames + ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. + ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. + ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. + ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) + ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right. + + ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure) + ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. + + // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer. + // The main 'OuterRect', omitted as a field, is window->Rect(). + ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window. + ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar) + ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect. + ImRect WorkRect; // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward). + ImRect ParentWorkRect; // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack? + ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back(). + ImRect ContentRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on. + ImVec2ih HitTestHoleSize; // Define an optional rectangular hole where mouse will pass-through the window. + ImVec2ih HitTestHoleOffset; + + int LastFrameActive; // Last frame number the window was Active. + float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) + float ItemWidthDefault; + ImGuiStorage StateStorage; + ImVector ColumnsStorage; + float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() + int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) + + ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) + ImDrawList DrawListInst; + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. + ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. + ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. + ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document) + + ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) + ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) + ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space + ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX. + ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin() + + int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy + int MemoryDrawListVtxCapacity; + bool MemoryCompacted; // Set when window extraneous data have been garbage collected + +public: + ImGuiWindow(ImGuiContext* context, const char* name); + ~ImGuiWindow(); + + ImGuiID GetID(const char* str, const char* str_end = NULL); + ImGuiID GetID(const void* ptr); + ImGuiID GetID(int n); + ImGuiID GetIDFromPos(const ImVec2& p_abs); + ImGuiID GetIDFromRectangle(const ImRect& r_abs); + + // We don't use g.FontSize because the window may be != g.CurrentWindow. + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + float CalcFontSize() const { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } + ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } + ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Tab bar, Tab item support +//----------------------------------------------------------------------------- + +// Extend ImGuiTabBarFlags_ +enum ImGuiTabBarFlagsPrivate_ +{ + ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] + ImGuiTabBarFlags_IsFocused = 1 << 21, + ImGuiTabBarFlags_SaveSettings = 1 << 22, // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs +}; + +// Extend ImGuiTabItemFlags_ +enum ImGuiTabItemFlagsPrivate_ +{ + ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, + ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) + ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button +}; + +// Storage for one active tab item (sizeof() 40 bytes) +struct ImGuiTabItem +{ + ImGuiID ID; + ImGuiTabItemFlags Flags; + int LastFrameVisible; + int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance + float Offset; // Position relative to beginning of tab + float Width; // Width currently displayed + float ContentWidth; // Width of label, stored during BeginTabItem() call + float RequestedWidth; // Width optionally requested by caller, -1.0f is unused + ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable + ImS16 IndexDuringLayout; // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions. + bool WantClose; // Marked as closed by SetTabItemClosed() + + ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } +}; + +// Storage for a tab bar (sizeof() 160 bytes) +struct IMGUI_API ImGuiTabBar +{ + ImGuiWindow* Window; + ImVector Tabs; + ImGuiTabBarFlags Flags; + ImGuiID ID; // Zero for tab-bars used by docking + ImGuiID SelectedTabId; // Selected tab/window + ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation + ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) + int CurrFrameVisible; + int PrevFrameVisible; + ImRect BarRect; + float CurrTabsContentsHeight; + float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar + float WidthAllTabs; // Actual width of all tabs (locked during layout) + float WidthAllTabsIdeal; // Ideal width if all tabs were visible and not clipped + float ScrollingAnim; + float ScrollingTarget; + float ScrollingTargetDistToVisibility; + float ScrollingSpeed; + float ScrollingRectMinX; + float ScrollingRectMaxX; + float SeparatorMinX; + float SeparatorMaxX; + ImGuiID ReorderRequestTabId; + ImS16 ReorderRequestOffset; + ImS8 BeginCount; + bool WantLayout; + bool VisibleTabWasSubmitted; + bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame + ImS16 TabsActiveCount; // Number of tabs submitted this frame. + ImS16 LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() + float ItemSpacingY; + ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() + ImVec2 BackupCursorPos; + ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. + + ImGuiTabBar(); +}; + +//----------------------------------------------------------------------------- +// [SECTION] Table support +//----------------------------------------------------------------------------- + +#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. +#define IMGUI_TABLE_MAX_COLUMNS 512 // May be further lifted + +// Our current column maximum is 64 but we may raise that in the future. +typedef ImS16 ImGuiTableColumnIdx; +typedef ImU16 ImGuiTableDrawChannelIdx; + +// [Internal] sizeof() ~ 112 +// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. +// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping. +// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". +struct ImGuiTableColumn +{ + ImGuiTableColumnFlags Flags; // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_ + float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. + float MinX; // Absolute positions + float MaxX; + float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() + float WidthAuto; // Automatic width + float WidthMax; // Maximum width (FIXME: overwritten by each instance) + float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. + float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). + ImRect ClipRect; // Clipping rectangle for the column + ImGuiID UserID; // Optional, value passed to TableSetupColumn() + float WorkMinX; // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column + float WorkMaxX; // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2) + float ItemWidth; // Current item width for the column, preserved across rows + float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width. + float ContentMaxXUnfrozen; + float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls + float ContentMaxXHeadersIdeal; + ImS16 NameOffset; // Offset into parent ColumnsNames[] + ImGuiTableColumnIdx DisplayOrder; // Index within Table's IndexToDisplayOrder[] (column may be reordered by users) + ImGuiTableColumnIdx IndexWithinEnabledSet; // Index within enabled/visible set (<= IndexToDisplayOrder) + ImGuiTableColumnIdx PrevEnabledColumn; // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column + ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column + ImGuiTableColumnIdx SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort + ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] + ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) + ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows + bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 + bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). + bool IsUserEnabledNextFrame; + bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). + bool IsVisibleY; + bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. + bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen). + bool IsPreserveWidthAuto; + ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte + ImU8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit + ImU8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem + ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending + ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3) + ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each) + ImU8 SortDirectionsAvailList; // Ordered list of available sort directions (2-bits each, total 8-bits) + + ImGuiTableColumn() + { + memset(this, 0, sizeof(*this)); + StretchWeight = WidthRequest = -1.0f; + NameOffset = -1; + DisplayOrder = IndexWithinEnabledSet = -1; + PrevEnabledColumn = NextEnabledColumn = -1; + SortOrder = -1; + SortDirection = ImGuiSortDirection_None; + DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1; + } +}; + +// Transient cell data stored per row. +// sizeof() ~ 6 bytes +struct ImGuiTableCellData +{ + ImU32 BgColor; // Actual color + ImGuiTableColumnIdx Column; // Column number +}; + +// Parameters for TableAngledHeadersRowEx() +// This may end up being refactored for more general purpose. +// sizeof() ~ 12 bytes +struct ImGuiTableHeaderData +{ + ImGuiTableColumnIdx Index; // Column index + ImU32 TextColor; + ImU32 BgColor0; + ImU32 BgColor1; +}; + +// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?) +// sizeof() ~ 24 bytes +struct ImGuiTableInstanceData +{ + ImGuiID TableInstanceID; + float LastOuterHeight; // Outer height from last frame + float LastTopHeadersRowHeight; // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set) + float LastFrozenHeight; // Height of frozen section from last frame + int HoveredRowLast; // Index of row which was hovered last frame. + int HoveredRowNext; // Index of row hovered this frame, set after encountering it. + + ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } +}; + +// sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory() +struct IMGUI_API ImGuiTable +{ + ImGuiID ID; + ImGuiTableFlags Flags; + void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] + ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] + ImSpan Columns; // Point within RawData[] + ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) + ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. + ImBitArrayPtr EnabledMaskByDisplayOrder; // Column DisplayOrder -> IsEnabled map + ImBitArrayPtr EnabledMaskByIndex; // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data + ImBitArrayPtr VisibleMaskByIndex; // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect) + ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order) + int SettingsOffset; // Offset in g.SettingsTables + int LastFrameActive; + int ColumnsCount; // Number of columns declared in BeginTable() + int CurrentRow; + int CurrentColumn; + ImS16 InstanceCurrent; // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched. + ImS16 InstanceInteracted; // Mark which instance (generally 0) of the same ID is being interacted with + float RowPosY1; + float RowPosY2; + float RowMinHeight; // Height submitted to TableNextRow() + float RowCellPaddingY; // Top and bottom padding. Reloaded during row change. + float RowTextBaseline; + float RowIndentOffsetX; + ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_ + ImGuiTableRowFlags LastRowFlags : 16; + int RowBgColorCounter; // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this. + ImU32 RowBgColor[2]; // Background color override for current row. + ImU32 BorderColorStrong; + ImU32 BorderColorLight; + float BorderX1; + float BorderX2; + float HostIndentX; + float MinColumnWidth; + float OuterPaddingX; + float CellPaddingX; // Padding from each borders. Locked in BeginTable()/Layout. + float CellSpacingX1; // Spacing between non-bordered cells. Locked in BeginTable()/Layout. + float CellSpacingX2; + float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details. + float ColumnsGivenWidth; // Sum of current column width + float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window + float ColumnsStretchSumWeights; // Sum of weight of all enabled stretching columns + float ResizedColumnNextWidth; + float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. + float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. + float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() + float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() + ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). + ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is + ImRect WorkRect; + ImRect InnerClipRect; + ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries + ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped + ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. + ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. + ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() + ImGuiWindow* OuterWindow; // Parent window for the table + ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) + ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names + ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly + ImGuiTableInstanceData InstanceDataFirst; + ImVector InstanceDataExtra; // FIXME-OPT: Using a small-vector pattern would be good. + ImGuiTableColumnSortSpecs SortSpecsSingle; + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. + ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() + ImGuiTableColumnIdx SortSpecsCount; + ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) + ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns using fixed width (<= ColumnsCount) + ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() + ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers + ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! + ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). + ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted. + ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. + ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. + ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. + ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. + ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) + ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 + ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. + ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. + ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. + ImGuiTableColumnIdx RightMostStretchedColumn; // Index of right-most stretched column. + ImGuiTableColumnIdx ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot + ImGuiTableColumnIdx FreezeRowsRequest; // Requested frozen rows count + ImGuiTableColumnIdx FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset) + ImGuiTableColumnIdx FreezeColumnsRequest; // Requested frozen columns count + ImGuiTableColumnIdx FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset) + ImGuiTableColumnIdx RowCellDataCurrent; // Index of current RowCellData[] entry in current row + ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. + ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] + ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; + bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. + bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). + bool IsInitializing; + bool IsSortSpecsDirty; + bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. + bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). + bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup() + bool IsSettingsRequestLoad; + bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data. + bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) + bool IsResetAllRequest; + bool IsResetDisplayOrderRequest; + bool IsUnfrozenRows; // Set when we got past the frozen row. + bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() + bool IsActiveIdAliveBeforeTable; + bool IsActiveIdInTable; + bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. + bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. + bool MemoryCompacted; + bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis + + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } +}; + +// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). +// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. +// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs. +// sizeof() ~ 136 bytes. +struct IMGUI_API ImGuiTableTempData +{ + int TableIndex; // Index in g.Tables.Buf[] pool + float LastTimeActive; // Last timestamp this structure was used + float AngledHeadersExtraWidth; // Used in EndTable() + ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() + + ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() + ImDrawListSplitter DrawSplitter; + + ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() + ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() + ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() + ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() + ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() + + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } +}; + +// sizeof() ~ 12 +struct ImGuiTableColumnSettings +{ + float WidthOrWeight; + ImGuiID UserID; + ImGuiTableColumnIdx Index; + ImGuiTableColumnIdx DisplayOrder; + ImGuiTableColumnIdx SortOrder; + ImU8 SortDirection : 2; + ImU8 IsEnabled : 1; // "Visible" in ini file + ImU8 IsStretch : 1; + + ImGuiTableColumnSettings() + { + WidthOrWeight = 0.0f; + UserID = 0; + Index = -1; + DisplayOrder = SortOrder = -1; + SortDirection = ImGuiSortDirection_None; + IsEnabled = 1; + IsStretch = 0; + } +}; + +// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.) +struct ImGuiTableSettings +{ + ImGuiID ID; // Set to 0 to invalidate/delete the setting + ImGuiTableFlags SaveFlags; // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..) + float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. + ImGuiTableColumnIdx ColumnsCount; + ImGuiTableColumnIdx ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher + bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) + + ImGuiTableSettings() { memset(this, 0, sizeof(*this)); } + ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] ImGui internal API +// No guarantee of forward compatibility here! +//----------------------------------------------------------------------------- + +namespace ImGui +{ + // Windows + // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) + // If this ever crashes because g.CurrentWindow is NULL, it means that either: + // - ImGui::NewFrame() has never been called, which is illegal. + // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. + IMGUI_API ImGuiIO& GetIOEx(ImGuiContext* ctx); + inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } + inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); + IMGUI_API ImGuiWindow* FindWindowByName(const char* name); + IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); + IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window); + IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); + IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); + IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); + IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); + IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); + IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); + IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); + inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } + inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } + inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } + inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } + + // Windows: Display Order and Focus Order + IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); + IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags); + IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); + IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); + IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); + + // Windows: Idle, Refresh Policies [EXPERIMENTAL] + IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); + + // Fonts, drawing + IMGUI_API void SetCurrentFont(ImFont* font); + inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } + inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); + + // Init + IMGUI_API void Initialize(); + IMGUI_API void Shutdown(); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). + + // NewFrame + IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); + IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); + IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); + IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); + IMGUI_API void UpdateMouseMovingWindowNewFrame(); + IMGUI_API void UpdateMouseMovingWindowEndFrame(); + + // Generic context hooks + IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); + IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); + IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); + + // Viewports + IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); + + // Settings + IMGUI_API void MarkIniSettingsDirty(); + IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); + IMGUI_API void ClearIniSettings(); + IMGUI_API void AddSettingsHandler(const ImGuiSettingsHandler* handler); + IMGUI_API void RemoveSettingsHandler(const char* type_name); + IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); + + // Settings - Windows + IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); + IMGUI_API ImGuiWindowSettings* FindWindowSettingsByID(ImGuiID id); + IMGUI_API ImGuiWindowSettings* FindWindowSettingsByWindow(ImGuiWindow* window); + IMGUI_API void ClearWindowSettings(const char* name); + + // Localization + IMGUI_API void LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count); + inline const char* LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : "*Missing Text*"; } + + // Scrolling + IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x); + IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); + IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); + IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); + + // Early work-in-progress API (ScrollToItem() will become public) + IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); + IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); + IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); +//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } +//#endif + + // Basic Accessors + inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } + inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; } + inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } + inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } + IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); + IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); + IMGUI_API void ClearActiveID(); + IMGUI_API ImGuiID GetHoveredID(); + IMGUI_API void SetHoveredID(ImGuiID id); + IMGUI_API void KeepAliveID(ImGuiID id); + IMGUI_API void MarkItemEdited(ImGuiID id); // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function. + IMGUI_API void PushOverrideID(ImGuiID id); // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes) + IMGUI_API ImGuiID GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed); + IMGUI_API ImGuiID GetIDWithSeed(int n, ImGuiID seed); + + // Basic Helpers for widget code + IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); + inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. + IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); + IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); + IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); + IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); + IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); + IMGUI_API void PushMultiItemsWidths(int components, float width_full); + IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); + + // Parameter stacks (shared) + IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); + IMGUI_API void BeginDisabledOverrideReenable(); + IMGUI_API void EndDisabledOverrideReenable(); + + // Logging/Capture + IMGUI_API void LogBegin(ImGuiLogFlags flags, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. + IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer + IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); + IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix); + + // Childs + IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags); + + // Popups, Modals + IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags); + IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); + IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsExceptModals(); + IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); + IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); + IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); + IMGUI_API ImGuiWindow* FindBlockingModal(ImGuiWindow* window); + IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); + IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + + // Tooltips + IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); + IMGUI_API bool BeginTooltipHidden(); + + // Menus + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); + IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); + IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); + + // Combos + IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); + IMGUI_API bool BeginComboPreview(); + IMGUI_API void EndComboPreview(); + + // Keyboard/Gamepad Navigation + IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void NavInitRequestApplyResult(); + IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); + IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data); + IMGUI_API void NavMoveRequestCancel(); + IMGUI_API void NavMoveRequestApplyResult(); + IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavHighlightActivated(ImGuiID id); + IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis); + IMGUI_API void SetNavCursorVisibleAfterMove(); + IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX(); + IMGUI_API void SetNavWindow(ImGuiWindow* window); + IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavFocusScope(ImGuiID focus_scope_id); + + // Focus/Activation + // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are + // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones. + IMGUI_API void FocusItem(); // Focus last item (no selection/activation). + IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. + + // Inputs + // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. + inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } + inline bool IsNamedKeyOrMod(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; } + inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } + inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } + inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } + inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } + inline bool IsLRModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } + ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord); + inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) + { + if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; + if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; + if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; + if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper; + return key; + } + + IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key); + inline ImGuiKeyData* GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); } + IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord); + inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } + IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); + IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); + IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis); + IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); + IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate); + IMGUI_API void TeleportMousePos(const ImVec2& pos); + IMGUI_API void SetActiveIdUsingAllKeyboardKeys(); + inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } + + // [EXPERIMENTAL] Low-Level: Key/Input Ownership + // - The idea is that instead of "eating" a given input, we can link to an owner id. + // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead). + // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. + // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0). + // - Input ownership is automatically released on the frame after a key is released. Therefore: + // - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case). + // - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover). + // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state. + // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step. + // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. + IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); + IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' + inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } + + // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership + // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag. + // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0) + // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'. + // Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. + // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API. + IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat. + IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0); + IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); + IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id); + + // Shortcut Testing & Routing + // - Set Shortcut() and SetNextItemShortcut() in imgui.h + // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(), + // allowing the system to decide where to route the input among other route-aware calls. + // (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check) + // - When using one of the routing option: + // - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.) + // - Routes are requested given a chord (key + modifiers) and a routing policy. + // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame. + // - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window). + // - Multiple read sites may use the same owner id can all access the granted route. + // - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location. + // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute() + // e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused. + IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); + IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0 + IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); + IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); + + // [EXPERIMENTAL] Focus Scope + // This is generally used to identify a unique input location (for e.g. a selection set) + // There is one per window (automatically set in Begin), but: + // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set. + // So in order to identify a set multiple lists in same window may each need a focus scope. + // If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope() + // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided. + // We don't use the ID Stack for this as it is common to want them separate. + IMGUI_API void PushFocusScope(ImGuiID id); + IMGUI_API void PopFocusScope(); + inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope() + + // Drag and Drop + IMGUI_API bool IsDragDropActive(); + IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); + IMGUI_API void ClearDragDrop(); + IMGUI_API bool IsDragDropPayloadBeingAccepted(); + IMGUI_API void RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect); + + // Typing-Select API + // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature) + // (this is currently not documented nor used by main library, but should work. See "widgets_typingselect" in imgui_test_suite for usage code. Please let us know if you use this!) + IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None); + IMGUI_API int TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); + IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); + IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); + + // Box-Select API + IMGUI_API bool BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags); + IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags); + + // Multi-Select API + IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags); + IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed); + IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected); + IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item); + inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; } + inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); } + + // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) + IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); + IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). + IMGUI_API void EndColumns(); // close columns + IMGUI_API void PushColumnClipRect(int column_index); + IMGUI_API void PushColumnsBackground(); + IMGUI_API void PopColumnsBackground(); + IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count); + IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id); + IMGUI_API float GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm); + IMGUI_API float GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset); + + // Tables: Candidates for public API + IMGUI_API void TableOpenContextMenu(int column_n = -1); + IMGUI_API void TableSetColumnWidth(int column_n, float width); + IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); + IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. + IMGUI_API float TableGetHeaderRowHeight(); + IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); + IMGUI_API void TablePushBackgroundChannel(); + IMGUI_API void TablePopBackgroundChannel(); + IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count); + + // Tables: Internals + inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } + IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); + IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); + IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); + IMGUI_API void TableBeginApplyRequests(ImGuiTable* table); + IMGUI_API void TableSetupDrawChannels(ImGuiTable* table); + IMGUI_API void TableUpdateLayout(ImGuiTable* table); + IMGUI_API void TableUpdateBorders(ImGuiTable* table); + IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); + IMGUI_API void TableDrawBorders(ImGuiTable* table); + IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); + IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); + IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); + inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } + inline ImGuiID TableGetInstanceID(ImGuiTable* table, int instance_no) { return TableGetInstanceData(table, instance_no)->TableInstanceID; } + IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); + IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); + IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column); + IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column); + IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column); + IMGUI_API void TableBeginRow(ImGuiTable* table); + IMGUI_API void TableEndRow(ImGuiTable* table); + IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n); + IMGUI_API void TableEndCell(ImGuiTable* table); + IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); + IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); + IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); + IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); + IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); + IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); + IMGUI_API void TableRemove(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); + IMGUI_API void TableGcCompactSettings(); + + // Tables: Settings + IMGUI_API void TableLoadSettings(ImGuiTable* table); + IMGUI_API void TableSaveSettings(ImGuiTable* table); + IMGUI_API void TableResetSettings(ImGuiTable* table); + IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table); + IMGUI_API void TableSettingsAddSettingsHandler(); + IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count); + IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id); + + // Tab Bars + inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; } + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); + IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order); + IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar); + inline int TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); } + IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name); + IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset); + IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); + IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); + IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); + IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); + IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); + + // Render helpers + // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. + // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) + IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); + IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); + IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); + IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); + IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); + IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); + IMGUI_API void RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4 +#endif + IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. + IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); + + // Render helpers (those functions don't access any ImGui state!) + IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); + IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); + IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); + IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); + IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); + + // Widgets + IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); + IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); + IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); + IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f); + IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width); + IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); + IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); + + // Widgets: Window Decorations + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API void Scrollbar(ImGuiAxis axis); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); + IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); + IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); + IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners + IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); + + // Widgets low-level behaviors + IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); + IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); + IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); + + // Widgets: Tree Nodes + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id); + IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open); + IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. + + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. + // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). + // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " + template IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); + template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); + template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags); + template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); + template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); + template IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value); + + // Data type helpers + IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); + IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); + IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); + IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL); + IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); + IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); + IMGUI_API bool DataTypeIsZero(ImGuiDataType data_type, const void* p_data); + + // InputText + IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API void InputTextDeactivateHook(ImGuiID id); + IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); + IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); + inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } + inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active + IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); + + // Color + IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); + IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); + IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); + + // Plot + IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg); + + // Shade functions (write over already created vertices) + IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); + IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); + IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out); + + // Garbage collection + IMGUI_API void GcCompactTransientMiscBuffers(); + IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); + IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); + + // Error handling, State Recovery + IMGUI_API bool ErrorLog(const char* msg); + IMGUI_API void ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out); + IMGUI_API void ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in); + IMGUI_API void ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in); + IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip(); + IMGUI_API bool BeginErrorTooltip(); + IMGUI_API void EndErrorTooltip(); + + // Debug Tools + IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free + IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end); + IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time! + IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time! + IMGUI_API void DebugLocateItemResolveWithLastItem(); + IMGUI_API void DebugBreakClearData(); + IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location); + IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location); + IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); + IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); + IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); + IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); + IMGUI_API void DebugNodeFont(ImFont* font); + IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); + IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); + IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); + IMGUI_API void DebugNodeTable(ImGuiTable* table); + IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); + IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); + IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); + IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state); + IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); + IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); + IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); + IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); + IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); + + // Obsolete functions +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + //inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89 + //inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89 + //inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! + + // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' + //inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + //inline void FocusableItemUnregister(ImGuiWindow* window) // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif + +} // namespace ImGui + + +//----------------------------------------------------------------------------- +// [SECTION] ImFontAtlas internal API +//----------------------------------------------------------------------------- + +// This structure is likely to evolve as we add support for incremental atlas updates +struct ImFontBuilderIO +{ + bool (*FontBuilder_Build)(ImFontAtlas* atlas); +}; + +// Helper for font builder +#ifdef IMGUI_ENABLE_STB_TRUETYPE +IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); +#endif +IMGUI_API void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); +IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); +IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value); +IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); +IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); + +//----------------------------------------------------------------------------- +// [SECTION] Test Engine specific hooks (imgui_test_engine) +//----------------------------------------------------------------------------- + +#ifdef IMGUI_ENABLE_TEST_ENGINE +extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data); // item_data may be NULL +extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); +extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + +// In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data); +#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA) // Register item bounding box +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) +#define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log +#else +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) +#endif + +//----------------------------------------------------------------------------- + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_tables.cpp b/lib/imgui/imgui_tables.cpp new file mode 100644 index 0000000..e36e6f1 --- /dev/null +++ b/lib/imgui/imgui_tables.cpp @@ -0,0 +1,4467 @@ +// dear imgui, v1.91.5 +// (tables and columns code) + +/* + +Index of this file: + +// [SECTION] Commentary +// [SECTION] Header mess +// [SECTION] Tables: Main code +// [SECTION] Tables: Simple accessors +// [SECTION] Tables: Row changes +// [SECTION] Tables: Columns changes +// [SECTION] Tables: Columns width management +// [SECTION] Tables: Drawing +// [SECTION] Tables: Sorting +// [SECTION] Tables: Headers +// [SECTION] Tables: Context Menu +// [SECTION] Tables: Settings (.ini data) +// [SECTION] Tables: Garbage Collection +// [SECTION] Tables: Debugging +// [SECTION] Columns, BeginColumns, EndColumns, etc. + +*/ + +// Navigating this file: +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. + +//----------------------------------------------------------------------------- +// [SECTION] Commentary +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Typical tables call flow: (root level is generally public API): +//----------------------------------------------------------------------------- +// - BeginTable() user begin into a table +// | BeginChild() - (if ScrollX/ScrollY is set) +// | TableBeginInitMemory() - first time table is used +// | TableResetSettings() - on settings reset +// | TableLoadSettings() - on settings load +// | TableBeginApplyRequests() - apply queued resizing/reordering/hiding requests +// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame) +// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width +// - TableSetupColumn() user submit columns details (optional) +// - TableSetupScrollFreeze() user submit scroll freeze information (optional) +//----------------------------------------------------------------------------- +// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). +// | TableSetupDrawChannels() - setup ImDrawList channels +// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission +// | TableBeginContextMenuPopup() +// | - TableDrawDefaultContextMenu() - draw right-click context menu contents +//----------------------------------------------------------------------------- +// - TableHeadersRow() or TableHeader() user submit a headers row (optional) +// | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction +// | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu +// - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers) +// - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow()) +// | TableEndRow() - finish existing row +// | TableBeginRow() - add a new row +// - TableSetColumnIndex() / TableNextColumn() user begin into a cell +// | TableEndCell() - close existing column/cell +// | TableBeginCell() - enter into current column/cell +// - [...] user emit contents +//----------------------------------------------------------------------------- +// - EndTable() user ends the table +// | TableDrawBorders() - draw outer borders, inner vertical borders +// | TableMergeDrawChannels() - merge draw channels if clipping isn't required +// | EndChild() - (if ScrollX/ScrollY is set) +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// TABLE SIZING +//----------------------------------------------------------------------------- +// (Read carefully because this is subtle but it does make sense!) +//----------------------------------------------------------------------------- +// About 'outer_size': +// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags. +// Default value is ImVec2(0.0f, 0.0f). +// X +// - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. +// - outer_size.x > 0.0f -> Set Fixed width. +// Y with ScrollX/ScrollY disabled: we output table directly in current window +// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll. +// - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set) +// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtendY is set) +// Y with ScrollX/ScrollY enabled: using a child window for scrolling +// - outer_size.y < 0.0f -> Bottom-align. Not meaningful if parent window can vertically scroll. +// - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window. +// - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis. +//----------------------------------------------------------------------------- +// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. +// Important to note how the two flags have slightly different behaviors! +// - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. +// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. +// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. +// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable). +//----------------------------------------------------------------------------- +// About 'inner_width': +// With ScrollX disabled: +// - inner_width -> *ignored* +// With ScrollX enabled: +// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird +// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns. +// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space! +//----------------------------------------------------------------------------- +// Details: +// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept +// of "available space" doesn't make sense. +// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding +// of what the value does. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// COLUMNS SIZING POLICIES +// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags) +//----------------------------------------------------------------------------- +// About overriding column sizing policy and width/weight with TableSetupColumn(): +// We use a default parameter of -1 for 'init_width'/'init_weight'. +// - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic +// - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom +// - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f +// - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom +// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f) +// and you can fit a 100.0f wide item in it without clipping and with padding honored. +//----------------------------------------------------------------------------- +// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) +// - with Table policy ImGuiTableFlags_SizingFixedFit --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width +// - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width +// - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f +// - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents +// Default Width and default Weight can be overridden when calling TableSetupColumn(). +//----------------------------------------------------------------------------- +// About mixing Fixed/Auto and Stretch columns together: +// - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. +// - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! +// that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. +// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. +// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths. +//----------------------------------------------------------------------------- +// About using column width: +// If a column is manually resizable or has a width specified with TableSetupColumn(): +// - you may use GetContentRegionAvail().x to query the width available in a given column. +// - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. +// If the column is not resizable and has no width specified with TableSetupColumn(): +// - its width will be automatic and be set to the max of items submitted. +// - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN). +// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// TABLES CLIPPING/CULLING +//----------------------------------------------------------------------------- +// About clipping/culling of Rows in Tables: +// - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows. +// ImGuiListClipper is reliant on the fact that rows are of equal height. +// See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper. +// - Note that auto-resizing columns don't play well with using the clipper. +// By default a table with _ScrollX but without _Resizable will have column auto-resize. +// So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed. +//----------------------------------------------------------------------------- +// About clipping/culling of Columns in Tables: +// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing +// width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know +// it is not going to contribute to row height. +// In many situations, you may skip submitting contents for every column but one (e.g. the first one). +// - Case A: column is not hidden by user, and at least partially in sight (most common case). +// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output. +// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). +// +// [A] [B] [C] +// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height. +// SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output. +// ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. +// ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). +// +// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. +// However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer. +//----------------------------------------------------------------------------- +// About clipping/culling of whole Tables: +// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// [SECTION] Header mess +//----------------------------------------------------------------------------- + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_internal.h" + +// System includes +#include // intptr_t + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#if __has_warning("-Wunknown-warning-option") +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access +#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#endif + +//----------------------------------------------------------------------------- +// [SECTION] Tables: Main code +//----------------------------------------------------------------------------- +// - TableFixFlags() [Internal] +// - TableFindByID() [Internal] +// - BeginTable() +// - BeginTableEx() [Internal] +// - TableBeginInitMemory() [Internal] +// - TableBeginApplyRequests() [Internal] +// - TableSetupColumnFlags() [Internal] +// - TableUpdateLayout() [Internal] +// - TableUpdateBorders() [Internal] +// - EndTable() +// - TableSetupColumn() +// - TableSetupScrollFreeze() +//----------------------------------------------------------------------------- + +// Configuration +static const int TABLE_DRAW_CHANNEL_BG0 = 0; +static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1; +static const int TABLE_DRAW_CHANNEL_NOCLIP = 2; // When using ImGuiTableFlags_NoClip (this becomes the last visible channel) +static const float TABLE_BORDER_SIZE = 1.0f; // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering. +static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f; // Extend outside inner borders. +static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped. + +// Helper +inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window) +{ + // Adjust flags: set default sizing policy + if ((flags & ImGuiTableFlags_SizingMask_) == 0) + flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame; + + // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame + if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) + flags |= ImGuiTableFlags_NoKeepColumnsVisible; + + // Adjust flags: enforce borders when resizable + if (flags & ImGuiTableFlags_Resizable) + flags |= ImGuiTableFlags_BordersInnerV; + + // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on + if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) + flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY); + + // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody + if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize) + flags &= ~ImGuiTableFlags_NoBordersInBody; + + // Adjust flags: disable saved settings if there's nothing to save + if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0) + flags |= ImGuiTableFlags_NoSavedSettings; + + // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) + if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) + flags |= ImGuiTableFlags_NoSavedSettings; + + return flags; +} + +ImGuiTable* ImGui::TableFindByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.Tables.GetByKey(id); +} + +// Read about "TABLE SIZING" at the top of this file. +bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) +{ + ImGuiID id = GetID(str_id); + return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width); +} + +bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* outer_window = GetCurrentWindow(); + if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible. + return false; + + // Sanity checks + IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS); + if (flags & ImGuiTableFlags_ScrollX) + IM_ASSERT(inner_width >= 0.0f); + + // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve. + // FIXME: coarse clipping because access to table data causes two issues: + // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine. + // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers. + // The side-effects of accessing table data on coarse clip would be: + // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations. + // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. + const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; + const ImVec2 avail_size = GetContentRegionAvail(); + const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f)); + const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); + const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! + if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) + { + ItemSize(outer_rect); + ItemAdd(outer_rect, id); + return false; + } + + // [DEBUG] Debug break requested by user + if (g.DebugBreakInTable == id) + IM_DEBUG_BREAK(); + + // Acquire storage for the table + ImGuiTable* table = g.Tables.GetOrAddByKey(id); + + // Acquire temporary buffers + const int table_idx = g.Tables.GetIndex(table); + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; + temp_data->TableIndex = table_idx; + table->DrawSplitter = &table->TempData->DrawSplitter; + table->DrawSplitter->Clear(); + + // Fix flags + table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; + flags = TableFixFlags(flags, outer_window); + + // Initialize + const int previous_frame_active = table->LastFrameActive; + const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; + const ImGuiTableFlags previous_flags = table->Flags; + table->ID = id; + table->Flags = flags; + table->LastFrameActive = g.FrameCount; + table->OuterWindow = table->InnerWindow = outer_window; + table->ColumnsCount = columns_count; + table->IsLayoutLocked = false; + table->InnerWidth = inner_width; + temp_data->UserOuterSize = outer_size; + + // Instance data (for instance 0, TableID == TableInstanceID) + ImGuiID instance_id; + table->InstanceCurrent = (ImS16)instance_no; + if (instance_no > 0) + { + IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); + if (table->InstanceDataExtra.Size < instance_no) + table->InstanceDataExtra.push_back(ImGuiTableInstanceData()); + instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instances" followed by (int)instance_no in ID stack. + } + else + { + instance_id = id; + } + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + table_instance->TableInstanceID = instance_id; + + // When not using a child window, WorkRect.Max will grow as we append contents. + if (use_child_window) + { + // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent + // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing) + ImVec2 override_content_size(FLT_MAX, FLT_MAX); + if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY)) + override_content_size.y = FLT_MIN; + + // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and + // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align + // based on the right side of the child window work rect, which would require knowing ahead if we are going to + // have decoration taking horizontal spaces (typically a vertical scrollbar). + if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f) + override_content_size.x = inner_width; + + if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX) + SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f)); + + // Reset scroll if we are reactivating it + if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) + SetNextWindowScroll(ImVec2(0.0f, 0.0f)); + + // Create scrolling region (without border and zero window padding) + ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; + BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags); + table->InnerWindow = g.CurrentWindow; + table->WorkRect = table->InnerWindow->WorkRect; + table->OuterRect = table->InnerWindow->Rect(); + table->InnerRect = table->InnerWindow->InnerRect; + IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); + + // Allow submitting when host is measuring + if (table->InnerWindow->SkipItems && outer_window_is_measuring_size) + table->InnerWindow->SkipItems = false; + + // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) + if (instance_no == 0) + { + table->HasScrollbarYPrev = table->HasScrollbarYCurr; + table->HasScrollbarYCurr = false; + } + table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY; + } + else + { + // For non-scrolling tables, WorkRect == OuterRect == InnerRect. + // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). + table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; + table->HasScrollbarYPrev = table->HasScrollbarYCurr = false; + } + + // Push a standardized ID for both child-using and not-child-using tables + PushOverrideID(id); + if (instance_no > 0) + PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol. + + // Backup a copy of host window members we will modify + ImGuiWindow* inner_window = table->InnerWindow; + table->HostIndentX = inner_window->DC.Indent.x; + table->HostClipRect = inner_window->ClipRect; + table->HostSkipItems = inner_window->SkipItems; + temp_data->HostBackupWorkRect = inner_window->WorkRect; + temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; + temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; + temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; + temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; + temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; + temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; + inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + + // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752) + // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the + // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap + // problem only affect scrolling tables in this case we can get away with doing it without extra cost). + if (inner_window != outer_window) + { + // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, + // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with + // different x/y values to BeginChild(). + if (flags & ImGuiTableFlags_BordersOuterV) + { + table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); + if (inner_window->DecoOuterSizeX2 == 0.0f) + table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x); + } + if (flags & ImGuiTableFlags_BordersOuterH) + { + table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); + if (inner_window->DecoOuterSizeY2 == 0.0f) + table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y); + } + } + + // Padding and Spacing + // - None ........Content..... Pad .....Content........ + // - PadOuter | Pad ..Content..... Pad .....Content.. Pad | + // - PadInner ........Content.. Pad | Pad ..Content........ + // - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad | + const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0; + const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true; + const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f; + const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f; + const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f; + table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border; + table->CellSpacingX2 = inner_spacing_explicit; + table->CellPaddingX = inner_padding_explicit; + + const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; + const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; + table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX; + + table->CurrentColumn = -1; + table->CurrentRow = -1; + table->RowBgColorCounter = 0; + table->LastRowFlags = ImGuiTableRowFlags_None; + table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; + table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width + table->InnerClipRect.ClipWithFull(table->HostClipRect); + table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y; + + table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow + table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() + table->RowCellPaddingY = 0.0f; + table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any + table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; + table->IsUnfrozenRows = true; + table->DeclColumnsCount = table->AngledHeadersCount = 0; + if (previous_frame_active + 1 < g.FrameCount) + table->IsActiveIdInTable = false; + table->AngledHeadersHeight = 0.0f; + temp_data->AngledHeadersExtraWidth = 0.0f; + + // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() + table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); + table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); + + // Make table current + g.CurrentTable = table; + outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); + outer_window->DC.CurrentTableIdx = table_idx; + if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. + inner_window->DC.CurrentTableIdx = table_idx; + + if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) + table->IsResetDisplayOrderRequest = true; + + // Mark as used to avoid GC + if (table_idx >= g.TablesLastTimeActive.Size) + g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); + g.TablesLastTimeActive[table_idx] = (float)g.Time; + temp_data->LastTimeActive = (float)g.Time; + table->MemoryCompacted = false; + + // Setup memory buffer (clear data if columns count changed) + ImGuiTableColumn* old_columns_to_preserve = NULL; + void* old_columns_raw_data = NULL; + const int old_columns_count = table->Columns.size(); + if (old_columns_count != 0 && old_columns_count != columns_count) + { + // Attempt to preserve width on column count change (#4046) + old_columns_to_preserve = table->Columns.Data; + old_columns_raw_data = table->RawData; + table->RawData = NULL; + } + if (table->RawData == NULL) + { + TableBeginInitMemory(table, columns_count); + table->IsInitializing = table->IsSettingsRequestLoad = true; + } + if (table->IsResetAllRequest) + TableResetSettings(table); + if (table->IsInitializing) + { + // Initialize + table->SettingsOffset = -1; + table->IsSortSpecsDirty = true; + table->InstanceInteracted = -1; + table->ContextPopupColumn = -1; + table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1; + table->AutoFitSingleColumn = -1; + table->HoveredColumnBody = table->HoveredColumnBorder = -1; + for (int n = 0; n < columns_count; n++) + { + ImGuiTableColumn* column = &table->Columns[n]; + if (old_columns_to_preserve && n < old_columns_count) + { + // FIXME: We don't attempt to preserve column order in this path. + *column = old_columns_to_preserve[n]; + } + else + { + float width_auto = column->WidthAuto; + *column = ImGuiTableColumn(); + column->WidthAuto = width_auto; + column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; + } + column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; + } + } + if (old_columns_raw_data) + IM_FREE(old_columns_raw_data); + + // Load settings + if (table->IsSettingsRequestLoad) + TableLoadSettings(table); + + // Handle DPI/font resize + // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well. + // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition. + // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory. + // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path. + const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ? + if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit) + { + const float scale_factor = new_ref_scale_unit / table->RefScale; + //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor); + for (int n = 0; n < columns_count; n++) + table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor; + } + table->RefScale = new_ref_scale_unit; + + // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call.. + // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user. + // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option. + inner_window->SkipItems = true; + + // Clear names + // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn() + if (table->ColumnsNames.Buf.Size > 0) + table->ColumnsNames.Buf.resize(0); + + // Apply queued resizing/reordering/hiding requests + TableBeginApplyRequests(table); + + return true; +} + +// For reference, the average total _allocation count_ for a table is: +// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[]) +// + 1 (for table->RawData allocated below) +// + 1 (for table->ColumnsNames, if names are used) +// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number) +// + 1 (for table->Splitter._Channels) +// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) +// Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details. +// Unused channels don't perform their +2 allocations. +void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) +{ + // Allocate single buffer for our arrays + const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count); + ImSpanAllocator<6> span_allocator; + span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn)); + span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx)); + span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4); + for (int n = 3; n < 6; n++) + span_allocator.Reserve(n, columns_bit_array_size); + table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes()); + memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes()); + span_allocator.SetArenaBasePtr(table->RawData); + span_allocator.GetSpan(0, &table->Columns); + span_allocator.GetSpan(1, &table->DisplayOrderToIndex); + span_allocator.GetSpan(2, &table->RowCellData); + table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3); + table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4); + table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5); +} + +// Apply queued resizing/reordering/hiding requests +void ImGui::TableBeginApplyRequests(ImGuiTable* table) +{ + // Handle resizing request + // (We process this in the TableBegin() of the first instance of each table) + // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling? + if (table->InstanceCurrent == 0) + { + if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX) + TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth); + table->LastResizedColumn = table->ResizedColumn; + table->ResizedColumnNextWidth = FLT_MAX; + table->ResizedColumn = -1; + + // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy. + // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings. + if (table->AutoFitSingleColumn != -1) + { + TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto); + table->AutoFitSingleColumn = -1; + } + } + + // Handle reordering request + // Note: we don't clear ReorderColumn after handling the request. + if (table->InstanceCurrent == 0) + { + if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) + table->ReorderColumn = -1; + table->HeldHeaderColumn = -1; + if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0) + { + // We need to handle reordering across hidden columns. + // In the configuration below, moving C to the right of E will lead to: + // ... C [D] E ---> ... [D] E C (Column name/index) + // ... 2 3 4 ... 2 3 4 (Display order) + const int reorder_dir = table->ReorderColumnDir; + IM_ASSERT(reorder_dir == -1 || reorder_dir == +1); + IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); + ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; + ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; + IM_UNUSED(dst_column); + const int src_order = src_column->DisplayOrder; + const int dst_order = dst_column->DisplayOrder; + src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order; + for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir) + table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; + IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); + + // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former. + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; + table->ReorderColumnDir = 0; + table->IsSettingsDirty = true; + } + } + + // Handle display order reset request + if (table->IsResetDisplayOrderRequest) + { + for (int n = 0; n < table->ColumnsCount; n++) + table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n; + table->IsResetDisplayOrderRequest = false; + table->IsSettingsDirty = true; + } +} + +// Adjust flags: default width mode + stretch columns are not allowed when auto extending +static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) +{ + ImGuiTableColumnFlags flags = flags_in; + + // Sizing Policy + if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) + { + const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); + if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame) + flags |= ImGuiTableColumnFlags_WidthFixed; + else + flags |= ImGuiTableColumnFlags_WidthStretch; + } + else + { + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. + } + + // Resize + if ((table->Flags & ImGuiTableFlags_Resizable) == 0) + flags |= ImGuiTableColumnFlags_NoResize; + + // Sorting + if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending)) + flags |= ImGuiTableColumnFlags_NoSort; + + // Indentation + if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0) + flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable; + + // Alignment + //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0) + // flags |= ImGuiTableColumnFlags_AlignCenter; + //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used. + + // Preserve status flags + column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_); + + // Build an ordered list of available sort directions + column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0; + if (table->Flags & ImGuiTableFlags_Sortable) + { + int count = 0, mask = 0, list = 0; + if ((flags & ImGuiTableColumnFlags_PreferSortAscending) != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } + if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } + if ((flags & ImGuiTableColumnFlags_PreferSortAscending) == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending) == 0) { mask |= 1 << ImGuiSortDirection_Ascending; list |= ImGuiSortDirection_Ascending << (count << 1); count++; } + if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; } + if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; } + column->SortDirectionsAvailList = (ImU8)list; + column->SortDirectionsAvailMask = (ImU8)mask; + column->SortDirectionsAvailCount = (ImU8)count; + ImGui::TableFixColumnSortDirection(table, column); + } +} + +// Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function. +// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first. +// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns. +// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns? +void ImGui::TableUpdateLayout(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(table->IsLayoutLocked == false); + + const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_); + table->IsDefaultDisplayOrder = true; + table->ColumnsEnabledCount = 0; + ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount); + ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount); + table->LeftMostEnabledColumn = -1; + table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE + + // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns. + // Process columns in their visible orders as we are building the Prev/Next indices. + int count_fixed = 0; // Number of columns that have fixed sizing policies + int count_stretch = 0; // Number of columns that have stretch sizing policies + int prev_visible_column_idx = -1; + bool has_auto_fit_request = false; + bool has_resizable = false; + float stretch_sum_width_auto = 0.0f; + float fixed_max_width_auto = 0.0f; + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + const int column_n = table->DisplayOrderToIndex[order_n]; + if (column_n != order_n) + table->IsDefaultDisplayOrder = false; + ImGuiTableColumn* column = &table->Columns[column_n]; + + // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame. + // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway. + // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect. + if (table->DeclColumnsCount <= column_n) + { + TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None); + column->NameOffset = -1; + column->UserID = 0; + column->InitStretchWeightOrWidth = -1.0f; + } + + // Update Enabled state, mark settings and sort specs dirty + if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) + column->IsUserEnabledNextFrame = true; + if (column->IsUserEnabled != column->IsUserEnabledNextFrame) + { + column->IsUserEnabled = column->IsUserEnabledNextFrame; + table->IsSettingsDirty = true; + } + column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; + + if (column->SortOrder != -1 && !column->IsEnabled) + table->IsSortSpecsDirty = true; + if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) + table->IsSortSpecsDirty = true; + + // Auto-fit unsized columns + const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f); + if (start_auto_fit) + column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames + + if (!column->IsEnabled) + { + column->IndexWithinEnabledSet = -1; + continue; + } + + // Mark as enabled and link to previous/next enabled column + column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; + column->NextEnabledColumn = -1; + if (prev_visible_column_idx != -1) + table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; + else + table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; + column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; + ImBitArraySetBit(table->EnabledMaskByIndex, column_n); + ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder); + prev_visible_column_idx = column_n; + IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); + + // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) + // Combine width from regular rows + width from headers unless requested not to. + if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0) + column->WidthAuto = TableGetColumnWidthAuto(table, column); + + // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) + const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; + if (column_is_resizable) + has_resizable = true; + if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable) + column->WidthAuto = column->InitStretchWeightOrWidth; + + if (column->AutoFitQueue != 0x00) + has_auto_fit_request = true; + if (column->Flags & ImGuiTableColumnFlags_WidthStretch) + { + stretch_sum_width_auto += column->WidthAuto; + count_stretch++; + } + else + { + fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto); + count_fixed++; + } + } + if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) + table->IsSortSpecsDirty = true; + table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; + IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0); + + // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid + // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510. + // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time. + if (has_auto_fit_request && table->OuterWindow != table->InnerWindow) + table->InnerWindow->SkipItems = false; + if (has_auto_fit_request) + table->IsSettingsDirty = true; + + // [Part 3] Fix column flags and record a few extra information. + float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. + float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. + table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + continue; + ImGuiTableColumn* column = &table->Columns[column_n]; + + const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0; + if (column->Flags & ImGuiTableColumnFlags_WidthFixed) + { + // Apply same widths policy + float width_auto = column->WidthAuto; + if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable)) + width_auto = fixed_max_width_auto; + + // Apply automatic width + // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) + if (column->AutoFitQueue != 0x00) + column->WidthRequest = width_auto; + else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput) + column->WidthRequest = width_auto; + + // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets + // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very + // large height (= first frame scrollbar display very off + clipper would skip lots of items). + // This is merely making the side-effect less extreme, but doesn't properly fixes it. + // FIXME: Move this to ->WidthGiven to avoid temporary lossyless? + // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller. + if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto) + column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale? + sum_width_requests += column->WidthRequest; + } + else + { + // Initialize stretch weight + if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable) + { + if (column->InitStretchWeightOrWidth > 0.0f) + column->StretchWeight = column->InitStretchWeightOrWidth; + else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp) + column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch; + else + column->StretchWeight = 1.0f; + } + + stretch_sum_weights += column->StretchWeight; + if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder) + table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n; + if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder) + table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n; + } + column->IsPreserveWidthAuto = false; + sum_width_requests += table->CellPaddingX * 2.0f; + } + table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed; + table->ColumnsStretchSumWeights = stretch_sum_weights; + + // [Part 4] Apply final widths based on requested widths + const ImRect work_rect = table->WorkRect; + const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); + const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920) + const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed); + const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; + float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; + table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + continue; + ImGuiTableColumn* column = &table->Columns[column_n]; + + // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest) + if (column->Flags & ImGuiTableColumnFlags_WidthStretch) + { + float weight_ratio = column->StretchWeight / stretch_sum_weights; + column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); + width_remaining_for_stretched_columns -= column->WidthRequest; + } + + // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column + // See additional comments in TableSetColumnWidth(). + if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1) + column->Flags |= ImGuiTableColumnFlags_NoDirectResize_; + + // Assign final width, record width in case we will need to shrink + column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth)); + table->ColumnsGivenWidth += column->WidthGiven; + } + + // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column). + // Using right-to-left distribution (more likely to match resizing cursor). + if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) + for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + continue; + ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]]; + if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch)) + continue; + column->WidthRequest += 1.0f; + column->WidthGiven += 1.0f; + width_remaining_for_stretched_columns -= 1.0f; + } + + // Determine if table is hovered which will be used to flag columns as hovered. + // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + // but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily + // clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem). + // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop. + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + table_instance->HoveredRowLast = table_instance->HoveredRowNext; + table_instance->HoveredRowNext = -1; + table->HoveredColumnBody = table->HoveredColumnBorder = -1; + const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); + const ImGuiID backup_active_id = g.ActiveId; + g.ActiveId = 0; + const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); + g.ActiveId = backup_active_id; + + // Determine skewed MousePos.x to support angled headers. + float mouse_skewed_x = g.IO.MousePos.x; + if (table->AngledHeadersHeight > 0.0f) + if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight) + mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope); + + // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column + // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. + int visible_n = 0; + bool has_at_least_one_column_requesting_output = false; + bool offset_x_frozen = (table->FreezeColumnsCount > 0); + float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; + ImRect host_clip_rect = table->InnerClipRect; + //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2; + ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount); + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + + column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen + + if (offset_x_frozen && table->FreezeColumnsCount == visible_n) + { + offset_x += work_rect.Min.x - table->OuterRect.Min.x; + offset_x_frozen = false; + } + + // Clear status flags + column->Flags &= ~ImGuiTableColumnFlags_StatusMask_; + + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + { + // Hidden column: clear a few fields and we are done with it for the remainder of the function. + // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper. + column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x; + column->WidthGiven = 0.0f; + column->ClipRect.Min.y = work_rect.Min.y; + column->ClipRect.Max.y = FLT_MAX; + column->ClipRect.ClipWithFull(host_clip_rect); + column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false; + column->IsSkipItems = true; + column->ItemWidth = 1.0f; + continue; + } + + // Lock start position + column->MinX = offset_x; + + // Lock width based on start position and minimum/maximum width for this position + column->WidthMax = TableCalcMaxColumnWidth(table, column_n); + column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax); + column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); + column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; + + // Lock other positions + // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging. + // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. + // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. + // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. + const float previous_instance_work_min_x = column->WorkMinX; + column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; + column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max + column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f); + column->ClipRect.Min.x = column->MinX; + column->ClipRect.Min.y = work_rect.Min.y; + column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX; + column->ClipRect.Max.y = FLT_MAX; + column->ClipRect.ClipWithFull(host_clip_rect); + + // Mark column as Clipped (not in sight) + // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables. + // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY. + // Taking advantage of LastOuterHeight would yield good results there... + // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x, + // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else). + // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small. + column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x); + column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y); + const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY; + if (is_visible) + ImBitArraySetBit(table->VisibleMaskByIndex, column_n); + + // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. + column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; + + // Mark column as SkipItems (ignoring all items/layout) + // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2) + column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; + if (column->IsSkipItems) + IM_ASSERT(!is_visible); + if (column->IsRequestOutput && !column->IsSkipItems) + has_at_least_one_column_requesting_output = true; + + // Update status flags + column->Flags |= ImGuiTableColumnFlags_IsEnabled; + if (is_visible) + column->Flags |= ImGuiTableColumnFlags_IsVisible; + if (column->SortOrder != -1) + column->Flags |= ImGuiTableColumnFlags_IsSorted; + + // Detect hovered column + if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) + { + column->Flags |= ImGuiTableColumnFlags_IsHovered; + table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; + } + + // Alignment + // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in + // many cases (to be able to honor this we might be able to store a log of cells width, per row, for + // visible rows, but nav/programmatic scroll would have visible artifacts.) + //if (column->Flags & ImGuiTableColumnFlags_AlignRight) + // column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen); + //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter) + // column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f); + + // Reset content width variables + if (table->InstanceCurrent == 0) + { + column->ContentMaxXFrozen = column->WorkMinX; + column->ContentMaxXUnfrozen = column->WorkMinX; + column->ContentMaxXHeadersUsed = column->WorkMinX; + column->ContentMaxXHeadersIdeal = column->WorkMinX; + } + else + { + // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation. + const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x; + column->ContentMaxXFrozen += offset_from_previous_instance; + column->ContentMaxXUnfrozen += offset_from_previous_instance; + column->ContentMaxXHeadersUsed += offset_from_previous_instance; + column->ContentMaxXHeadersIdeal += offset_from_previous_instance; + } + + // Don't decrement auto-fit counters until container window got a chance to submit its items + if (table->HostSkipItems == false && table->InstanceCurrent == 0) + { + column->AutoFitQueue >>= 1; + column->CannotSkipItemsQueue >>= 1; + } + + if (visible_n < table->FreezeColumnsCount) + host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x); + + offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; + visible_n++; + } + + // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible. + // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar. + if (has_at_least_one_column_requesting_output == false) + { + table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true; + table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false; + } + + // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) + // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either + // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. + const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); + if (is_hovering_table && table->HoveredColumnBody == -1) + if (mouse_skewed_x >= unused_x1) + table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; + if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) + table->Flags &= ~ImGuiTableFlags_Resizable; + + table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0); + + // [Part 8] Lock actual OuterRect/WorkRect right-most position. + // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. + // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. + if (table->RightMostStretchedColumn != -1) + table->Flags &= ~ImGuiTableFlags_NoHostExtendX; + if (table->Flags & ImGuiTableFlags_NoHostExtendX) + { + table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; + table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); + } + table->InnerWindow->ParentWorkRect = table->WorkRect; + table->BorderX1 = table->InnerClipRect.Min.x; + table->BorderX2 = table->InnerClipRect.Max.x; + + // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call. + float window_content_max_y; + if (table->Flags & ImGuiTableFlags_NoHostExtendY) + window_content_max_y = table->OuterRect.Max.y; + else + window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y); + table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y); + + // [Part 9] Allocate draw channels and setup background cliprect + TableSetupDrawChannels(table); + + // [Part 10] Hit testing on borders + if (table->Flags & ImGuiTableFlags_Resizable) + TableUpdateBorders(table); + table_instance->LastTopHeadersRowHeight = 0.0f; + table->IsLayoutLocked = true; + table->IsUsingHeaders = false; + + // Highlight header + table->HighlightColumnHeader = -1; + if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent) + table->HighlightColumnHeader = table->ContextPopupColumn; + else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1) + if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive)) + table->HighlightColumnHeader = table->HoveredColumnBody; + + // [Part 11] Default context menu + // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). + // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup(). + // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, + // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. + if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) + { + TableDrawDefaultContextMenu(table, table->Flags); + EndPopup(); + } + + // [Part 12] Sanitize and build sort specs before we have a chance to use them for display. + // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change) + if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) + TableSortSpecsBuild(table); + + // [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns) + if (table->FreezeColumnsRequest > 0) + table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x; + if (table->FreezeRowsRequest > 0) + table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight; + table_instance->LastFrozenHeight = 0.0f; + + // Initial state + ImGuiWindow* inner_window = table->InnerWindow; + if (table->Flags & ImGuiTableFlags_NoClip) + table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + else + inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect? +} + +// Process hit-testing on resizing borders. Actual size change will be applied in EndTable() +// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise. +void ImGui::TableUpdateBorders(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable); + + // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and + // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not + // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height). + // Actual columns highlight/render will be performed in EndTable() and not be affected. + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale); + const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight; + const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight); + const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; + + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + continue; + + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) + continue; + + // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders() + const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body; + if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) + continue; + + if (!column->IsVisibleX && table->LastResizedColumn != column_n) + continue; + + ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); + ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); + ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav); + //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); + + bool hovered = false, held = false; + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); + if (pressed && IsMouseDoubleClicked(0)) + { + TableSetColumnWidthAutoSingle(table, column_n); + ClearActiveID(); + held = false; + } + if (held) + { + if (table->LastResizedColumn == -1) + table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX; + table->ResizedColumn = (ImGuiTableColumnIdx)column_n; + table->InstanceInteracted = table->InstanceCurrent; + } + if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held) + { + table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n; + SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + } +} + +void ImGui::EndTable() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!"); + + // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some + // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border) + //IM_ASSERT(table->IsLayoutLocked && "Table unused: never called TableNextRow(), is that the intent?"); + + // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our + // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc. + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + + const ImGuiTableFlags flags = table->Flags; + ImGuiWindow* inner_window = table->InnerWindow; + ImGuiWindow* outer_window = table->OuterWindow; + ImGuiTableTempData* temp_data = table->TempData; + IM_ASSERT(inner_window == g.CurrentWindow); + IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); + + if (table->IsInsideRow) + TableEndRow(table); + + // Context menu in columns body + if (flags & ImGuiTableFlags_ContextMenuInBody) + if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) + TableOpenContextMenu((int)table->HoveredColumnBody); + + // Finalize table height + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; + inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; + inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; + const float inner_content_max_y = table->RowPosY2; + IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); + if (inner_window != outer_window) + inner_window->DC.CursorMaxPos.y = inner_content_max_y; + else if (!(flags & ImGuiTableFlags_NoHostExtendY)) + table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height + table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y); + table_instance->LastOuterHeight = table->OuterRect.GetHeight(); + + // Setup inner scrolling range + // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y, + // but since the later is likely to be impossible to do we'd rather update both axises together. + if (table->Flags & ImGuiTableFlags_ScrollX) + { + const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; + float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x; + if (table->RightMostEnabledColumn != -1) + max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); + if (table->ResizedColumn != -1) + max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); + table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; + } + + // Pop clipping rect + if (!(flags & ImGuiTableFlags_NoClip)) + inner_window->DrawList->PopClipRect(); + inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back(); + + // Draw borders + if ((flags & ImGuiTableFlags_Borders) != 0) + TableDrawBorders(table); + +#if 0 + // Strip out dummy channel draw calls + // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out) + // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway. + // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices. + if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1) + { + ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel]; + dummy_channel->_CmdBuffer.resize(0); + dummy_channel->_IdxBuffer.resize(0); + } +#endif + + // Flatten channels and merge draw calls + ImDrawListSplitter* splitter = table->DrawSplitter; + splitter->SetCurrentChannel(inner_window->DrawList, 0); + if ((table->Flags & ImGuiTableFlags_NoClip) == 0) + TableMergeDrawChannels(table); + splitter->Merge(inner_window->DrawList); + + // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() + float auto_fit_width_for_fixed = 0.0f; + float auto_fit_width_for_stretched = 0.0f; + float auto_fit_width_for_stretched_min = 0.0f; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column); + if (column->Flags & ImGuiTableColumnFlags_WidthFixed) + auto_fit_width_for_fixed += column_width_request; + else + auto_fit_width_for_stretched += column_width_request; + if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0) + auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights)); + } + const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); + table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min); + + // Update scroll + if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window) + { + inner_window->Scroll.x = 0.0f; + } + else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent) + { + // When releasing a column being resized, scroll to keep the resulting column in sight + const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f; + ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn]; + if (column->MaxX < table->InnerClipRect.Min.x) + SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f); + else if (column->MaxX > table->InnerClipRect.Max.x) + SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f); + } + + // Apply resizing/dragging at the end of the frame + if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted) + { + ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; + const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)); + const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); + table->ResizedColumnNextWidth = new_width; + } + + table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false); + + // Pop from id stack + IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); + if (table->InstanceCurrent > 0) + PopID(); + PopID(); + + // Restore window data that we modified + const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; + inner_window->WorkRect = temp_data->HostBackupWorkRect; + inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; + inner_window->SkipItems = table->HostSkipItems; + outer_window->DC.CursorPos = table->OuterRect.Min; + outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; + outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; + + // Layout in outer window + // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding + // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414) + if (inner_window != outer_window) + { + short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; + inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. + g.CurrentTable = NULL; // To avoid error recovery recursing + EndChild(); + g.CurrentTable = table; + inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; + } + else + { + ItemSize(table->OuterRect.GetSize()); + ItemAdd(table->OuterRect, 0); + } + + // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar + if (table->Flags & ImGuiTableFlags_NoHostExtendX) + { + // FIXME-TABLE: Could we remove this section? + // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents + IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); + } + else if (temp_data->UserOuterSize.x <= 0.0f) + { + // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block + // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags. + // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback. + const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y + const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x); + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size)); + } + else + { + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); + } + if (temp_data->UserOuterSize.y <= 0.0f) + { + const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f; + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); + outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size)); + } + else + { + // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set) + outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y); + } + + // Save settings + if (table->IsSettingsDirty) + TableSaveSettings(table); + table->IsInitializing = false; + + // Clear or restore current table, if any + IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; + g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + if (g.CurrentTable) + { + g.CurrentTable->TempData = temp_data; + g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; + } + outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; + NavUpdateCurrentWindowIsScrollPushableX(); +} + +// See "COLUMNS SIZING POLICIES" comments at the top of this file +// If (init_width_or_weight <= 0.0f) it is ignored +void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); + IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!"); + IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); + if (table->DeclColumnsCount >= table->ColumnsCount) + { + IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, "Called TableSetupColumn() too many times!"); + return; + } + + ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; + table->DeclColumnsCount++; + + // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. + // Give a grace to users of ImGuiTableFlags_ScrollX. + if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0) + IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column."); + + // When passing a width automatically enforce WidthFixed policy + // (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable) + if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) + if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) + flags |= ImGuiTableColumnFlags_WidthFixed; + if (flags & ImGuiTableColumnFlags_AngledHeader) + { + flags |= ImGuiTableColumnFlags_NoHeaderLabel; + table->AngledHeadersCount++; + } + + TableSetupColumnFlags(table, column, flags); + column->UserID = user_id; + flags = column->Flags; + + // Initialize defaults + column->InitStretchWeightOrWidth = init_width_or_weight; + if (table->IsInitializing) + { + // Init width or weight + if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) + { + if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) + column->WidthRequest = init_width_or_weight; + if (flags & ImGuiTableColumnFlags_WidthStretch) + column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f; + + // Disable auto-fit if an explicit width/weight has been specified + if (init_width_or_weight > 0.0f) + column->AutoFitQueue = 0x00; + } + + // Init default visibility/sort state + if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0) + column->IsUserEnabled = column->IsUserEnabledNextFrame = false; + if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0) + { + column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. + column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending); + } + } + + // Store name (append with zero-terminator in contiguous buffer) + // FIXME: If we recorded the number of \n in names we could compute header row height + column->NameOffset = -1; + if (label != NULL && label[0] != 0) + { + column->NameOffset = (ImS16)table->ColumnsNames.size(); + table->ColumnsNames.append(label, label + strlen(label) + 1); + } +} + +// [Public] +void ImGui::TableSetupScrollFreeze(int columns, int rows) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); + IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); + IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); + IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit + + table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; + table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; + table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; + table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; + table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b + + // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) + for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) + { + int order_n = table->DisplayOrderToIndex[column_n]; + if (order_n != column_n && order_n >= table->FreezeColumnsRequest) + { + ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); + ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); + } + } +} + +//----------------------------------------------------------------------------- +// [SECTION] Tables: Simple accessors +//----------------------------------------------------------------------------- +// - TableGetColumnCount() +// - TableGetColumnName() +// - TableGetColumnName() [Internal] +// - TableSetColumnEnabled() +// - TableGetColumnFlags() +// - TableGetCellBgRect() [Internal] +// - TableGetColumnResizeID() [Internal] +// - TableGetHoveredColumn() [Internal] +// - TableGetHoveredRow() [Internal] +// - TableSetBgColor() +//----------------------------------------------------------------------------- + +int ImGui::TableGetColumnCount() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + return table ? table->ColumnsCount : 0; +} + +const char* ImGui::TableGetColumnName(int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return NULL; + if (column_n < 0) + column_n = table->CurrentColumn; + return TableGetColumnName(table, column_n); +} + +const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) +{ + if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount) + return ""; // NameOffset is invalid at this point + const ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->NameOffset == -1) + return ""; + return &table->ColumnsNames.Buf[column->NameOffset]; +} + +// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) +// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) +// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. +// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). +// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. +// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. +void ImGui::TableSetColumnEnabled(int column_n, bool enabled) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL); + if (!table) + return; + IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above + if (column_n < 0) + column_n = table->CurrentColumn; + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + ImGuiTableColumn* column = &table->Columns[column_n]; + column->IsUserEnabledNextFrame = enabled; +} + +// We allow querying for an extra column in order to poll the IsHovered state of the right-most section +ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return ImGuiTableColumnFlags_None; + if (column_n < 0) + column_n = table->CurrentColumn; + if (column_n == table->ColumnsCount) + return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None; + return table->Columns[column_n].Flags; +} + +// Return the cell rectangle based on currently known height. +// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations. +// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height. +// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right +// columns report a small offset so their CellBgRect can extend up to the outer border. +// FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling. +ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n) +{ + const ImGuiTableColumn* column = &table->Columns[column_n]; + float x1 = column->MinX; + float x2 = column->MaxX; + //if (column->PrevEnabledColumn == -1) + // x1 -= table->OuterPaddingX; + //if (column->NextEnabledColumn == -1) + // x2 += table->OuterPaddingX; + x1 = ImMax(x1, table->WorkRect.Min.x); + x2 = ImMin(x2, table->WorkRect.Max.x); + return ImRect(x1, table->RowPosY1, x2, table->RowPosY2); +} + +// Return the resizing ID for the right-side of the given column. +ImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no) +{ + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + ImGuiID instance_id = TableGetInstanceID(table, instance_no); + return instance_id + 1 + column_n; // FIXME: #6140: still not ideal +} + +// Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column. +int ImGui::TableGetHoveredColumn() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return -1; + return (int)table->HoveredColumnBody; +} + +// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row. +// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value. +// This difference with is the reason why this is not public yet. +int ImGui::TableGetHoveredRow() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return -1; + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + return (int)table_instance->HoveredRowLast; +} + +void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(target != ImGuiTableBgTarget_None); + + if (color == IM_COL32_DISABLE) + color = 0; + + // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time. + switch (target) + { + case ImGuiTableBgTarget_CellBg: + { + if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard + return; + if (column_n == -1) + column_n = table->CurrentColumn; + if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n)) + return; + if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) + table->RowCellDataCurrent++; + ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent]; + cell_data->BgColor = color; + cell_data->Column = (ImGuiTableColumnIdx)column_n; + break; + } + case ImGuiTableBgTarget_RowBg0: + case ImGuiTableBgTarget_RowBg1: + { + if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard + return; + IM_ASSERT(column_n == -1); + int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0; + table->RowBgColor[bg_idx] = color; + break; + } + default: + IM_ASSERT(0); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Row changes +//------------------------------------------------------------------------- +// - TableGetRowIndex() +// - TableNextRow() +// - TableBeginRow() [Internal] +// - TableEndRow() [Internal] +//------------------------------------------------------------------------- + +// [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows +int ImGui::TableGetRowIndex() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return 0; + return table->CurrentRow; +} + +// [Public] Starts into the first cell of a new row +void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + if (table->IsInsideRow) + TableEndRow(table); + + table->LastRowFlags = table->RowFlags; + table->RowFlags = row_flags; + table->RowCellPaddingY = g.Style.CellPadding.y; + table->RowMinHeight = row_min_height; + TableBeginRow(table); + + // We honor min_row_height requested by user, but cannot guarantee per-row maximum height, + // because that would essentially require a unique clipping rectangle per-cell. + table->RowPosY2 += table->RowCellPaddingY * 2.0f; + table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height); + + // Disable output until user calls TableNextColumn() + table->InnerWindow->SkipItems = true; +} + +// [Internal] Only called by TableNextRow() +void ImGui::TableBeginRow(ImGuiTable* table) +{ + ImGuiWindow* window = table->InnerWindow; + IM_ASSERT(!table->IsInsideRow); + + // New row + table->CurrentRow++; + table->CurrentColumn = -1; + table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE; + table->RowCellDataCurrent = -1; + table->IsInsideRow = true; + + // Begin frozen rows + float next_y1 = table->RowPosY2; + if (table->CurrentRow == 0 && table->FreezeRowsCount > 0) + next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y; + + table->RowPosY1 = table->RowPosY2 = next_y1; + table->RowTextBaseline = 0.0f; + table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent + + window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns. + window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too. + window->DC.IsSameLine = window->DC.IsSetPos = false; + window->DC.CursorMaxPos.y = next_y1; + + // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging. + if (table->RowFlags & ImGuiTableRowFlags_Headers) + { + TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg)); + if (table->CurrentRow == 0) + table->IsUsingHeaders = true; + } +} + +// [Internal] Called by TableNextRow() +void ImGui::TableEndRow(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window == table->InnerWindow); + IM_ASSERT(table->IsInsideRow); + + if (table->CurrentColumn != -1) + TableEndCell(table); + + // Logging + if (g.LogEnabled) + LogRenderedText(NULL, "|"); + + // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is + // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding. + window->DC.CursorPos.y = table->RowPosY2; + + // Row background fill + const float bg_y1 = table->RowPosY1; + const float bg_y2 = table->RowPosY2; + const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); + const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers))) + table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1; + + const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); + if (is_visible) + { + // Update data for TableGetHoveredRow() + if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0) + table_instance->HoveredRowNext = table->CurrentRow; + + // Decide of background color for the row + ImU32 bg_col0 = 0; + ImU32 bg_col1 = 0; + if (table->RowBgColor[0] != IM_COL32_DISABLE) + bg_col0 = table->RowBgColor[0]; + else if (table->Flags & ImGuiTableFlags_RowBg) + bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg); + if (table->RowBgColor[1] != IM_COL32_DISABLE) + bg_col1 = table->RowBgColor[1]; + + // Decide of top border color + ImU32 top_border_col = 0; + const float border_size = TABLE_BORDER_SIZE; + if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH)) + top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; + + const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0; + const bool draw_strong_bottom_border = unfreeze_rows_actual; + if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color) + { + // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is + // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. + if ((table->Flags & ImGuiTableFlags_NoClip) == 0) + window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); + } + + // Draw row background + // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle + if (bg_col0 || bg_col1) + { + ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2); + row_rect.ClipWith(table->BgClipRect); + if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y) + window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0); + if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y) + window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1); + } + + // Draw cell background color + if (draw_cell_bg_color) + { + ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent]; + for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) + { + // As we render the BG here we need to clip things (for layout we would not) + // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling. + const ImGuiTableColumn* column = &table->Columns[cell_data->Column]; + ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); + cell_bg_rect.ClipWith(table->BgClipRect); + cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling + cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); + if (cell_bg_rect.Min.y < cell_bg_rect.Max.y) + window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); + } + } + + // Draw top border + if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) + window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size); + + // Draw bottom border at the row unfreezing mark (always strong) + if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) + window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); + } + + // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) + // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and + // get the new cursor position. + if (unfreeze_rows_request) + { + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; + const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); + table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; + + if (unfreeze_rows_actual) + { + IM_ASSERT(table->IsUnfrozenRows == false); + table->IsUnfrozenRows = true; + + // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect + table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y); + table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y; + table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; + IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); + + float row_height = table->RowPosY2 - table->RowPosY1; + table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; + table->RowPosY1 = table->RowPosY2 - row_height; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + column->DrawChannelCurrent = column->DrawChannelUnfrozen; + column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; + } + + // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y + SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + } + } + + if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) + table->RowBgColorCounter++; + table->IsInsideRow = false; +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Columns changes +//------------------------------------------------------------------------- +// - TableGetColumnIndex() +// - TableSetColumnIndex() +// - TableNextColumn() +// - TableBeginCell() [Internal] +// - TableEndCell() [Internal] +//------------------------------------------------------------------------- + +int ImGui::TableGetColumnIndex() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return 0; + return table->CurrentColumn; +} + +// [Public] Append into a specific column +bool ImGui::TableSetColumnIndex(int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return false; + + if (table->CurrentColumn != column_n) + { + if (table->CurrentColumn != -1) + TableEndCell(table); + IM_ASSERT(column_n >= 0 && table->ColumnsCount); + TableBeginCell(table, column_n); + } + + // Return whether the column is visible. User may choose to skip submitting items based on this return value, + // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. + return table->Columns[column_n].IsRequestOutput; +} + +// [Public] Append into the next column, wrap and create a new row when already on last column +bool ImGui::TableNextColumn() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return false; + + if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount) + { + if (table->CurrentColumn != -1) + TableEndCell(table); + TableBeginCell(table, table->CurrentColumn + 1); + } + else + { + TableNextRow(); + TableBeginCell(table, 0); + } + + // Return whether the column is visible. User may choose to skip submitting items based on this return value, + // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. + return table->Columns[table->CurrentColumn].IsRequestOutput; +} + + +// [Internal] Called by TableSetColumnIndex()/TableNextColumn() +// This is called very frequently, so we need to be mindful of unnecessary overhead. +// FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns. +void ImGui::TableBeginCell(ImGuiTable* table, int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTableColumn* column = &table->Columns[column_n]; + ImGuiWindow* window = table->InnerWindow; + table->CurrentColumn = column_n; + + // Start position is roughly ~~ CellRect.Min + CellPadding + Indent + float start_x = column->WorkMinX; + if (column->Flags & ImGuiTableColumnFlags_IndentEnable) + start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row. + + window->DC.CursorPos.x = start_x; + window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY; + window->DC.CursorMaxPos.x = window->DC.CursorPos.x; + window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT + window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns. + window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; + window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; + + // Note how WorkRect.Max.y is only set once during layout + window->WorkRect.Min.y = window->DC.CursorPos.y; + window->WorkRect.Min.x = column->WorkMinX; + window->WorkRect.Max.x = column->WorkMaxX; + window->DC.ItemWidth = column->ItemWidth; + + window->SkipItems = column->IsSkipItems; + if (column->IsSkipItems) + { + g.LastItemData.ID = 0; + g.LastItemData.StatusFlags = 0; + } + + if (table->Flags & ImGuiTableFlags_NoClip) + { + // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); + } + else + { + // FIXME-TABLE: Could avoid this if draw channel is dummy channel? + SetWindowClipRectBeforeSetChannel(window, column->ClipRect); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + } + + // Logging + if (g.LogEnabled && !column->IsSkipItems) + { + LogRenderedText(&window->DC.CursorPos, "|"); + g.LogLinePosY = FLT_MAX; + } +} + +// [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn() +void ImGui::TableEndCell(ImGuiTable* table) +{ + ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; + ImGuiWindow* window = table->InnerWindow; + + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + + // Report maximum position so we can infer content size per column. + float* p_max_pos_x; + if (table->RowFlags & ImGuiTableRowFlags_Headers) + p_max_pos_x = &column->ContentMaxXHeadersUsed; // Useful in case user submit contents in header row that is not a TableHeader() call + else + p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen; + *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x); + if (column->IsEnabled) + table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY); + column->ItemWidth = window->DC.ItemWidth; + + // Propagate text baseline for the entire row + // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one. + table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset); +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Columns width management +//------------------------------------------------------------------------- +// - TableGetMaxColumnWidth() [Internal] +// - TableGetColumnWidthAuto() [Internal] +// - TableSetColumnWidth() +// - TableSetColumnWidthAutoSingle() [Internal] +// - TableSetColumnWidthAutoAll() [Internal] +// - TableUpdateColumnsWeightFromWidth() [Internal] +//------------------------------------------------------------------------- +// Note that actual columns widths are computed in TableUpdateLayout(). +//------------------------------------------------------------------------- + +// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis. +float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n) +{ + const ImGuiTableColumn* column = &table->Columns[column_n]; + float max_width = FLT_MAX; + const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; + if (table->Flags & ImGuiTableFlags_ScrollX) + { + // Frozen columns can't reach beyond visible width else scrolling will naturally break. + // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) + if (column->DisplayOrder < table->FreezeColumnsRequest) + { + max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; + max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; + } + } + else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) + { + // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make + // sure they are all visible. Because of this we also know that all of the columns will always fit in + // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) + // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. + // See "table_width_distrib" and "table_width_keep_visible" tests + max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX; + //max_width -= table->CellSpacingX1; + max_width -= table->CellSpacingX2; + max_width -= table->CellPaddingX * 2.0f; + max_width -= table->OuterPaddingX; + } + return max_width; +} + +// Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field +float ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column) +{ + const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX; + const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX; + float width_auto = content_width_body; + if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth)) + width_auto = ImMax(width_auto, content_width_headers); + + // Non-resizable fixed columns preserve their requested width + if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f) + if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize)) + width_auto = column->InitStretchWeightOrWidth; + + return ImMax(width_auto, table->MinColumnWidth); +} + +// 'width' = inner column width, without padding +void ImGui::TableSetColumnWidth(int column_n, float width) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && table->IsLayoutLocked == false); + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + ImGuiTableColumn* column_0 = &table->Columns[column_n]; + float column_0_width = width; + + // Apply constraints early + // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) + IM_ASSERT(table->MinColumnWidth > 0.0f); + const float min_width = table->MinColumnWidth; + const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933) + column_0_width = ImClamp(column_0_width, min_width, max_width); + if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) + return; + + //IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); + ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; + + // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. + // - All fixed: easy. + // - All stretch: easy. + // - One or more fixed + one stretch: easy. + // - One or more fixed + more than one stretch: tricky. + // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here. + + // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. + // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. + // Scenarios: + // - F1 F2 F3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset. + // - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered. + // - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size. + // - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) + // - W1 W2 W3 resize from W1| or W2| --> ok + // - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) + // - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1) + // - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1) + // - W1 W2 F3 resize from W1| or W2| --> ok + // - W1 F2 W3 resize from W1| or F2| --> ok + // - F1 W2 F3 resize from W2| --> ok + // - F1 W3 F2 resize from W3| --> ok + // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. + // - W1 F2 F3 resize from F2| --> ok + // All resizes from a Wx columns are locking other columns. + + // Possible improvements: + // - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns. + // - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix. + + // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). + + // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize. + // This is the preferred resize path + if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed) + if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder) + { + column_0->WidthRequest = column_0_width; + table->IsSettingsDirty = true; + return; + } + + // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) + if (column_1 == NULL) + column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; + if (column_1 == NULL) + return; + + // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column. + // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) + float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); + column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; + IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f); + column_0->WidthRequest = column_0_width; + column_1->WidthRequest = column_1_width; + if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch) + TableUpdateColumnsWeightFromWidth(table); + table->IsSettingsDirty = true; +} + +// Disable clipping then auto-fit, will take 2 frames +// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns) +void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n) +{ + // Single auto width uses auto-fit + ImGuiTableColumn* column = &table->Columns[column_n]; + if (!column->IsEnabled) + return; + column->CannotSkipItemsQueue = (1 << 0); + table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n; +} + +void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) +{ + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column + continue; + column->CannotSkipItemsQueue = (1 << 0); + column->AutoFitQueue = (1 << 1); + } +} + +void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) +{ + IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1); + + // Measure existing quantities + float visible_weight = 0.0f; + float visible_width = 0.0f; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) + continue; + IM_ASSERT(column->StretchWeight > 0.0f); + visible_weight += column->StretchWeight; + visible_width += column->WidthRequest; + } + IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f); + + // Apply new weights + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) + continue; + column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight; + IM_ASSERT(column->StretchWeight > 0.0f); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Drawing +//------------------------------------------------------------------------- +// - TablePushBackgroundChannel() [Internal] +// - TablePopBackgroundChannel() [Internal] +// - TableSetupDrawChannels() [Internal] +// - TableMergeDrawChannels() [Internal] +// - TableGetColumnBorderCol() [Internal] +// - TableDrawBorders() [Internal] +//------------------------------------------------------------------------- + +// Bg2 is used by Selectable (and possibly other widgets) to render to the background. +// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. +void ImGui::TablePushBackgroundChannel() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiTable* table = g.CurrentTable; + + // Optimization: avoid SetCurrentChannel() + PushClipRect() + table->HostBackupInnerClipRect = window->ClipRect; + SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); +} + +void ImGui::TablePopBackgroundChannel() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiTable* table = g.CurrentTable; + ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; + + // Optimization: avoid PopClipRect() + SetCurrentChannel() + SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); +} + +// Allocate draw channels. Called by TableUpdateLayout() +// - We allocate them following storage order instead of display order so reordering columns won't needlessly +// increase overall dormant memory cost. +// - We isolate headers draw commands in their own channels instead of just altering clip rects. +// This is in order to facilitate merging of draw commands. +// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels. +// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other +// channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged. +// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for +// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). +// Draw channel allocation (before merging): +// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call) +// - Clip --> 2+D+N channels +// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) +// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) +// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0. +void ImGui::TableSetupDrawChannels(ImGuiTable* table) +{ + const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1; + const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount; + const int channels_for_bg = 1 + 1 * freeze_row_multiplier; + const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0; + const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; + table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); + table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); + table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; + table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); + + int draw_channel_current = 2; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->IsVisibleX && column->IsVisibleY) + { + column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current); + column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0)); + if (!(table->Flags & ImGuiTableFlags_NoClip)) + draw_channel_current++; + } + else + { + column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel; + } + column->DrawChannelCurrent = column->DrawChannelFrozen; + } + + // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. + // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. + // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) + table->BgClipRect = table->InnerClipRect; + table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect; + table->Bg2ClipRectForDrawCmd = table->HostClipRect; + IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); +} + +// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable(). +// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect, +// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels(). +// +// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve +// this we merge their clip rect and make them contiguous in the channel list, so they can be merged +// by the call to DrawSplitter.Merge() following to the call to this function. +// We reorder draw commands by arranging them into a maximum of 4 distinct groups: +// +// 1 group: 2 groups: 2 groups: 4 groups: +// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze +// [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll +// +// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled). +// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group +// based on its position (within frozen rows/columns groups or not). +// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect. +// This function assume that each column are pointing to a distinct draw channel, +// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask. +// +// Column channels will not be merged into one of the 1-4 groups in the following cases: +// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value). +// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds +// matches, by e.g. calling SetCursorScreenPos(). +// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here.. +// we could do better but it's going to be rare and probably not worth the hassle. +// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd. +// +// This function is particularly tricky to understand.. take a breath. +void ImGui::TableMergeDrawChannels(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + ImDrawListSplitter* splitter = table->DrawSplitter; + const bool has_freeze_v = (table->FreezeRowsCount > 0); + const bool has_freeze_h = (table->FreezeColumnsCount > 0); + IM_ASSERT(splitter->_Current == 0); + + // Track which groups we are going to attempt to merge, and which channels goes into each group. + struct MergeGroup + { + ImRect ClipRect; + int ChannelsCount = 0; + ImBitArrayPtr ChannelsMask = NULL; + }; + int merge_group_mask = 0x00; + MergeGroup merge_groups[4]; + + // Use a reusable temp buffer for the merge masks as they are dynamically sized. + const int max_draw_channels = (4 + table->ColumnsCount * 2); + const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels); + g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5); + memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5); + for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++) + merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n)); + ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4)); + + // 1. Scan channels and take note of those which can be merged + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n)) + continue; + ImGuiTableColumn* column = &table->Columns[column_n]; + + const int merge_group_sub_count = has_freeze_v ? 2 : 1; + for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++) + { + const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen; + + // Don't attempt to merge if there are multiple draw calls within the column + ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; + if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() + src_channel->_CmdBuffer.pop_back(); + if (src_channel->_CmdBuffer.Size != 1) + continue; + + // Find out the width of this merge group and check if it will fit in our column + // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it) + if (!(column->Flags & ImGuiTableColumnFlags_NoClip)) + { + float content_max_x; + if (!has_freeze_v) + content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze + else if (merge_group_sub_n == 0) + content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed); // Row freeze: use width before freeze + else + content_max_x = column->ContentMaxXUnfrozen; // Row freeze: use width after freeze + if (content_max_x > column->ClipRect.Max.x) + continue; + } + + const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2); + IM_ASSERT(channel_no < max_draw_channels); + MergeGroup* merge_group = &merge_groups[merge_group_n]; + if (merge_group->ChannelsCount == 0) + merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + ImBitArraySetBit(merge_group->ChannelsMask, channel_no); + merge_group->ChannelsCount++; + merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect); + merge_group_mask |= (1 << merge_group_n); + } + + // Invalidate current draw channel + // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data) + column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1; + } + + // [DEBUG] Display merge groups +#if 0 + if (g.IO.KeyShift) + for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + { + MergeGroup* merge_group = &merge_groups[merge_group_n]; + if (merge_group->ChannelsCount == 0) + continue; + char buf[32]; + ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount); + ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4); + ImVec2 text_size = CalcTextSize(buf, NULL); + GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255)); + GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL); + GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255)); + } +#endif + + // 2. Rewrite channel list in our preferred order + if (merge_group_mask != 0) + { + // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels(). + const int LEADING_DRAW_CHANNELS = 2; + g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized + ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; + ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count); + ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen); + IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); + int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); + //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; + ImRect host_rect = table->HostClipRect; + for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) + { + if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) + { + MergeGroup* merge_group = &merge_groups[merge_group_n]; + ImRect merge_clip_rect = merge_group->ClipRect; + + // Extend outer-most clip limits to match those of host, so draw calls can be merged even if + // outer-most columns have some outer padding offsetting them from their parent ClipRect. + // The principal cases this is dealing with are: + // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge + // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge + // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit + // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect. + if ((merge_group_n & 1) == 0 || !has_freeze_h) + merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x); + if ((merge_group_n & 2) == 0 || !has_freeze_v) + merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y); + if ((merge_group_n & 1) != 0) + merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); + if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) + merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); + //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG] + //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); + //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); + remaining_count -= merge_group->ChannelsCount; + for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++) + remaining_mask[n] &= ~merge_group->ChannelsMask[n]; + for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++) + { + // Copy + overwrite new clip rect + if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n)) + continue; + IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n); + merge_channels_count--; + + ImDrawChannel* channel = &splitter->_Channels[n]; + IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect))); + channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4(); + memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); + } + } + + // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1) + if (merge_group_n == 1 && has_freeze_v) + memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel)); + } + + // Append unmergeable channels that we didn't reorder at the end of the list + for (int n = 0; n < splitter->_Count && remaining_count != 0; n++) + { + if (!IM_BITARRAY_TESTBIT(remaining_mask, n)) + continue; + ImDrawChannel* channel = &splitter->_Channels[n]; + memcpy(dst_tmp++, channel, sizeof(ImDrawChannel)); + remaining_count--; + } + IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size); + memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel)); + } +} + +static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n) +{ + const bool is_hovered = (table->HoveredColumnBorder == column_n); + const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); + if (is_resized || is_hovered) + return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); + if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize))) + return table->BorderColorStrong; + return table->BorderColorLight; +} + +// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) +void ImGui::TableDrawBorders(ImGuiTable* table) +{ + ImGuiWindow* inner_window = table->InnerWindow; + if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect)) + return; + + ImDrawList* inner_drawlist = inner_window->DrawList; + table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); + inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); + + // Draw inner border and resizing feedback + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + const float border_size = TABLE_BORDER_SIZE; + const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); + const float draw_y2_body = table->InnerRect.Max.y; + const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; + if (table->Flags & ImGuiTableFlags_BordersInnerV) + { + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + continue; + + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + const bool is_hovered = (table->HoveredColumnBorder == column_n); + const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); + const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); + if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) + continue; + + // Decide whether right-most column is visible + if (column->NextEnabledColumn == -1 && !is_resizable) + if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX)) + continue; + if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. + continue; + + // Draw in outer window so right-most column won't be clipped + // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. + float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head; + if (draw_y2 > draw_y1) + inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); + } + } + + // Draw outer border + // FIXME: could use AddRect or explicit VLine/HLine helper? + if (table->Flags & ImGuiTableFlags_BordersOuter) + { + // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call + // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their + // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part + // of it in inner window, and the part that's over scrollbars in the outer window..) + // Either solution currently won't allow us to use a larger border size: the border would clipped. + const ImRect outer_border = table->OuterRect; + const ImU32 outer_col = table->BorderColorStrong; + if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) + { + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); + } + else if (table->Flags & ImGuiTableFlags_BordersOuterV) + { + inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); + inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); + } + else if (table->Flags & ImGuiTableFlags_BordersOuterH) + { + inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); + inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); + } + } + if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) + { + // Draw bottom-most row border between it is above outer border. + const float border_y = table->RowPosY2; + if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) + inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); + } + + inner_drawlist->PopClipRect(); +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Sorting +//------------------------------------------------------------------------- +// - TableGetSortSpecs() +// - TableFixColumnSortDirection() [Internal] +// - TableGetColumnNextSortDirection() [Internal] +// - TableSetColumnSortDirection() [Internal] +// - TableSortSpecsSanitize() [Internal] +// - TableSortSpecsBuild() [Internal] +//------------------------------------------------------------------------- + +// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set) +// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have +// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, +// else you may wastefully sort your data every frame! +// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()! +ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL); + + if (!(table->Flags & ImGuiTableFlags_Sortable)) + return NULL; + + // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths. + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + + TableSortSpecsBuild(table); + return &table->SortSpecs; +} + +static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n) +{ + IM_ASSERT(n < column->SortDirectionsAvailCount); + return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03); +} + +// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending) +void ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column) +{ + if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0) + return; + column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); + table->IsSortSpecsDirty = true; +} + +// Calculate next sort direction that would be set after clicking the column +// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click. +// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op. +IM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2); +ImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column) +{ + IM_ASSERT(column->SortDirectionsAvailCount > 0); + if (column->SortOrder == -1) + return TableGetColumnAvailSortDirection(column, 0); + for (int n = 0; n < 3; n++) + if (column->SortDirection == TableGetColumnAvailSortDirection(column, n)) + return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount); + IM_ASSERT(0); + return ImGuiSortDirection_None; +} + +// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert +// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code. +void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + + if (!(table->Flags & ImGuiTableFlags_SortMulti)) + append_to_sort_specs = false; + if (!(table->Flags & ImGuiTableFlags_SortTristate)) + IM_ASSERT(sort_direction != ImGuiSortDirection_None); + + ImGuiTableColumnIdx sort_order_max = 0; + if (append_to_sort_specs) + for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) + sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder); + + ImGuiTableColumn* column = &table->Columns[column_n]; + column->SortDirection = (ImU8)sort_direction; + if (column->SortDirection == ImGuiSortDirection_None) + column->SortOrder = -1; + else if (column->SortOrder == -1 || !append_to_sort_specs) + column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0; + + for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) + { + ImGuiTableColumn* other_column = &table->Columns[other_column_n]; + if (other_column != column && !append_to_sort_specs) + other_column->SortOrder = -1; + TableFixColumnSortDirection(table, other_column); + } + table->IsSettingsDirty = true; + table->IsSortSpecsDirty = true; +} + +void ImGui::TableSortSpecsSanitize(ImGuiTable* table) +{ + IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable); + + // Clear SortOrder from hidden column and verify that there's no gap or duplicate. + int sort_order_count = 0; + ImU64 sort_order_mask = 0x00; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->SortOrder != -1 && !column->IsEnabled) + column->SortOrder = -1; + if (column->SortOrder == -1) + continue; + sort_order_count++; + sort_order_mask |= ((ImU64)1 << column->SortOrder); + IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8); + } + + const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1); + const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti); + if (need_fix_linearize || need_fix_single_sort_order) + { + ImU64 fixed_mask = 0x00; + for (int sort_n = 0; sort_n < sort_order_count; sort_n++) + { + // Fix: Rewrite sort order fields if needed so they have no gap or duplicate. + // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1) + int column_with_smallest_sort_order = -1; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1) + if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder) + column_with_smallest_sort_order = column_n; + IM_ASSERT(column_with_smallest_sort_order != -1); + fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order); + table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n; + + // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set. + if (need_fix_single_sort_order) + { + sort_order_count = 1; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (column_n != column_with_smallest_sort_order) + table->Columns[column_n].SortOrder = -1; + break; + } + } + } + + // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag) + if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort)) + { + sort_order_count = 1; + column->SortOrder = 0; + column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); + break; + } + } + + table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count; +} + +void ImGui::TableSortSpecsBuild(ImGuiTable* table) +{ + bool dirty = table->IsSortSpecsDirty; + if (dirty) + { + TableSortSpecsSanitize(table); + table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + table->SortSpecs.SpecsDirty = true; // Mark as dirty for user + table->IsSortSpecsDirty = false; // Mark as not dirty for us + } + + // Write output + // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable() + ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; + if (dirty && sort_specs != NULL) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->SortOrder == -1) + continue; + IM_ASSERT(column->SortOrder < table->SortSpecsCount); + ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; + sort_spec->ColumnUserID = column->UserID; + sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; + sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; + sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection; + } + + table->SortSpecs.Specs = sort_specs; + table->SortSpecs.SpecsCount = table->SortSpecsCount; +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Headers +//------------------------------------------------------------------------- +// - TableGetHeaderRowHeight() [Internal] +// - TableGetHeaderAngledMaxLabelWidth() [Internal] +// - TableHeadersRow() +// - TableHeader() +// - TableAngledHeadersRow() +// - TableAngledHeadersRowEx() [Internal] +//------------------------------------------------------------------------- + +float ImGui::TableGetHeaderRowHeight() +{ + // Caring for a minor edge case: + // Calculate row height, for the unlikely case that some labels may be taller than others. + // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height. + // In your custom header row you may omit this all together and just call TableNextRow() without a height... + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float row_height = g.FontSize; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0) + row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y); + return row_height + g.Style.CellPadding.y * 2.0f; +} + +float ImGui::TableGetHeaderAngledMaxLabelWidth() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float width = 0.0f; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader) + width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x); + return width + g.Style.CellPadding.y * 2.0f; // Swap padding +} + +// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). +// The intent is that advanced users willing to create customized headers would not need to use this helper +// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. +// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. +// This code is intentionally written to not make much use of internal functions, to give you better direction +// if you need to write your own. +// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. +void ImGui::TableHeadersRow() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); + + // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make + // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this. + if (!table->IsLayoutLocked) + TableUpdateLayout(table); + + // Open row + const float row_height = TableGetHeaderRowHeight(); + TableNextRow(ImGuiTableRowFlags_Headers, row_height); + const float row_y1 = GetCursorScreenPos().y; + if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. + return; + + const int columns_count = TableGetColumnCount(); + for (int column_n = 0; column_n < columns_count; column_n++) + { + if (!TableSetColumnIndex(column_n)) + continue; + + // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation) + const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); + PushID(column_n); + TableHeader(name); + PopID(); + } + + // Allow opening popup from the right-most section after the last column. + ImVec2 mouse_pos = ImGui::GetMousePos(); + if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) + if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) + TableOpenContextMenu(columns_count); // Will open a non-column-specific popup. +} + +// Emit a column header (text + optional sort order) +// We cpu-clip text here so that all columns headers can be merged into a same draw call. +// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader() +void ImGui::TableHeader(const char* label) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!"); + IM_ASSERT(table->CurrentColumn != -1); + const int column_n = table->CurrentColumn; + ImGuiTableColumn* column = &table->Columns[column_n]; + + // Label + if (label == NULL) + label = ""; + const char* label_end = FindRenderedTextEnd(label); + ImVec2 label_size = CalcTextSize(label, label_end, true); + ImVec2 label_pos = window->DC.CursorPos; + + // If we already got a row height, there's use that. + // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect? + ImRect cell_r = TableGetCellBgRect(table, column_n); + float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f); + + // Calculate ideal size for sort order arrow + float w_arrow = 0.0f; + float w_sort_text = 0.0f; + bool sort_arrow = false; + char sort_order_suf[4] = ""; + const float ARROW_SCALE = 0.65f; + if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) + { + w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); + if (column->SortOrder != -1) + sort_arrow = true; + if (column->SortOrder > 0) + { + ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); + w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x; + } + } + + // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging. + float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow; + column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x)); + column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); + + // Keep header highlighted when context menu is open. + ImGuiID id = window->GetID(label); + ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); + ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal + if (!ItemAdd(bb, id)) + return; + + //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] + //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] + + // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. + const bool highlight = (table->HighlightColumnHeader == column_n); + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); + if (held || hovered || highlight) + { + const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); + } + else + { + // Submit single cell bg color in the case we didn't submit a full header row + if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); + } + RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding); + if (held) + table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; + window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; + + // Drag and drop to re-order columns. + // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone. + if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive) + { + // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x + table->ReorderColumn = (ImGuiTableColumnIdx)column_n; + table->InstanceInteracted = table->InstanceCurrent; + + // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder. + if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x) + if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL) + if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder)) + if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) + table->ReorderColumnDir = -1; + if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x) + if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL) + if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder)) + if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) + table->ReorderColumnDir = +1; + } + + // Sort order arrow + const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x); + if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) + { + if (column->SortOrder != -1) + { + float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text); + float y = label_pos.y; + if (column->SortOrder > 0) + { + PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f)); + RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf); + PopStyleColor(); + x += w_sort_text; + } + RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE); + } + + // Handle clicking on column header to adjust Sort Order + if (pressed && table->ReorderColumn != column_n) + { + ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column); + TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift); + } + } + + // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will + // be merged into a single draw call. + //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE); + RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); + + const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); + if (text_clipped && hovered && g.ActiveId == 0) + SetItemTooltip("%.*s", (int)(label_end - label), label); + + // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden + if (IsMouseReleased(1) && IsItemHovered()) + TableOpenContextMenu(column_n); +} + +// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. +// FIXME: No hit-testing/button on the angled header. +void ImGui::TableAngledHeadersRow() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + ImGuiTableTempData* temp_data = table->TempData; + temp_data->AngledHeadersRequests.resize(0); + temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount); + + // Which column needs highlight? + const ImGuiID row_id = GetID("##AngledHeaders"); + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + int highlight_column_n = table->HighlightColumnHeader; + if (highlight_column_n == -1 && table->HoveredColumnBody != -1) + if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) + highlight_column_n = table->HoveredColumnBody; + + // Build up request + ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg); + ImU32 col_text = GetColorU32(ImGuiCol_Text); + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + { + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here. + continue; + ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 }; + temp_data->AngledHeadersRequests.push_back(request); + } + + // Render row + TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size); +} + +// Important: data must be fed left to right +void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + ImGuiWindow* window = g.CurrentWindow; + ImDrawList* draw_list = window->DrawList; + IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); + IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); + + if (max_label_width == 0.0f) + max_label_width = TableGetHeaderAngledMaxLabelWidth(); + + // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user. + const bool flip_label = (angle < 0.0f); + angle -= IM_PI * 0.5f; + const float cos_a = ImCos(angle); + const float sin_a = ImSin(angle); + const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a; + const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a; + const ImVec2 unit_right = ImVec2(cos_a, sin_a); + + // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow() + // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other. + const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f; + const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y)); + table->AngledHeadersHeight = row_height; + table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; + const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right + + // Declare row, override and draw our own background + TableNextRow(ImGuiTableRowFlags_Headers, row_height); + TableNextColumn(); + const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2); + table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0); + float clip_rect_min_x = table->BgClipRect.Min.x; + if (table->FreezeColumnsCount > 0) + clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX); + TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel + PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns + draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. + PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns + + ButtonBehavior(row_r, row_id, NULL, NULL); + KeepAliveID(row_id); + + const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better + const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f); + const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component + const ImVec2 align = g.Style.TableAngledHeadersTextAlign; + + // Draw background and labels in first pass, then all borders. + float max_x = 0.0f; + for (int pass = 0; pass < 2; pass++) + for (int order_n = 0; order_n < data_count; order_n++) + { + const ImGuiTableHeaderData* request = &data[order_n]; + const int column_n = request->Index; + ImGuiTableColumn* column = &table->Columns[column_n]; + + ImVec2 bg_shape[4]; + bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); + bg_shape[1] = ImVec2(column->MinX, row_r.Max.y); + bg_shape[2] = bg_shape[1] + header_angled_vector; + bg_shape[3] = bg_shape[0] + header_angled_vector; + if (pass == 0) + { + // Draw shape + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0); + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight + max_x = ImMax(max_x, bg_shape[3].x); + + // Draw label + // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset. + // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated. + const char* label_name = TableGetColumnName(table, column_n); + const char* label_name_end = FindRenderedTextEnd(label_name); + const float line_off_step_x = (g.FontSize / -sin_a); + const int label_lines = ImTextCountLines(label_name, label_name_end); + + // Left<>Right alignment + float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f; + float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x; + line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x; + + // Register header width + column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x); + + while (label_name < label_name_end) + { + const char* label_name_eol = strchr(label_name, '\n'); + if (label_name_eol == NULL) + label_name_eol = label_name_end; + + // FIXME: Individual line clipping for right-most column is broken for negative angles. + ImVec2 label_size = CalcTextSize(label_name, label_name_eol); + float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text. + float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x); + ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); + int vtx_idx_begin = draw_list->_VtxCurrentIdx; + PushStyleColor(ImGuiCol_Text, request->TextColor); + RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); + PopStyleColor(); + int vtx_idx_end = draw_list->_VtxCurrentIdx; + + // Up<>Down alignment + const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f); + const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f); + + // Rotate and offset label + ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y); + ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y); + line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x; + pivot_out += unit_right * padding.y; + if (flip_label) + pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x)); + pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x; + ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset + //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } + + label_name = label_name_eol + 1; + } + } + if (pass == 1) + { + // Draw border + draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); + } + } + PopClipRect(); + PopClipRect(); + table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX); +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Context Menu +//------------------------------------------------------------------------- +// - TableOpenContextMenu() [Internal] +// - TableBeginContextMenuPopup() [Internal] +// - TableDrawDefaultContextMenu() [Internal] +//------------------------------------------------------------------------- + +// Use -1 to open menu not specific to a given column. +void ImGui::TableOpenContextMenu(int column_n) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (column_n == -1 && table->CurrentColumn != -1) // When called within a column automatically use this one (for consistency) + column_n = table->CurrentColumn; + if (column_n == table->ColumnsCount) // To facilitate using with TableGetHoveredColumn() + column_n = -1; + IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount); + if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) + { + table->IsContextPopupOpen = true; + table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n; + table->InstanceInteracted = table->InstanceCurrent; + const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); + OpenPopupEx(context_menu_id, ImGuiPopupFlags_None); + } +} + +bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table) +{ + if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted) + return false; + const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); + if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) + return true; + table->IsContextPopupOpen = false; + return false; +} + +// Output context menu into current window (generally a popup) +// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data? +// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags. +// - ImGuiTableFlags_Resizable -> display Sizing menu items +// - ImGuiTableFlags_Reorderable -> display "Reset Order" +////- ImGuiTableFlags_Sortable -> display sorting options (disabled) +// - ImGuiTableFlags_Hideable -> display columns visibility menu items +// It means if you have a custom context menus you can call this section and omit some sections, and add your own. +void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + bool want_separator = false; + const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1; + ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL; + + // Sizing + if (flags_for_section_to_display & ImGuiTableFlags_Resizable) + { + if (column != NULL) + { + const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled; + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // "###SizeOne" + TableSetColumnWidthAutoSingle(table, column_n); + } + + const char* size_all_desc; + if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame) + size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit); // "###SizeAll" All fixed + else + size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault); // "###SizeAll" All stretch or mixed + if (MenuItem(size_all_desc, NULL)) + TableSetColumnWidthAutoAll(table); + want_separator = true; + } + + // Ordering + if (flags_for_section_to_display & ImGuiTableFlags_Reorderable) + { + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) + table->IsResetDisplayOrderRequest = true; + want_separator = true; + } + + // Reset all (should work but seems unnecessary/noisy to expose?) + //if (MenuItem("Reset all")) + // table->IsResetAllRequest = true; + + // Sorting + // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) +#if 0 + if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) + { + if (want_separator) + Separator(); + want_separator = true; + + bool append_to_sort_specs = g.IO.KeyShift; + if (MenuItem("Sort in Ascending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0)) + TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs); + if (MenuItem("Sort in Descending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0)) + TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs); + } +#endif + + // Hiding / Visibility + if (flags_for_section_to_display & ImGuiTableFlags_Hideable) + { + if (want_separator) + Separator(); + want_separator = true; + + PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); + for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) + { + ImGuiTableColumn* other_column = &table->Columns[other_column_n]; + if (other_column->Flags & ImGuiTableColumnFlags_Disabled) + continue; + + const char* name = TableGetColumnName(table, other_column_n); + if (name == NULL || name[0] == 0) + name = ""; + + // Make sure we can't hide the last active column + bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; + if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) + menu_item_active = false; + if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) + other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; + } + PopItemFlag(); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Settings (.ini data) +//------------------------------------------------------------------------- +// FIXME: The binding/finding/creating flow are too confusing. +//------------------------------------------------------------------------- +// - TableSettingsInit() [Internal] +// - TableSettingsCalcChunkSize() [Internal] +// - TableSettingsCreate() [Internal] +// - TableSettingsFindByID() [Internal] +// - TableGetBoundSettings() [Internal] +// - TableResetSettings() +// - TableSaveSettings() [Internal] +// - TableLoadSettings() [Internal] +// - TableSettingsHandler_ClearAll() [Internal] +// - TableSettingsHandler_ApplyAll() [Internal] +// - TableSettingsHandler_ReadOpen() [Internal] +// - TableSettingsHandler_ReadLine() [Internal] +// - TableSettingsHandler_WriteAll() [Internal] +// - TableSettingsInstallHandler() [Internal] +//------------------------------------------------------------------------- +// [Init] 1: TableSettingsHandler_ReadXXXX() Load and parse .ini file into TableSettings. +// [Main] 2: TableLoadSettings() When table is created, bind Table to TableSettings, serialize TableSettings data into Table. +// [Main] 3: TableSaveSettings() When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty. +// [Main] 4: TableSettingsHandler_WriteAll() When .ini file is dirty (which can come from other source), save TableSettings into .ini file. +//------------------------------------------------------------------------- + +// Clear and initialize empty settings instance +static void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max) +{ + IM_PLACEMENT_NEW(settings) ImGuiTableSettings(); + ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings(); + for (int n = 0; n < columns_count_max; n++, settings_column++) + IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings(); + settings->ID = id; + settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count; + settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max; + settings->WantApply = true; +} + +static size_t TableSettingsCalcChunkSize(int columns_count) +{ + return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings); +} + +ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count) +{ + ImGuiContext& g = *GImGui; + ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count)); + TableSettingsInit(settings, id, columns_count, columns_count); + return settings; +} + +// Find existing settings +ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id) +{ + // FIXME-OPT: Might want to store a lookup map for this? + ImGuiContext& g = *GImGui; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + if (settings->ID == id) + return settings; + return NULL; +} + +// Get settings for a given table, NULL if none +ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table) +{ + if (table->SettingsOffset != -1) + { + ImGuiContext& g = *GImGui; + ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset); + IM_ASSERT(settings->ID == table->ID); + if (settings->ColumnsCountMax >= table->ColumnsCount) + return settings; // OK + settings->ID = 0; // Invalidate storage, we won't fit because of a count change + } + return NULL; +} + +// Restore initial state of table (with or without saved settings) +void ImGui::TableResetSettings(ImGuiTable* table) +{ + table->IsInitializing = table->IsSettingsDirty = true; + table->IsResetAllRequest = false; + table->IsSettingsRequestLoad = false; // Don't reload from ini + table->SettingsLoadedFlags = ImGuiTableFlags_None; // Mark as nothing loaded so our initialized data becomes authoritative +} + +void ImGui::TableSaveSettings(ImGuiTable* table) +{ + table->IsSettingsDirty = false; + if (table->Flags & ImGuiTableFlags_NoSavedSettings) + return; + + // Bind or create settings data + ImGuiContext& g = *GImGui; + ImGuiTableSettings* settings = TableGetBoundSettings(table); + if (settings == NULL) + { + settings = TableSettingsCreate(table->ID, table->ColumnsCount); + table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); + } + settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount; + + // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings + IM_ASSERT(settings->ID == table->ID); + IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount); + ImGuiTableColumn* column = table->Columns.Data; + ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); + + bool save_ref_scale = false; + settings->SaveFlags = ImGuiTableFlags_None; + for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++) + { + const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest; + column_settings->WidthOrWeight = width_or_weight; + column_settings->Index = (ImGuiTableColumnIdx)n; + column_settings->DisplayOrder = column->DisplayOrder; + column_settings->SortOrder = column->SortOrder; + column_settings->SortDirection = column->SortDirection; + column_settings->IsEnabled = column->IsUserEnabled; + column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; + if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) + save_ref_scale = true; + + // We skip saving some data in the .ini file when they are unnecessary to restore our state. + // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f. + // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present. + if (width_or_weight != column->InitStretchWeightOrWidth) + settings->SaveFlags |= ImGuiTableFlags_Resizable; + if (column->DisplayOrder != n) + settings->SaveFlags |= ImGuiTableFlags_Reorderable; + if (column->SortOrder != -1) + settings->SaveFlags |= ImGuiTableFlags_Sortable; + if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) + settings->SaveFlags |= ImGuiTableFlags_Hideable; + } + settings->SaveFlags &= table->Flags; + settings->RefScale = save_ref_scale ? table->RefScale : 0.0f; + + MarkIniSettingsDirty(); +} + +void ImGui::TableLoadSettings(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + table->IsSettingsRequestLoad = false; + if (table->Flags & ImGuiTableFlags_NoSavedSettings) + return; + + // Bind settings + ImGuiTableSettings* settings; + if (table->SettingsOffset == -1) + { + settings = TableSettingsFindByID(table->ID); + if (settings == NULL) + return; + if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return... + table->IsSettingsDirty = true; + table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings); + } + else + { + settings = TableGetBoundSettings(table); + } + + table->SettingsLoadedFlags = settings->SaveFlags; + table->RefScale = settings->RefScale; + + // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn + ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); + ImU64 display_order_mask = 0; + for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) + { + int column_n = column_settings->Index; + if (column_n < 0 || column_n >= table->ColumnsCount) + continue; + + ImGuiTableColumn* column = &table->Columns[column_n]; + if (settings->SaveFlags & ImGuiTableFlags_Resizable) + { + if (column_settings->IsStretch) + column->StretchWeight = column_settings->WidthOrWeight; + else + column->WidthRequest = column_settings->WidthOrWeight; + column->AutoFitQueue = 0x00; + } + if (settings->SaveFlags & ImGuiTableFlags_Reorderable) + column->DisplayOrder = column_settings->DisplayOrder; + else + column->DisplayOrder = (ImGuiTableColumnIdx)column_n; + display_order_mask |= (ImU64)1 << column->DisplayOrder; + column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; + column->SortOrder = column_settings->SortOrder; + column->SortDirection = column_settings->SortDirection; + } + + // Validate and fix invalid display order data + const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; + if (display_order_mask != expected_display_order_mask) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; + + // Rebuild index + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; +} + +static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + ImGuiContext& g = *ctx; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; + g.SettingsTables.clear(); +} + +// Apply to existing windows (if any) +static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + ImGuiContext& g = *ctx; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + { + table->IsSettingsRequestLoad = true; + table->SettingsOffset = -1; + } +} + +static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + ImGuiID id = 0; + int columns_count = 0; + if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2) + return NULL; + + if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id)) + { + if (settings->ColumnsCountMax >= columns_count) + { + TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle + return settings; + } + settings->ID = 0; // Invalidate storage, we won't fit because of a count change + } + return ImGui::TableSettingsCreate(id, columns_count); +} + +static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) +{ + // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" + ImGuiTableSettings* settings = (ImGuiTableSettings*)entry; + float f = 0.0f; + int column_n = 0, r = 0, n = 0; + + if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; } + + if (sscanf(line, "Column %d%n", &column_n, &r) == 1) + { + if (column_n < 0 || column_n >= settings->ColumnsCount) + return; + line = ImStrSkipBlank(line + r); + char c = 0; + ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n; + column->Index = (ImGuiTableColumnIdx)column_n; + if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; } + if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; } + if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; } + if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; } + if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; } + if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; } + } +} + +static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + ImGuiContext& g = *ctx; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + { + if (settings->ID == 0) // Skip ditched settings + continue; + + // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped + // (e.g. Order was unchanged) + const bool save_size = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0; + const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0; + const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0; + const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0; + if (!save_size && !save_visible && !save_order && !save_sort) + continue; + + buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve + buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount); + if (settings->RefScale != 0.0f) + buf->appendf("RefScale=%g\n", settings->RefScale); + ImGuiTableColumnSettings* column = settings->GetColumnSettings(); + for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) + { + // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" + bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); + if (!save_column) + continue; + buf->appendf("Column %-2d", column_n); + if (column->UserID != 0) { buf->appendf(" UserID=%08X", column->UserID); } + if (save_size && column->IsStretch) { buf->appendf(" Weight=%.4f", column->WidthOrWeight); } + if (save_size && !column->IsStretch) { buf->appendf(" Width=%d", (int)column->WidthOrWeight); } + if (save_visible) { buf->appendf(" Visible=%d", column->IsEnabled); } + if (save_order) { buf->appendf(" Order=%d", column->DisplayOrder); } + if (save_sort && column->SortOrder != -1) { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); } + buf->append("\n"); + } + buf->append("\n"); + } +} + +void ImGui::TableSettingsAddSettingsHandler() +{ + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Table"; + ini_handler.TypeHash = ImHashStr("Table"); + ini_handler.ClearAllFn = TableSettingsHandler_ClearAll; + ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; + ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll; + ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; + AddSettingsHandler(&ini_handler); +} + +//------------------------------------------------------------------------- +// [SECTION] Tables: Garbage Collection +//------------------------------------------------------------------------- +// - TableRemove() [Internal] +// - TableGcCompactTransientBuffers() [Internal] +// - TableGcCompactSettings() [Internal] +//------------------------------------------------------------------------- + +// Remove Table (currently only used by TestEngine) +void ImGui::TableRemove(ImGuiTable* table) +{ + //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID); + ImGuiContext& g = *GImGui; + int table_idx = g.Tables.GetIndex(table); + //memset(table->RawData.Data, 0, table->RawData.size_in_bytes()); + //memset(table, 0, sizeof(ImGuiTable)); + g.Tables.Remove(table->ID, table); + g.TablesLastTimeActive[table_idx] = -1.0f; +} + +// Free up/compact internal Table buffers for when it gets unused +void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) +{ + //IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); + ImGuiContext& g = *GImGui; + IM_ASSERT(table->MemoryCompacted == false); + table->SortSpecs.Specs = NULL; + table->SortSpecsMulti.clear(); + table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume. + table->ColumnsNames.clear(); + table->MemoryCompacted = true; + for (int n = 0; n < table->ColumnsCount; n++) + table->Columns[n].NameOffset = -1; + g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; +} + +void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) +{ + temp_data->DrawSplitter.ClearFreeMemory(); + temp_data->LastTimeActive = -1.0f; +} + +// Compact and remove unused settings data (currently only used by TestEngine) +void ImGui::TableGcCompactSettings() +{ + ImGuiContext& g = *GImGui; + int required_memory = 0; + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + if (settings->ID != 0) + required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount); + if (required_memory == g.SettingsTables.Buf.Size) + return; + ImChunkStream new_chunk_stream; + new_chunk_stream.Buf.reserve(required_memory); + for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) + if (settings->ID != 0) + memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount)); + g.SettingsTables.swap(new_chunk_stream); +} + + +//------------------------------------------------------------------------- +// [SECTION] Tables: Debugging +//------------------------------------------------------------------------- +// - DebugNodeTable() [Internal] +//------------------------------------------------------------------------- + +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + +static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy) +{ + sizing_policy &= ImGuiTableFlags_SizingMask_; + if (sizing_policy == ImGuiTableFlags_SizingFixedFit) { return "FixedFit"; } + if (sizing_policy == ImGuiTableFlags_SizingFixedSame) { return "FixedSame"; } + if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return "StretchProp"; } + if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return "StretchSame"; } + return "N/A"; +} + +void ImGui::DebugNodeTable(ImGuiTable* table) +{ + ImGuiContext& g = *GImGui; + const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. + if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } + bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); + if (!is_active) { PopStyleColor(); } + if (IsItemHovered()) + GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); + if (IsItemVisible() && table->HoveredColumnBody != -1) + GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255)); + if (!open) + return; + if (table->InstanceCurrent > 0) + Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1); + if (g.IO.ConfigDebugIsDebuggerPresent) + { + if (DebugBreakButton("**DebugBreak**", "in BeginTable()")) + g.DebugBreakInTable = table->ID; + SameLine(); + } + + bool clear_settings = SmallButton("Clear settings"); + BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags)); + BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : ""); + BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); + BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); + BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); + for (int n = 0; n < table->InstanceCurrent + 1; n++) + { + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n); + BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight); + } + //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); + float sum_weights = 0.0f; + for (int n = 0; n < table->ColumnsCount; n++) + if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch) + sum_weights += table->Columns[n].StretchWeight; + for (int n = 0; n < table->ColumnsCount; n++) + { + ImGuiTableColumn* column = &table->Columns[n]; + const char* name = TableGetColumnName(table, n); + char buf[512]; + ImFormatString(buf, IM_ARRAYSIZE(buf), + "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" + "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" + "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" + "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" + "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" + "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..", + n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "", + column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, + column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f, + column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, + column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, + column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags, + (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "", + (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "", + (column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : ""); + Bullet(); + Selectable(buf); + if (IsItemHovered()) + { + ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y); + GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255)); + } + } + if (ImGuiTableSettings* settings = TableGetBoundSettings(table)) + DebugNodeTableSettings(settings); + if (clear_settings) + table->IsResetAllRequest = true; + TreePop(); +} + +void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings) +{ + if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount)) + return; + BulletText("SaveFlags: 0x%08X", settings->SaveFlags); + BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax); + for (int n = 0; n < settings->ColumnsCount; n++) + { + ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n]; + ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None; + BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X", + n, column_settings->DisplayOrder, column_settings->SortOrder, + (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---", + column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID); + } + TreePop(); +} + +#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS + +void ImGui::DebugNodeTable(ImGuiTable*) {} +void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {} + +#endif + + +//------------------------------------------------------------------------- +// [SECTION] Columns, BeginColumns, EndColumns, etc. +// (This is a legacy API, prefer using BeginTable/EndTable!) +//------------------------------------------------------------------------- +// FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.) +//------------------------------------------------------------------------- +// - SetWindowClipRectBeforeSetChannel() [Internal] +// - GetColumnIndex() +// - GetColumnsCount() +// - GetColumnOffset() +// - GetColumnWidth() +// - SetColumnOffset() +// - SetColumnWidth() +// - PushColumnClipRect() [Internal] +// - PushColumnsBackground() [Internal] +// - PopColumnsBackground() [Internal] +// - FindOrCreateColumns() [Internal] +// - GetColumnsID() [Internal] +// - BeginColumns() +// - NextColumn() +// - EndColumns() +// - Columns() +//------------------------------------------------------------------------- + +// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences, +// they would meddle many times with the underlying ImDrawCmd. +// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let +// the subsequent single call to SetCurrentChannel() does it things once. +void ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect) +{ + ImVec4 clip_rect_vec4 = clip_rect.ToVec4(); + window->ClipRect = clip_rect; + window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4; + window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4; +} + +int ImGui::GetColumnIndex() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0; +} + +int ImGui::GetColumnsCount() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1; +} + +float ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm) +{ + return offset_norm * (columns->OffMaxX - columns->OffMinX); +} + +float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset) +{ + return offset / (columns->OffMaxX - columns->OffMinX); +} + +static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f; + +static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) +{ + // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing + // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. + IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); + + float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x; + x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); + if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths)) + x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); + + return x; +} + +float ImGui::GetColumnOffset(int column_index) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (columns == NULL) + return 0.0f; + + if (column_index < 0) + column_index = columns->Current; + IM_ASSERT(column_index < columns->Columns.Size); + + const float t = columns->Columns[column_index].OffsetNorm; + const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t); + return x_offset; +} + +static float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false) +{ + if (column_index < 0) + column_index = columns->Current; + + float offset_norm; + if (before_resize) + offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize; + else + offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm; + return ImGui::GetColumnOffsetFromNorm(columns, offset_norm); +} + +float ImGui::GetColumnWidth(int column_index) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (columns == NULL) + return GetContentRegionAvail().x; + + if (column_index < 0) + column_index = columns->Current; + return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm); +} + +void ImGui::SetColumnOffset(int column_index, float offset) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiOldColumns* columns = window->DC.CurrentColumns; + IM_ASSERT(columns != NULL); + + if (column_index < 0) + column_index = columns->Current; + IM_ASSERT(column_index < columns->Columns.Size); + + const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1); + const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; + + if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow)) + offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); + columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX); + + if (preserve_width) + SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); +} + +void ImGui::SetColumnWidth(int column_index, float width) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + IM_ASSERT(columns != NULL); + + if (column_index < 0) + column_index = columns->Current; + SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width); +} + +void ImGui::PushColumnClipRect(int column_index) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (column_index < 0) + column_index = columns->Current; + + ImGuiOldColumnData* column = &columns->Columns[column_index]; + PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false); +} + +// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns) +void ImGui::PushColumnsBackground() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (columns->Count == 1) + return; + + // Optimization: avoid SetCurrentChannel() + PushClipRect() + columns->HostBackupClipRect = window->ClipRect; + SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect); + columns->Splitter.SetCurrentChannel(window->DrawList, 0); +} + +void ImGui::PopColumnsBackground() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (columns->Count == 1) + return; + + // Optimization: avoid PopClipRect() + SetCurrentChannel() + SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect); + columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); +} + +ImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id) +{ + // We have few columns per window so for now we don't need bother much with turning this into a faster lookup. + for (int n = 0; n < window->ColumnsStorage.Size; n++) + if (window->ColumnsStorage[n].ID == id) + return &window->ColumnsStorage[n]; + + window->ColumnsStorage.push_back(ImGuiOldColumns()); + ImGuiOldColumns* columns = &window->ColumnsStorage.back(); + columns->ID = id; + return columns; +} + +ImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count) +{ + ImGuiWindow* window = GetCurrentWindow(); + + // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. + // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. + PushID(0x11223347 + (str_id ? 0 : columns_count)); + ImGuiID id = window->GetID(str_id ? str_id : "columns"); + PopID(); + + return id; +} + +void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(columns_count >= 1); + IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported + + // Acquire storage for the columns set + ImGuiID id = GetColumnsID(str_id, columns_count); + ImGuiOldColumns* columns = FindOrCreateColumns(window, id); + IM_ASSERT(columns->ID == id); + columns->Current = 0; + columns->Count = columns_count; + columns->Flags = flags; + window->DC.CurrentColumns = columns; + window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); + + columns->HostCursorPosY = window->DC.CursorPos.y; + columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x; + columns->HostInitialClipRect = window->ClipRect; + columns->HostBackupParentWorkRect = window->ParentWorkRect; + window->ParentWorkRect = window->WorkRect; + + // Set state for first column + // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect + const float column_padding = g.Style.ItemSpacing.x; + const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize)); + const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f); + const float max_2 = window->WorkRect.Max.x + half_clip_extend_x; + columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f); + columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f); + columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; + + // Clear data if columns count changed + if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) + columns->Columns.resize(0); + + // Initialize default widths + columns->IsFirstFrame = (columns->Columns.Size == 0); + if (columns->Columns.Size == 0) + { + columns->Columns.reserve(columns_count + 1); + for (int n = 0; n < columns_count + 1; n++) + { + ImGuiOldColumnData column; + column.OffsetNorm = n / (float)columns_count; + columns->Columns.push_back(column); + } + } + + for (int n = 0; n < columns_count; n++) + { + // Compute clipping rectangle + ImGuiOldColumnData* column = &columns->Columns[n]; + float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n)); + float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f); + column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); + column->ClipRect.ClipWithFull(window->ClipRect); + } + + if (columns->Count > 1) + { + columns->Splitter.Split(window->DrawList, 1 + columns->Count); + columns->Splitter.SetCurrentChannel(window->DrawList, 1); + PushColumnClipRect(0); + } + + // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user. + float offset_0 = GetColumnOffset(columns->Current); + float offset_1 = GetColumnOffset(columns->Current + 1); + float width = offset_1 - offset_0; + PushItemWidth(width * 0.65f); + window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; + window->WorkRect.Max.y = window->ContentRegionRect.Max.y; +} + +void ImGui::NextColumn() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems || window->DC.CurrentColumns == NULL) + return; + + ImGuiContext& g = *GImGui; + ImGuiOldColumns* columns = window->DC.CurrentColumns; + + if (columns->Count == 1) + { + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + IM_ASSERT(columns->Current == 0); + return; + } + + // Next column + if (++columns->Current == columns->Count) + columns->Current = 0; + + PopItemWidth(); + + // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect() + // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them), + ImGuiOldColumnData* column = &columns->Columns[columns->Current]; + SetWindowClipRectBeforeSetChannel(window, column->ClipRect); + columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1); + + const float column_padding = g.Style.ItemSpacing.x; + columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); + if (columns->Current > 0) + { + // Columns 1+ ignore IndentX (by canceling it out) + // FIXME-COLUMNS: Unnecessary, could be locked? + window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding; + } + else + { + // New row/line: column 0 honor IndentX. + window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); + window->DC.IsSameLine = false; + columns->LineMinY = columns->LineMaxY; + } + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.y = columns->LineMinY; + window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + window->DC.CurrLineTextBaseOffset = 0.0f; + + // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup. + float offset_0 = GetColumnOffset(columns->Current); + float offset_1 = GetColumnOffset(columns->Current + 1); + float width = offset_1 - offset_0; + PushItemWidth(width * 0.65f); + window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; +} + +void ImGui::EndColumns() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + ImGuiOldColumns* columns = window->DC.CurrentColumns; + IM_ASSERT(columns != NULL); + + PopItemWidth(); + if (columns->Count > 1) + { + PopClipRect(); + columns->Splitter.Merge(window->DrawList); + } + + const ImGuiOldColumnFlags flags = columns->Flags; + columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); + window->DC.CursorPos.y = columns->LineMaxY; + if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize)) + window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX; // Restore cursor max pos, as columns don't grow parent + + // Draw columns borders and handle resize + // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy + bool is_being_resized = false; + if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems) + { + // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers. + const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y); + const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y); + int dragging_column = -1; + for (int n = 1; n < columns->Count; n++) + { + ImGuiOldColumnData* column = &columns->Columns[n]; + float x = window->Pos.x + GetColumnOffset(n); + const ImGuiID column_id = columns->ID + ImGuiID(n); + const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale); + const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); + if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav)) + continue; + + bool hovered = false, held = false; + if (!(flags & ImGuiOldColumnFlags_NoResize)) + { + ButtonBehavior(column_hit_rect, column_id, &hovered, &held); + if (hovered || held) + SetMouseCursor(ImGuiMouseCursor_ResizeEW); + if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize)) + dragging_column = n; + } + + // Draw column + const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); + const float xi = IM_TRUNC(x); + window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); + } + + // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. + if (dragging_column != -1) + { + if (!columns->IsBeingResized) + for (int n = 0; n < columns->Count + 1; n++) + columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm; + columns->IsBeingResized = is_being_resized = true; + float x = GetDraggedColumnOffset(columns, dragging_column); + SetColumnOffset(dragging_column, x); + } + } + columns->IsBeingResized = is_being_resized; + + window->WorkRect = window->ParentWorkRect; + window->ParentWorkRect = columns->HostBackupParentWorkRect; + window->DC.CurrentColumns = NULL; + window->DC.ColumnsOffset.x = 0.0f; + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + NavUpdateCurrentWindowIsScrollPushableX(); +} + +void ImGui::Columns(int columns_count, const char* id, bool borders) +{ + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(columns_count >= 1); + + ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder); + //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior + ImGuiOldColumns* columns = window->DC.CurrentColumns; + if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) + return; + + if (columns != NULL) + EndColumns(); + + if (columns_count != 1) + BeginColumns(id, columns_count, flags); +} + +//------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imgui_widgets.cpp b/lib/imgui/imgui_widgets.cpp new file mode 100644 index 0000000..b5d66ba --- /dev/null +++ b/lib/imgui/imgui_widgets.cpp @@ -0,0 +1,10310 @@ +// dear imgui, v1.91.5 +// (widgets code) + +/* + +Index of this file: + +// [SECTION] Forward Declarations +// [SECTION] Widgets: Text, etc. +// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.) +// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.) +// [SECTION] Widgets: ComboBox +// [SECTION] Data Type and Data Formatting Helpers +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +// [SECTION] Widgets: InputText, InputTextMultiline +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +// [SECTION] Widgets: Selectable +// [SECTION] Widgets: Typing-Select support +// [SECTION] Widgets: Box-Select support +// [SECTION] Widgets: Multi-Select support +// [SECTION] Widgets: Multi-Select helpers +// [SECTION] Widgets: ListBox +// [SECTION] Widgets: PlotLines, PlotHistogram +// [SECTION] Widgets: Value helpers +// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. +// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc. + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_internal.h" + +// System includes +#include // intptr_t + +//------------------------------------------------------------------------- +// Warnings +//------------------------------------------------------------------------- + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#if __has_warning("-Wunknown-warning-option") +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access +#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#endif + +//------------------------------------------------------------------------- +// Data +//------------------------------------------------------------------------- + +// Widgets +static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f; // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior. +static const float DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f; // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags. + +// Those MIN/MAX values are not define because we need to point to them +static const signed char IM_S8_MIN = -128; +static const signed char IM_S8_MAX = 127; +static const unsigned char IM_U8_MIN = 0; +static const unsigned char IM_U8_MAX = 0xFF; +static const signed short IM_S16_MIN = -32768; +static const signed short IM_S16_MAX = 32767; +static const unsigned short IM_U16_MIN = 0; +static const unsigned short IM_U16_MAX = 0xFFFF; +static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); +static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) +static const ImU32 IM_U32_MIN = 0; +static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) +#ifdef LLONG_MIN +static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); +static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); +#else +static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; +static const ImS64 IM_S64_MAX = 9223372036854775807LL; +#endif +static const ImU64 IM_U64_MIN = 0; +#ifdef ULLONG_MAX +static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); +#else +static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); +#endif + +//------------------------------------------------------------------------- +// [SECTION] Forward Declarations +//------------------------------------------------------------------------- + +// For InputTextEx() +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Text, etc. +//------------------------------------------------------------------------- +// - TextEx() [Internal] +// - TextUnformatted() +// - Text() +// - TextV() +// - TextColored() +// - TextColoredV() +// - TextDisabled() +// - TextDisabledV() +// - TextWrapped() +// - TextWrappedV() +// - LabelText() +// - LabelTextV() +// - BulletText() +// - BulletTextV() +//------------------------------------------------------------------------- + +void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); + if (text_end - text <= 2000 || wrap_enabled) + { + // Common case + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size, 0.0f); + if (!ItemAdd(bb, 0)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); + } + else + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. + const char* line = text; + const float line_height = GetTextLineHeight(); + ImVec2 text_size(0, 0); + + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) + { + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + text_size.y = (pos - text_pos).y; + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size, 0.0f); + ItemAdd(bb, 0); + } +} + +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + +void ImGui::Text(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextV(fmt, args); + va_end(args); +} + +void ImGui::TextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const char* text, *text_end; + ImFormatStringToTempBufferV(&text, &text_end, fmt, args); + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + +void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextColoredV(col, fmt, args); + va_end(args); +} + +void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, col); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextDisabled(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextDisabledV(fmt, args); + va_end(args); +} + +void ImGui::TextDisabledV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextWrapped(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextWrappedV(fmt, args); + va_end(args); +} + +void ImGui::TextWrappedV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + const bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set + if (need_backup) + PushTextWrapPos(0.0f); + TextV(fmt, args); + if (need_backup) + PopTextWrapPos(); +} + +void ImGui::LabelText(const char* label, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + LabelTextV(label, fmt, args); + va_end(args); +} + +// Add a label+text combo aligned to other label+value widgets +void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float w = CalcItemWidth(); + + const char* value_text_begin, *value_text_end; + ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args); + const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImVec2 pos = window->DC.CursorPos; + const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); + const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, 0)) + return; + + // Render + RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin, *text_end; + ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); + const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y); // Empty text doesn't add padding + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrLineTextBaseOffset; + ItemSize(total_size, 0.0f); + const ImRect bb(pos, pos + total_size); + if (!ItemAdd(bb, 0)) + return; + + // Render + ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col); + RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Main +//------------------------------------------------------------------------- +// - ButtonBehavior() [Internal] +// - Button() +// - SmallButton() +// - InvisibleButton() +// - ArrowButton() +// - CloseButton() [Internal] +// - CollapseButton() [Internal] +// - GetWindowScrollbarID() [Internal] +// - GetWindowScrollbarRect() [Internal] +// - Scrollbar() [Internal] +// - ScrollbarEx() [Internal] +// - Image() +// - ImageButton() +// - Checkbox() +// - CheckboxFlagsT() [Internal] +// - CheckboxFlags() +// - RadioButton() +// - ProgressBar() +// - Bullet() +// - Hyperlink() +//------------------------------------------------------------------------- + +// The ButtonBehavior() function is key to many interactions and used by many/most widgets. +// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_), +// this code is a little complex. +// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior. +// See the series of events below and the corresponding state reported by dear imgui: +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnClickRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+0 (mouse is outside bb) - - - - - - +// Frame N+1 (mouse moves inside bb) - true - - - - +// Frame N+2 (mouse button is down) - true true true - true +// Frame N+3 (mouse button is down) - true true - - - +// Frame N+4 (mouse moves outside bb) - - true - - - +// Frame N+5 (mouse moves inside bb) - true true - - - +// Frame N+6 (mouse button is released) true true - - true - +// Frame N+7 (mouse button is released) - true - - - - +// Frame N+8 (mouse moves outside bb) - - - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+2 (mouse button is down) true true true true - true +// Frame N+3 (mouse button is down) - true true - - - +// Frame N+6 (mouse button is released) - true - - true - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnRelease: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+2 (mouse button is down) - true - - - true +// Frame N+3 (mouse button is down) - true - - - - +// Frame N+6 (mouse button is released) true true - - - - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// with PressedOnDoubleClick: return-value IsItemHovered() IsItemActive() IsItemActivated() IsItemDeactivated() IsItemClicked() +// Frame N+0 (mouse button is down) - true - - - true +// Frame N+1 (mouse button is down) - true - - - - +// Frame N+2 (mouse button is released) - true - - - - +// Frame N+3 (mouse button is released) - true - - - - +// Frame N+4 (mouse button is down) true true true true - true +// Frame N+5 (mouse button is down) - true true - - - +// Frame N+6 (mouse button is released) - true - - true - +// Frame N+7 (mouse button is released) - true - - - - +//------------------------------------------------------------------------------------------------------------------------------------------------ +// Note that some combinations are supported, +// - PressedOnDragDropHold can generally be associated with any flag. +// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported. +//------------------------------------------------------------------------------------------------------------------------------------------------ +// The behavior of the return-value changes when ImGuiButtonFlags_Repeat is set: +// Repeat+ Repeat+ Repeat+ Repeat+ +// PressedOnClickRelease PressedOnClick PressedOnRelease PressedOnDoubleClick +//------------------------------------------------------------------------------------------------------------------------------------------------- +// Frame N+0 (mouse button is down) - true - true +// ... - - - - +// Frame N + RepeatDelay true true - true +// ... - - - - +// Frame N + RepeatDelay + RepeatRate*N true true - true +//------------------------------------------------------------------------------------------------------------------------------------------------- + +// - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc. +// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);' +// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading. +// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature. +// One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior() +// with same ID and different MouseButton (see #8030). You can fix it by: +// (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags. +// or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag() +bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Default behavior inherited from item flags + // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that. + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags); + if (flags & ImGuiButtonFlags_AllowOverlap) + item_flags |= ImGuiItemFlags_AllowOverlap; + + // Default only reacts to left mouse button + if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) + flags |= ImGuiButtonFlags_MouseButtonLeft; + + // Default behavior requires click + release inside bounding box + if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) + flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_; + + ImGuiWindow* backup_hovered_window = g.HoveredWindow; + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; + if (flatten_hovered_children) + g.HoveredWindow = window; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + // Alternate registration spot, for when caller didn't use ItemAdd() + if (g.LastItemData.ID != id) + IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL); +#endif + + bool pressed = false; + bool hovered = ItemHoverable(bb, id, item_flags); + + // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button + if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) + if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + { + hovered = true; + SetHoveredID(id); + if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) + { + pressed = true; + g.DragDropHoldJustPressedId = id; + FocusWindow(window); + } + } + + if (flatten_hovered_children) + g.HoveredWindow = backup_hovered_window; + + // Mouse handling + const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; + if (hovered) + { + IM_ASSERT(id != 0); // Lazily check inside rare path. + + // Poll mouse buttons + // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. + // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. + int mouse_button_clicked = -1; + int mouse_button_released = -1; + for (int button = 0; button < 3; button++) + if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. + { + if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } + if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } + } + + // Process initial action + const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt); + if (mods_ok) + { + if (mouse_button_clicked != -1 && g.ActiveId != id) + { + if (!(flags & ImGuiButtonFlags_NoSetKeyOwner)) + SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id); + if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) + { + SetActiveID(id, window); + g.ActiveIdMouseButton = mouse_button_clicked; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + { + SetFocusID(id, window); + FocusWindow(window); + } + else + { + FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child + } + } + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) + { + pressed = true; + if (flags & ImGuiButtonFlags_NoHoldingActiveId) + ClearActiveID(); + else + SetActiveID(id, window); // Hold on ID + g.ActiveIdMouseButton = mouse_button_clicked; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + { + SetFocusID(id, window); + FocusWindow(window); + } + else + { + FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child + } + } + } + if (flags & ImGuiButtonFlags_PressedOnRelease) + { + if (mouse_button_released != -1) + { + const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior + if (!has_repeated_at_least_once) + pressed = true; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why. + ClearActiveID(); + } + } + + // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). + // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. + if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) + if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id)) + pressed = true; + } + + if (pressed && g.IO.ConfigNavCursorVisibleAuto) + g.NavCursorVisible = false; + } + + // Keyboard/Gamepad navigation handling + // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse. + if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav) + if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) + hovered = true; + if (g.NavActivateDownId == id) + { + bool nav_activated_by_code = (g.NavActivateId == id); + bool nav_activated_by_inputs = (g.NavActivatePressedId == id); + if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) + { + // Avoid pressing multiple keys from triggering excessive amount of repeat events + const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); + const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter); + const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate); + const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration); + nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + } + if (nav_activated_by_code || nav_activated_by_inputs) + { + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + pressed = true; + SetActiveID(id, window); + g.ActiveIdSource = g.NavInputSource; + if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) + SetFocusID(id, window); + if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) + g.ActiveIdFromShortcut = true; + } + } + + // Process while held + bool held = false; + if (g.ActiveId == id) + { + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + + const int mouse_button = g.ActiveIdMouseButton; + if (mouse_button == -1) + { + // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304). + ClearActiveID(); + } + else if (IsMouseDown(mouse_button, test_owner_id)) + { + held = true; + } + else + { + bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0; + bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0; + if ((release_in || release_anywhere) && !g.DragDropActive) + { + // Report as pressed when releasing the mouse (this is the most common path) + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; + bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps + bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); + if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) + pressed = true; + } + ClearActiveID(); + } + if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto) + g.NavCursorVisible = false; + } + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) + { + // When activated using Nav, we hold on the ActiveID until activation button is released + if (g.NavActivateDownId == id) + held = true; // hovered == true not true as we are already likely hovered on direct activation. + else + ClearActiveID(); + } + if (pressed) + g.ActiveIdHasBeenPressedBefore = true; + } + + // Activation highlight (this may be a remote activation) + if (g.NavHighlightActivatedId == id) + hovered = true; + + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + + return pressed; +} + +bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) + pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavCursor(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + + if (g.LogEnabled) + LogSetNextTextDecoration("[", "]"); + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + +bool ImGui::Button(const char* label, const ImVec2& size_arg) +{ + return ButtonEx(label, size_arg, ImGuiButtonFlags_None); +} + +// Small buttons fits within text without additional vertical spacing. +bool ImGui::SmallButton(const char* label) +{ + ImGuiContext& g = *GImGui; + float backup_padding_y = g.Style.FramePadding.y; + g.Style.FramePadding.y = 0.0f; + bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); + g.Style.FramePadding.y = backup_padding_y; + return pressed; +} + +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. + IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); + + const ImGuiID id = window->GetID(str_id); + ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(size); + if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + RenderNavCursor(bb, id); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); + return pressed; +} + +bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiID id = window->GetID(str_id); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + const float default_size = GetFrameHeight(); + ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderNavCursor(bb, id); + RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); + RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); + return pressed; +} + +bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) +{ + float sz = GetFrameHeight(); + return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None); +} + +// Button to close a window +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) + // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); + ImRect bb_interact = bb; + const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); + if (area_to_visible_ratio < 1.5f) + bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); + + // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. + // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). + bool is_clipped = !ItemAdd(bb_interact, id); + + bool hovered, held; + bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); + if (is_clipped) + return pressed; + + // Render + ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); + if (hovered) + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); + ImU32 cross_col = GetColorU32(ImGuiCol_Text); + ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); + + return pressed; +} + +bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); + bool is_clipped = !ItemAdd(bb, id); + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + if (is_clipped) + return pressed; + + // Render + ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImU32 text_col = GetColorU32(ImGuiCol_Text); + if (hovered || held) + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); + RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + + // Switch to moving the window after mouse is moved beyond the initial drag threshold + if (IsItemActive() && IsMouseDragging(0)) + StartMouseMovingWindow(window); + + return pressed; +} + +ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) +{ + return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); +} + +// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. +ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) +{ + const ImRect outer_rect = window->Rect(); + const ImRect inner_rect = window->InnerRect; + const float border_size = window->WindowBorderSize; + const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) + IM_ASSERT(scrollbar_size > 0.0f); + if (axis == ImGuiAxis_X) + return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); + else + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); +} + +void ImGui::Scrollbar(ImGuiAxis axis) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = GetWindowScrollbarID(window, axis); + + // Calculate scrollbar bounding box + ImRect bb = GetWindowScrollbarRect(window, axis); + ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; + if (axis == ImGuiAxis_X) + { + rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; + if (!window->ScrollbarY) + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; + } + else + { + if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) + rounding_corners |= ImDrawFlags_RoundCornersTopRight; + if (!window->ScrollbarX) + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; + } + float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; + float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; + ImS64 scroll = (ImS64)window->Scroll[axis]; + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners); + window->Scroll[axis] = (float)scroll; +} + +// Vertical/Horizontal scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +// Still, the code should probably be made simpler.. +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const float bb_frame_width = bb_frame.GetWidth(); + const float bb_frame_height = bb_frame.GetHeight(); + if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) + return false; + + // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab) + float alpha = 1.0f; + if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f) + alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); + if (alpha <= 0.0f) + return false; + + const ImGuiStyle& style = g.Style; + const bool allow_interaction = (alpha >= 1.0f); + + ImRect bb = bb_frame; + bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); + + // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) + const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); + + // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav); + ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); + + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v); + float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space + if (held && allow_interaction && grab_h_norm < 1.0f) + { + const float scrollbar_pos_v = bb.Min[axis]; + const float mouse_pos_v = g.IO.MousePos[axis]; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); + + const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0; + if (g.ActiveIdIsJustActivated) + { + // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab + const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0); + g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir; + g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; + } + + // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position + if (g.ScrollbarSeekMode == 0) + { + // Absolute seeking + const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); + } + else + { + // Page by page + if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode) + { + float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f; + *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max); + } + } + + // Update values for rendering + scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + + // Update distance to grab now that we have seek'ed and saturated + //if (seek_absolute) + // g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; + } + + // Render + const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); + window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags); + ImRect grab_rect; + if (axis == ImGuiAxis_X) + grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); + else + grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels); + window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); + + return held; +} + +// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples +// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f; + const ImVec2 padding(border_size, border_size); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + return; + + // Render + if (border_size > 0.0f) + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size); + window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); +} + +// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) +// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. +bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImVec2 padding = g.Style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); + ItemSize(bb); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavCursor(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); + window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} + +// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. +bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col); +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy API obsoleted in 1.89. Two differences with new ImageButton() +// - old ImageButton() used ImTextureId as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID) +// - new ImageButton() requires an explicit 'const char* str_id' +// - old ImageButton() had frame_padding' override argument. +// - new ImageButton() always use style.FramePadding. +/* +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + // Default to using texture ID as ID. User can still push string/integer prefixes. + PushID((ImTextureID)(intptr_t)user_texture_id); + if (frame_padding >= 0) + PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding)); + bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col); + if (frame_padding >= 0) + PopStyleVar(); + PopID(); + return ret; +} +*/ +#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +bool ImGui::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); + const bool is_visible = ItemAdd(total_bb, id); + const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (!is_visible) + if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support + { + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + return false; + } + + // Range-Selection/Multi-selection support (header) + bool checked = *v; + if (is_multi_select) + MultiSelectItemHeader(id, &checked, NULL); + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + + // Range-Selection/Multi-selection support (footer) + if (is_multi_select) + MultiSelectItemFooter(id, &checked, &pressed); + else if (pressed) + checked = !checked; + + if (*v != checked) + { + *v = checked; + pressed = true; // return value + MarkItemEdited(id); + } + + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + if (is_visible) + { + RenderNavCursor(total_bb, id); + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); + if (mixed_value) + { + // Undocumented tristate/mixed/indeterminate checkbox (#2644) + // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) + ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); + window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); + } + else if (*v) + { + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); + } + } + const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); + if (g.LogEnabled) + LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); + if (is_visible && label_size.x > 0.0f) + RenderText(label_pos, label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + return pressed; +} + +template +bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) +{ + bool all_on = (*flags & flags_value) == flags_value; + bool any_on = (*flags & flags_value) != 0; + bool pressed; + if (!all_on && any_on) + { + ImGuiContext& g = *GImGui; + g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; + pressed = Checkbox(label, &all_on); + } + else + { + pressed = Checkbox(label, &all_on); + + } + if (pressed) + { + if (all_on) + *flags |= flags_value; + else + *flags &= ~flags_value; + } + return pressed; +} + +bool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value) +{ + return CheckboxFlagsT(label, flags, flags_value); +} + +bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) +{ + return CheckboxFlagsT(label, flags, flags_value); +} + +bool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value) +{ + return CheckboxFlagsT(label, flags, flags_value); +} + +bool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value) +{ + return CheckboxFlagsT(label, flags, flags_value); +} + +bool ImGui::RadioButton(const char* label, bool active) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = IM_ROUND(center.x); + center.y = IM_ROUND(center.y); + const float radius = (square_sz - 1.0f) * 0.5f; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + MarkItemEdited(id); + + RenderNavCursor(total_bb, id); + const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); + if (active) + { + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark)); + } + + if (style.FrameBorderSize > 0.0f) + { + window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); + } + + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); + if (g.LogEnabled) + LogRenderedText(&label_pos, active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(label_pos, label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + +// FIXME: This would work nicely if it was a public template, e.g. 'template RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it.. +bool ImGui::RadioButton(const char* label, int* v, int v_button) +{ + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + *v = v_button; + return pressed; +} + +// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size +void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos; + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f); + ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, 0)) + return; + + // Fraction < 0.0f will display an indeterminate progress bar animation + // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works. + const bool is_indeterminate = (fraction < 0.0f); + if (!is_indeterminate) + fraction = ImSaturate(fraction); + + // Out of courtesy we accept a NaN fraction without crashing + float fill_n0 = 0.0f; + float fill_n1 = (fraction == fraction) ? fraction : 0.0f; + + if (is_indeterminate) + { + const float fill_width_n = 0.2f; + fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n; + fill_n1 = ImSaturate(fill_n0 + fill_width_n); + fill_n0 = ImSaturate(fill_n0); + } + + // Render + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); + RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding); + + // Default displaying the fraction as percentage string, but user can override it + // Don't display text for indeterminate bars by default + char overlay_buf[32]; + if (!is_indeterminate || overlay != NULL) + { + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); + overlay = overlay_buf; + } + + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + { + float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; + RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); + } + } +} + +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + { + SameLine(0, style.FramePadding.x * 2); + return; + } + + // Render and stay on same line + ImU32 text_col = GetColorU32(ImGuiCol_Text); + RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col); + SameLine(0, style.FramePadding.x * 2.0f); +} + +// This is provided as a convenience for being an often requested feature. +// FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system. +// Because of this we currently don't provide many styling options for this widget +// (e.g. hovered/active colors are automatically inferred from a single color). +bool ImGui::TextLink(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(label); + const char* label_end = FindRenderedTextEnd(label); + + ImVec2 pos = window->DC.CursorPos; + ImVec2 size = CalcTextSize(label, label_end, true); + ImRect bb(pos, pos + size); + ItemSize(size, 0.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + RenderNavCursor(bb, id); + + if (hovered) + SetMouseCursor(ImGuiMouseCursor_Hand); + + ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink]; + ImVec4 line_colf = text_colf; + { + // FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets, + // as we are currently experimenting/planning a different styling system. + float h, s, v; + ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v); + if (held || hovered) + { + v = ImSaturate(v + (held ? 0.4f : 0.3f)); + h = ImFmod(h + 0.02f, 1.0f); + } + ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z); + v = ImSaturate(v - 0.20f); + ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z); + } + + float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f); + window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode. + + PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); + RenderText(bb.Min, label, label_end); + PopStyleColor(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + +void ImGui::TextLinkOpenURL(const char* label, const char* url) +{ + ImGuiContext& g = *GImGui; + if (url == NULL) + url = label; + if (TextLink(label)) + if (g.PlatformIO.Platform_OpenInShellFn != NULL) + g.PlatformIO.Platform_OpenInShellFn(&g, url); + SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label + if (BeginPopupContextItem()) + { + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink))) + SetClipboardText(url); + EndPopup(); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Low-level Layout helpers +//------------------------------------------------------------------------- +// - Spacing() +// - Dummy() +// - NewLine() +// - AlignTextToFramePadding() +// - SeparatorEx() [Internal] +// - Separator() +// - SplitterBehavior() [Internal] +// - ShrinkWidths() [Internal] +//------------------------------------------------------------------------- + +void ImGui::Spacing() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ItemSize(ImVec2(0, 0)); +} + +void ImGui::Dummy(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(size); + ItemAdd(bb, 0); +} + +void ImGui::NewLine() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.IsSameLine = false; + if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. + ItemSize(ImVec2(0, 0)); + else + ItemSize(ImVec2(0.0f, g.FontSize)); + window->DC.LayoutType = backup_layout_type; +} + +void ImGui::AlignTextToFramePadding() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); + window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y); +} + +// Horizontal/vertical separating line +// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues. +// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are. +void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected + IM_ASSERT(thickness > 0.0f); + + if (flags & ImGuiSeparatorFlags_Vertical) + { + // Vertical separator, for menu bars (use current line height). + float y1 = window->DC.CursorPos.y; + float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y; + const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2)); + ItemSize(ImVec2(thickness, 0.0f)); + if (!ItemAdd(bb, 0)) + return; + + // Draw + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); + if (g.LogEnabled) + LogText(" |"); + } + else if (flags & ImGuiSeparatorFlags_Horizontal) + { + // Horizontal Separator + float x1 = window->DC.CursorPos.x; + float x2 = window->WorkRect.Max.x; + + // Preserve legacy behavior inside Columns() + // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set. + // We currently don't need to provide the same feature for tables because tables naturally have border features. + ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; + if (columns) + { + x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03 + x2 = window->Pos.x + window->Size.x; + PushColumnsBackground(); + } + + // We don't provide our width to the layout so that it doesn't get feed back into AutoFit + // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) + const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change. + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness)); + ItemSize(ImVec2(0.0f, thickness_for_layout)); + + if (ItemAdd(bb, 0)) + { + // Draw + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); + if (g.LogEnabled) + LogRenderedText(&bb.Min, "--------------------------------\n"); + + } + if (columns) + { + PopColumnsBackground(); + columns->LineMinY = window->DC.CursorPos.y; + } + } +} + +void ImGui::Separator() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + // Those flags should eventually be configurable by the user + // FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f. + ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; + + // Only applies to legacy Columns() api as they relied on Separator() a lot. + if (window->DC.CurrentColumns) + flags |= ImGuiSeparatorFlags_SpanAllColumns; + + SeparatorEx(flags, 1.0f); +} + +void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = g.Style; + + const ImVec2 label_size = CalcTextSize(label, label_end, false); + const ImVec2 pos = window->DC.CursorPos; + const ImVec2 padding = style.SeparatorTextPadding; + + const float separator_thickness = style.SeparatorTextBorderSize; + const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); + const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); + ItemSize(min_size, text_baseline_y); + if (!ItemAdd(bb, id)) + return; + + const float sep1_x1 = pos.x; + const float sep2_x2 = bb.Max.x; + const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); + + const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); + const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN + + // This allows using SameLine() to position something in the 'extra_w' + window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x; + + const ImU32 separator_col = GetColorU32(ImGuiCol_Separator); + if (label_size.x > 0.0f) + { + const float sep1_x2 = label_pos.x - style.ItemSpacing.x; + const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x; + if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f) + window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness); + if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f) + window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); + if (g.LogEnabled) + LogSetNextTextDecoration("---", NULL); + RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size); + } + else + { + if (g.LogEnabled) + LogText("---"); + if (separator_thickness > 0.0f) + window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); + } +} + +void ImGui::SeparatorText(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want: + // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight) + // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string) + // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...' + // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item, + // and then we can turn this into a format function. + SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f); +} + +// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) + return false; + + // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is + // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. + // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. + ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + button_flags |= ImGuiButtonFlags_AllowOverlap; +#endif + + bool hovered, held; + ImRect bb_interact = bb; + bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); + ButtonBehavior(bb_interact, id, &hovered, &held, button_flags); + if (hovered) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb + + if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); + + ImRect bb_render = bb; + if (held) + { + float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis]; + + // Minimum pane size + float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); + float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); + if (mouse_delta < -size_1_maximum_delta) + mouse_delta = -size_1_maximum_delta; + if (mouse_delta > size_2_maximum_delta) + mouse_delta = size_2_maximum_delta; + + // Apply resize + if (mouse_delta != 0.0f) + { + *size1 = ImMax(*size1 + mouse_delta, min_size1); + *size2 = ImMax(*size2 - mouse_delta, min_size2); + bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); + MarkItemEdited(id); + } + } + + // Render at new position + if (bg_col & IM_COL32_A_MASK) + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); + const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); + + return held; +} + +static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs) +{ + const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs; + const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs; + if (int d = (int)(b->Width - a->Width)) + return d; + return (b->Index - a->Index); +} + +// Shrink excess width from a set of item, by removing width from the larger items first. +// Set items Width to -1.0f to disable shrinking this item. +void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess) +{ + if (count == 1) + { + if (items[0].Width >= 0.0f) + items[0].Width = ImMax(items[0].Width - width_excess, 1.0f); + return; + } + ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); + int count_same_width = 1; + while (width_excess > 0.0f && count_same_width < count) + { + while (count_same_width < count && items[0].Width <= items[count_same_width].Width) + count_same_width++; + float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f); + if (max_width_to_remove_per_item <= 0.0f) + break; + float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item); + for (int item_n = 0; item_n < count_same_width; item_n++) + items[item_n].Width -= width_to_remove_per_item; + width_excess -= width_to_remove_per_item * count_same_width; + } + + // Round width and redistribute remainder + // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator. + width_excess = 0.0f; + for (int n = 0; n < count; n++) + { + float width_rounded = ImTrunc(items[n].Width); + width_excess += items[n].Width - width_rounded; + items[n].Width = width_rounded; + } + while (width_excess > 0.0f) + for (int n = 0; n < count && width_excess > 0.0f; n++) + { + float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f); + items[n].Width += width_to_add; + width_excess -= width_to_add; + } +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ComboBox +//------------------------------------------------------------------------- +// - CalcMaxPopupHeightFromItemCount() [Internal] +// - BeginCombo() +// - BeginComboPopup() [Internal] +// - EndCombo() +// - BeginComboPreview() [Internal] +// - EndComboPreview() [Internal] +// - Combo() +//------------------------------------------------------------------------- + +static float CalcMaxPopupHeightFromItemCount(int items_count) +{ + ImGuiContext& g = *GImGui; + if (items_count <= 0) + return FLT_MAX; + return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); +} + +bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + if (flags & ImGuiComboFlags_WidthFitPreview) + IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); + + const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &bb)) + return false; + + // Open on click + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); + bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); + if (pressed && !popup_open) + { + OpenPopupEx(popup_id, ImGuiPopupFlags_None); + popup_open = true; + } + + // Render shape + const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); + RenderNavCursor(bb, id); + if (!(flags & ImGuiComboFlags_NoPreview)) + window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); + if (!(flags & ImGuiComboFlags_NoArrowButton)) + { + ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImU32 text_col = GetColorU32(ImGuiCol_Text); + window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); + if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) + RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); + } + RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); + + // Custom preview + if (flags & ImGuiComboFlags_CustomPreview) + { + g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); + IM_ASSERT(preview_value == NULL || preview_value[0] == 0); + preview_value = NULL; + } + + // Render preview and label + if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) + { + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); + RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); + } + if (label_size.x > 0) + RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); + + if (!popup_open) + return false; + + g.NextWindowData.Flags = backup_next_window_data_flags; + return BeginComboPopup(popup_id, bb, flags); +} + +bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) + { + g.NextWindowData.ClearFlags(); + return false; + } + + // Set popup size + float w = bb.GetWidth(); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) + { + g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); + } + else + { + if ((flags & ImGuiComboFlags_HeightMask_) == 0) + flags |= ImGuiComboFlags_HeightRegular; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + int popup_max_height_in_items = -1; + if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; + else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; + else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; + ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX); + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size + constraint_min.x = w; + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f) + constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); + SetNextWindowSizeConstraints(constraint_min, constraint_max); + } + + // This is essentially a specialized version of BeginPopupEx() + char name[16]; + ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth + + // Set position given a custom constraint (peak into expected window size so we can position it) + // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? + // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? + if (ImGuiWindow* popup_window = FindWindowByName(name)) + if (popup_window->WasActive) + { + // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. + ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); + popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" + ImRect r_outer = GetPopupAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); + SetNextWindowPos(pos); + } + + // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() + ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; + PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text + bool ret = Begin(name, NULL, window_flags); + PopStyleVar(); + if (!ret) + { + EndPopup(); + IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above + return false; + } + g.BeginComboDepth++; + return true; +} + +void ImGui::EndCombo() +{ + ImGuiContext& g = *GImGui; + EndPopup(); + g.BeginComboDepth--; +} + +// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements +// (Experimental, see GitHub issues: #1658, #4168) +bool ImGui::BeginComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) + return false; + IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? + if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional) + return false; + + // FIXME: This could be contained in a PushWorkRect() api + preview_data->BackupCursorPos = window->DC.CursorPos; + preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; + preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; + preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + preview_data->BackupLayout = window->DC.LayoutType; + window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.IsSameLine = false; + PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); + + return true; +} + +void ImGui::EndComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future + ImDrawList* draw_list = window->DrawList; + if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) + if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command + { + draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; + draw_list->_TryMergeDrawCmds(); + } + PopClipRect(); + window->DC.CursorPos = preview_data->BackupCursorPos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); + window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; + window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; + window->DC.LayoutType = preview_data->BackupLayout; + window->DC.IsSameLine = false; + preview_data->PreviewRect = ImRect(); +} + +// Getter for the old Combo() API: const char*[] +static const char* Items_ArrayGetter(void* data, int idx) +{ + const char* const* items = (const char* const*)data; + return items[idx]; +} + +// Getter for the old Combo() API: "item1\0item2\0item3\0" +static const char* Items_SingleStringGetter(void* data, int idx) +{ + const char* items_separated_by_zeros = (const char*)data; + int items_count = 0; + const char* p = items_separated_by_zeros; + while (*p) + { + if (idx == items_count) + break; + p += strlen(p) + 1; + items_count++; + } + return *p ? p : NULL; +} + +// Old API, prefer using BeginCombo() nowadays if you can. +bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) +{ + ImGuiContext& g = *GImGui; + + // Call the getter to obtain the preview string which is a parameter to BeginCombo() + const char* preview_value = NULL; + if (*current_item >= 0 && *current_item < items_count) + preview_value = getter(user_data, *current_item); + + // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. + if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) + SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); + + if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) + return false; + + // Display items + bool value_changed = false; + ImGuiListClipper clipper; + clipper.Begin(items_count); + clipper.IncludeItemByIndex(*current_item); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const char* item_text = getter(user_data, i); + if (item_text == NULL) + item_text = "*Unknown item*"; + + PushID(i); + const bool item_selected = (i == *current_item); + if (Selectable(item_text, item_selected) && *current_item != i) + { + value_changed = true; + *current_item = i; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + + EndCombo(); + if (value_changed) + MarkItemEdited(g.LastItemData.ID); + + return value_changed; +} + +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + +// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) +{ + int items_count = 0; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open + while (*p) + { + p += strlen(p) + 1; + items_count++; + } + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); + return value_changed; +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); }; +static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx) +{ + ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data; + const char* s = NULL; + data->OldCallback(data->UserData, idx, &s); + return s; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items) +{ + ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; + return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items); +} +bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items) +{ + ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; + return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items); +} + +#endif + +//------------------------------------------------------------------------- +// [SECTION] Data Type and Data Formatting Helpers [Internal] +//------------------------------------------------------------------------- +// - DataTypeGetInfo() +// - DataTypeFormatString() +// - DataTypeApplyOp() +// - DataTypeApplyFromText() +// - DataTypeCompare() +// - DataTypeClamp() +// - GetMinimumStepAtDecimalPrecision +// - RoundScalarWithFormat<>() +//------------------------------------------------------------------------- + +static const ImGuiDataTypeInfo GDataTypeInfo[] = +{ + { sizeof(char), "S8", "%d", "%d" }, // ImGuiDataType_S8 + { sizeof(unsigned char), "U8", "%u", "%u" }, + { sizeof(short), "S16", "%d", "%d" }, // ImGuiDataType_S16 + { sizeof(unsigned short), "U16", "%u", "%u" }, + { sizeof(int), "S32", "%d", "%d" }, // ImGuiDataType_S32 + { sizeof(unsigned int), "U32", "%u", "%u" }, +#ifdef _MSC_VER + { sizeof(ImS64), "S64", "%I64d","%I64d" }, // ImGuiDataType_S64 + { sizeof(ImU64), "U64", "%I64u","%I64u" }, +#else + { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 + { sizeof(ImU64), "U64", "%llu", "%llu" }, +#endif + { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double + { sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool +}; +IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); + +const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) +{ + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + return &GDataTypeInfo[data_type]; +} + +int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format) +{ + // Signedness doesn't matter when pushing integer arguments + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) + return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data); + if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data); + if (data_type == ImGuiDataType_Float) + return ImFormatString(buf, buf_size, format, *(const float*)p_data); + if (data_type == ImGuiDataType_Double) + return ImFormatString(buf, buf_size, format, *(const double*)p_data); + if (data_type == ImGuiDataType_S8) + return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data); + if (data_type == ImGuiDataType_U8) + return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data); + if (data_type == ImGuiDataType_S16) + return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data); + if (data_type == ImGuiDataType_U16) + return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data); + IM_ASSERT(0); + return 0; +} + +void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2) +{ + IM_ASSERT(op == '+' || op == '-'); + switch (data_type) + { + case ImGuiDataType_S8: + if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + return; + case ImGuiDataType_U8: + if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + return; + case ImGuiDataType_S16: + if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + return; + case ImGuiDataType_U16: + if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + return; + case ImGuiDataType_S32: + if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + return; + case ImGuiDataType_U32: + if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + return; + case ImGuiDataType_S64: + if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + return; + case ImGuiDataType_U64: + if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + return; + case ImGuiDataType_Float: + if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } + if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } + return; + case ImGuiDataType_Double: + if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } + if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } + return; + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); +} + +// User can input math operators (e.g. +100) to edit a numerical values. +// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. +bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty) +{ + // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. + const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); + ImGuiDataTypeStorage data_backup; + memcpy(&data_backup, p_data, type_info->Size); + + while (ImCharIsBlankA(*buf)) + buf++; + if (!buf[0]) + { + if (p_data_when_empty != NULL) + { + memcpy(p_data, p_data_when_empty, type_info->Size); + return memcmp(&data_backup, p_data, type_info->Size) != 0; + } + return false; + } + + // Sanitize format + // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf + // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %. + char format_sanitized[32]; + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + format = type_info->ScanFmt; + else + format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); + + // Small types need a 32-bit buffer to receive the result from scanf() + int v32 = 0; + if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1) + return false; + if (type_info->Size < 4) + { + if (data_type == ImGuiDataType_S8) + *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); + else if (data_type == ImGuiDataType_U8) + *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); + else if (data_type == ImGuiDataType_S16) + *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); + else if (data_type == ImGuiDataType_U16) + *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); + else + IM_ASSERT(0); + } + + return memcmp(&data_backup, p_data, type_info->Size) != 0; +} + +template +static int DataTypeCompareT(const T* lhs, const T* rhs) +{ + if (*lhs < *rhs) return -1; + if (*lhs > *rhs) return +1; + return 0; +} + +int ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2) +{ + switch (data_type) + { + case ImGuiDataType_S8: return DataTypeCompareT((const ImS8* )arg_1, (const ImS8* )arg_2); + case ImGuiDataType_U8: return DataTypeCompareT((const ImU8* )arg_1, (const ImU8* )arg_2); + case ImGuiDataType_S16: return DataTypeCompareT((const ImS16* )arg_1, (const ImS16* )arg_2); + case ImGuiDataType_U16: return DataTypeCompareT((const ImU16* )arg_1, (const ImU16* )arg_2); + case ImGuiDataType_S32: return DataTypeCompareT((const ImS32* )arg_1, (const ImS32* )arg_2); + case ImGuiDataType_U32: return DataTypeCompareT((const ImU32* )arg_1, (const ImU32* )arg_2); + case ImGuiDataType_S64: return DataTypeCompareT((const ImS64* )arg_1, (const ImS64* )arg_2); + case ImGuiDataType_U64: return DataTypeCompareT((const ImU64* )arg_1, (const ImU64* )arg_2); + case ImGuiDataType_Float: return DataTypeCompareT((const float* )arg_1, (const float* )arg_2); + case ImGuiDataType_Double: return DataTypeCompareT((const double*)arg_1, (const double*)arg_2); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return 0; +} + +template +static bool DataTypeClampT(T* v, const T* v_min, const T* v_max) +{ + // Clamp, both sides are optional, return true if modified + if (v_min && *v < *v_min) { *v = *v_min; return true; } + if (v_max && *v > *v_max) { *v = *v_max; return true; } + return false; +} + +bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max) +{ + switch (data_type) + { + case ImGuiDataType_S8: return DataTypeClampT((ImS8* )p_data, (const ImS8* )p_min, (const ImS8* )p_max); + case ImGuiDataType_U8: return DataTypeClampT((ImU8* )p_data, (const ImU8* )p_min, (const ImU8* )p_max); + case ImGuiDataType_S16: return DataTypeClampT((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max); + case ImGuiDataType_U16: return DataTypeClampT((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max); + case ImGuiDataType_S32: return DataTypeClampT((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max); + case ImGuiDataType_U32: return DataTypeClampT((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max); + case ImGuiDataType_S64: return DataTypeClampT((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max); + case ImGuiDataType_U64: return DataTypeClampT((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max); + case ImGuiDataType_Float: return DataTypeClampT((float* )p_data, (const float* )p_min, (const float* )p_max); + case ImGuiDataType_Double: return DataTypeClampT((double*)p_data, (const double*)p_min, (const double*)p_max); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +bool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data) +{ + ImGuiContext& g = *GImGui; + return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0; +} + +static float GetMinimumStepAtDecimalPrecision(int decimal_precision) +{ + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + if (decimal_precision < 0) + return FLT_MIN; + return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); +} + +template +TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) +{ + IM_UNUSED(data_type); + IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); + const char* fmt_start = ImParseFormatFindStart(format); + if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string + return v; + + // Sanitize format + char fmt_sanitized[32]; + ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); + fmt_start = fmt_sanitized; + + // Format value with our rounding, and read back + char v_str[64]; + ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); + const char* p = v_str; + while (*p == ' ') + p++; + v = (TYPE)ImAtof(p); + + return v; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +//------------------------------------------------------------------------- +// - DragBehaviorT<>() [Internal] +// - DragBehavior() [Internal] +// - DragScalar() +// - DragScalarN() +// - DragFloat() +// - DragFloat2() +// - DragFloat3() +// - DragFloat4() +// - DragFloatRange2() +// - DragInt() +// - DragInt2() +// - DragInt3() +// - DragInt4() +// - DragIntRange2() +//------------------------------------------------------------------------- + +// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) +template +bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange))); + const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround); + const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + + // Default tweak speed + if (v_speed == 0.0f && is_bounded && (v_max - v_min < FLT_MAX)) + v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); + + // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings + float adjust_delta = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) + { + adjust_delta = g.IO.MouseDelta[axis]; + if (g.IO.KeyAlt) + adjust_delta *= 1.0f / 100.0f; + if (g.IO.KeyShift) + adjust_delta *= 10.0f; + } + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) + { + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; + const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); + const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); + const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f; + adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor; + v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); + } + adjust_delta *= v_speed; + + // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter. + if (axis == ImGuiAxis_Y) + adjust_delta = -adjust_delta; + + // For logarithmic use our range is effectively 0..1 so scale the delta into that range + if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0 + adjust_delta /= (float)(v_max - v_min); + + // Clear current value on activation + // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. + const bool is_just_activated = g.ActiveIdIsJustActivated; + const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); + if (is_just_activated || is_already_past_limits_and_pushing_outward) + { + g.DragCurrentAccum = 0.0f; + g.DragCurrentAccumDirty = false; + } + else if (adjust_delta != 0.0f) + { + g.DragCurrentAccum += adjust_delta; + g.DragCurrentAccumDirty = true; + } + + if (!g.DragCurrentAccumDirty) + return false; + + TYPE v_cur = *v; + FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; + + float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true + const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense) + if (is_logarithmic) + { + // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; + logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); + + // Convert to parametric space, apply delta, convert back + float v_old_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + float v_new_parametric = v_old_parametric + g.DragCurrentAccum; + v_cur = ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + v_old_ref_for_accum_remainder = v_old_parametric; + } + else + { + v_cur += (SIGNEDTYPE)g.DragCurrentAccum; + } + + // Round to user desired precision based on format string + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_cur = RoundScalarWithFormatT(format, data_type, v_cur); + + // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. + g.DragCurrentAccumDirty = false; + if (is_logarithmic) + { + // Convert to parametric space, apply delta, convert back + float v_new_parametric = ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder); + } + else + { + g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); + } + + // Lose zero sign for float/double + if (v_cur == (TYPE)-0) + v_cur = (TYPE)0; + + if (*v != v_cur && is_bounded) + { + if (is_wrapped) + { + // Wrap values + if (v_cur < v_min) + v_cur += v_max - v_min + (is_floating_point ? 0 : 1); + if (v_cur > v_max) + v_cur -= v_max - v_min + (is_floating_point ? 0 : 1); + } + else + { + // Clamp values + handle overflow/wrap-around for integer types. + if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point)) + v_cur = v_min; + if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point)) + v_cur = v_max; + } + } + + // Apply result + if (*v == v_cur) + return false; + *v = v_cur; + return true; +} + +bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) +{ + // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. + IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); + + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + { + // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) + ClearActiveID(); + else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + ClearActiveID(); + } + if (g.ActiveId != id) + return false; + if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + return false; + + switch (data_type) + { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } + case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags); + case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags); + case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags); + case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags); + case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, flags); + case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, flags); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional. +// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. +bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); + bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); + if (!temp_input_is_active) + { + // Tabbing or CTRL-clicking on Drag turns it into an InputText + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); + const bool make_active = (clicked || double_clicked || g.NavActivateId == id); + if (make_active && (clicked || double_clicked)) + SetKeyOwner(ImGuiKey_MouseLeft, id); + if (make_active && temp_input_allowed) + if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) + temp_input_is_active = true; + + // (Optional) simple click (without moving) turns Drag into an InputText + if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) + if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) + { + g.NavActivateId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + temp_input_is_active = true; + } + + if (make_active && !temp_input_is_active) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + } + } + + if (temp_input_is_active) + { + // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) + bool clamp_enabled = false; + if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL)) + { + const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max + if (p_min == NULL || p_max == NULL || clamp_range_dir < 0) + clamp_enabled = true; + else if (clamp_range_dir == 0) + clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true; + } + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); + } + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavCursor(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + // Drag behavior + const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags); + if (value_changed) + MarkItemEdited(id); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); + return value_changed; +} + +bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags); + PopID(); + PopItemWidth(); + p_data = (void*)((char*)p_data + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags); +} + +// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. +bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2, CalcItemWidth()); + + float min_min = (v_min >= v_max) ? -FLT_MAX : v_min; + float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); + ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); + bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); + float max_max = (v_min >= v_max) ? FLT_MAX : v_max; + ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); + value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextEx(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags); +} + +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags); +} + +// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this. +bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2, CalcItemWidth()); + + int min_min = (v_min >= v_max) ? INT_MIN : v_min; + int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max); + ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0); + bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min); + int max_max = (v_min >= v_max) ? INT_MAX : v_max; + ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0); + value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextEx(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +//------------------------------------------------------------------------- +// - ScaleRatioFromValueT<> [Internal] +// - ScaleValueFromRatioT<> [Internal] +// - SliderBehaviorT<>() [Internal] +// - SliderBehavior() [Internal] +// - SliderScalar() +// - SliderScalarN() +// - SliderFloat() +// - SliderFloat2() +// - SliderFloat3() +// - SliderFloat4() +// - SliderAngle() +// - SliderInt() +// - SliderInt2() +// - SliderInt3() +// - SliderInt4() +// - VSliderScalar() +// - VSliderFloat() +// - VSliderInt() +//------------------------------------------------------------------------- + +// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT) +template +float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) +{ + if (v_min == v_max) + return 0.0f; + IM_UNUSED(data_type); + + const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); + if (is_logarithmic) + { + bool flipped = v_max < v_min; + + if (flipped) // Handle the case where the range is backwards + ImSwap(v_min, v_max); + + // Fudge min/max to avoid getting close to log(0) + FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; + FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; + + // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) + if ((v_min == 0.0f) && (v_max < 0.0f)) + v_min_fudged = -logarithmic_zero_epsilon; + else if ((v_max == 0.0f) && (v_min < 0.0f)) + v_max_fudged = -logarithmic_zero_epsilon; + + float result; + if (v_clamped <= v_min_fudged) + result = 0.0f; // Workaround for values that are in-range but below our fudge + else if (v_clamped >= v_max_fudged) + result = 1.0f; // Workaround for values that are in-range but above our fudge + else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions + { + float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine) + float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; + float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; + if (v == 0.0f) + result = zero_point_center; // Special case for exactly zero + else if (v < 0.0f) + result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L; + else + result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R)); + } + else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider + result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged)); + else + result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged)); + + return flipped ? (1.0f - result) : result; + } + else + { + // Linear slider + return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min)); + } +} + +// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) +template +TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) +{ + // We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" + // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. + if (t <= 0.0f || v_min == v_max) + return v_min; + if (t >= 1.0f) + return v_max; + + TYPE result = (TYPE)0; + if (is_logarithmic) + { + // Fudge min/max to avoid getting silly results close to zero + FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min; + FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max; + + const bool flipped = v_max < v_min; // Check if range is "backwards" + if (flipped) + ImSwap(v_min_fudged, v_max_fudged); + + // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon) + if ((v_max == 0.0f) && (v_min < 0.0f)) + v_max_fudged = -logarithmic_zero_epsilon; + + float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range + + if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts + { + float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space + float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; + float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize; + if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R) + result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise) + else if (t_with_flip < zero_point_center) + result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L)))); + else + result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R)))); + } + else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider + result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip))); + else + result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip)); + } + else + { + // Linear slider + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + if (is_floating_point) + { + result = ImLerp(v_min, v_max, t); + } + else if (t < 1.0) + { + // - For integer values we want the clicking position to match the grab box so we round above + // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. + // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 + // range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. + FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t; + result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5))); + } + } + + return result; +} + +// FIXME: Try to move more of the code into shared SliderBehavior() +template +bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it. + + // Calculate bounds + const float grab_padding = 2.0f; // FIXME: Should be part of style. + const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; + float grab_sz = style.GrabMinSize; + if (!is_floating_point && v_range_f >= 0.0f) // v_range_f < 0 may happen on integer overflows + grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit + grab_sz = ImMin(grab_sz, slider_sz); + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; + const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f; + + float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true + float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true + if (is_logarithmic) + { + // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; + logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); + zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); + } + + // Process interacting with the slider + bool value_changed = false; + if (g.ActiveId == id) + { + bool set_new_value = false; + float clicked_t = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (!g.IO.MouseDown[0]) + { + ClearActiveID(); + } + else + { + const float mouse_abs_pos = g.IO.MousePos[axis]; + if (g.ActiveIdIsJustActivated) + { + float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + if (axis == ImGuiAxis_Y) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. + g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; + } + if (slider_usable_sz > 0.0f) + clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); + if (axis == ImGuiAxis_Y) + clicked_t = 1.0f - clicked_t; + set_new_value = true; + } + } + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) + { + if (g.ActiveIdIsJustActivated) + { + g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation + g.SliderCurrentAccumDirty = false; + } + + float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis); + if (input_delta != 0.0f) + { + const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); + const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; + if (decimal_precision > 0) + { + input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds + if (tweak_slow) + input_delta /= 10.0f; + } + else + { + if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow) + input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps + else + input_delta /= 100.0f; + } + if (tweak_fast) + input_delta *= 10.0f; + + g.SliderCurrentAccum += input_delta; + g.SliderCurrentAccumDirty = true; + } + + float delta = g.SliderCurrentAccum; + if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + { + ClearActiveID(); + } + else if (g.SliderCurrentAccumDirty) + { + clicked_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + + if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits + { + set_new_value = false; + g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate + } + else + { + set_new_value = true; + float old_clicked_t = clicked_t; + clicked_t = ImSaturate(clicked_t + delta); + + // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator + TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_new = RoundScalarWithFormatT(format, data_type, v_new); + float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + + if (delta > 0) + g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta); + else + g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta); + } + + g.SliderCurrentAccumDirty = false; + } + } + + if (set_new_value) + if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + set_new_value = false; + + if (set_new_value) + { + TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + + // Round to user desired precision based on format string + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_new = RoundScalarWithFormatT(format, data_type, v_new); + + // Apply result + if (*v != v_new) + { + *v = v_new; + value_changed = true; + } + } + } + + if (slider_sz < 1.0f) + { + *out_grab_bb = ImRect(bb.Min, bb.Min); + } + else + { + // Output grab position so it can be displayed by the caller + float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + if (axis == ImGuiAxis_Y) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + if (axis == ImGuiAxis_X) + *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); + else + *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); + } + + return value_changed; +} + +// For 32-bit and larger types, slider bounds are limited to half the natural type range. +// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. +// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. +bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. + IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); + IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX() + + switch (data_type) + { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; } + case ImGuiDataType_S32: + IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2); + return SliderBehaviorT(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_U32: + IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2); + return SliderBehaviorT(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_S64: + IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2); + return SliderBehaviorT(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_U64: + IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2); + return SliderBehaviorT(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_Float: + IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f); + return SliderBehaviorT(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_Double: + IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f); + return SliderBehaviorT(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required. +// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. +bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); + bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); + if (!temp_input_is_active) + { + // Tabbing or CTRL-clicking on Slider turns it into an input box + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); + const bool make_active = (clicked || g.NavActivateId == id); + if (make_active && clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); + if (make_active && temp_input_allowed) + if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) + temp_input_is_active = true; + + if (make_active && !temp_input_is_active) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + } + } + + if (temp_input_is_active) + { + // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp) + const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0; + return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL); + } + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavCursor(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + if (grab_bb.Max.x > grab_bb.Min.x) + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0)); + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) +{ + if (format == NULL) + format = "%.0f deg"; + float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags); + *v_rad = v_deg * (2 * IM_PI) / 360.0f; + return value_changed; +} + +bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags); +} + +bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, id)) + return false; + + // Default format string when passing NULL + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags); + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); + if (clicked || g.NavActivateId == id) + { + if (clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + } + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavCursor(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + if (grab_bb.Max.y > grab_bb.Min.y) + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) +{ + return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags); +} + +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) +{ + return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +//------------------------------------------------------------------------- +// - ImParseFormatFindStart() [Internal] +// - ImParseFormatFindEnd() [Internal] +// - ImParseFormatTrimDecorations() [Internal] +// - ImParseFormatSanitizeForPrinting() [Internal] +// - ImParseFormatSanitizeForScanning() [Internal] +// - ImParseFormatPrecision() [Internal] +// - TempInputTextScalar() [Internal] +// - InputScalar() +// - InputScalarN() +// - InputFloat() +// - InputFloat2() +// - InputFloat3() +// - InputFloat4() +// - InputInt() +// - InputInt2() +// - InputInt3() +// - InputInt4() +// - InputDouble() +//------------------------------------------------------------------------- + +// We don't use strchr() because our strings are usually very short and often start with '%' +const char* ImParseFormatFindStart(const char* fmt) +{ + while (char c = fmt[0]) + { + if (c == '%' && fmt[1] != '%') + return fmt; + else if (c == '%') + fmt++; + fmt++; + } + return fmt; +} + +const char* ImParseFormatFindEnd(const char* fmt) +{ + // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. + if (fmt[0] != '%') + return fmt; + const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); + const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); + for (char c; (c = *fmt) != 0; fmt++) + { + if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) + return fmt + 1; + if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) + return fmt + 1; + } + return fmt; +} + +// Extract the format out of a format string with leading or trailing decorations +// fmt = "blah blah" -> return "" +// fmt = "%.3f" -> return fmt +// fmt = "hello %.3f" -> return fmt + 6 +// fmt = "%.3f hello" -> return buf written with "%.3f" +const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size) +{ + const char* fmt_start = ImParseFormatFindStart(fmt); + if (fmt_start[0] != '%') + return ""; + const char* fmt_end = ImParseFormatFindEnd(fmt_start); + if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. + return fmt_start; + ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); + return buf; +} + +// Sanitize format +// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi +// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. +void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size) +{ + const char* fmt_end = ImParseFormatFindEnd(fmt_in); + IM_UNUSED(fmt_out_size); + IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! + while (fmt_in < fmt_end) + { + char c = *fmt_in++; + if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. + *(fmt_out++) = c; + } + *fmt_out = 0; // Zero-terminate +} + +// - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" +const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) +{ + const char* fmt_end = ImParseFormatFindEnd(fmt_in); + const char* fmt_out_begin = fmt_out; + IM_UNUSED(fmt_out_size); + IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! + bool has_type = false; + while (fmt_in < fmt_end) + { + char c = *fmt_in++; + if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#')) + continue; + has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits + if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. + *(fmt_out++) = c; + } + *fmt_out = 0; // Zero-terminate + return fmt_out_begin; +} + +template +static const char* ImAtoi(const char* src, TYPE* output) +{ + int negative = 0; + if (*src == '-') { negative = 1; src++; } + if (*src == '+') { src++; } + TYPE v = 0; + while (*src >= '0' && *src <= '9') + v = (v * 10) + (*src++ - '0'); + *output = negative ? -v : v; + return src; +} + +// Parse display precision back from the display format string +// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. +int ImParseFormatPrecision(const char* fmt, int default_precision) +{ + fmt = ImParseFormatFindStart(fmt); + if (fmt[0] != '%') + return default_precision; + fmt++; + while (*fmt >= '0' && *fmt <= '9') + fmt++; + int precision = INT_MAX; + if (*fmt == '.') + { + fmt = ImAtoi(fmt + 1, &precision); + if (precision < 0 || precision > 99) + precision = default_precision; + } + if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation + precision = -1; + if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) + precision = -1; + return (precision == INT_MAX) ? default_precision : precision; +} + +// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) +// FIXME: Facilitate using this in variety of other situations. +// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but +// the expected relationship between TempInputXXX functions and LastItemData is a little fishy. +bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) +{ + // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. + // We clear ActiveID on the first frame to allow the InputText() taking it back. + ImGuiContext& g = *GImGui; + const bool init = (g.TempInputId != id); + if (init) + ClearActiveID(); + + g.CurrentWindow->DC.CursorPos = bb.Min; + g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); + if (init) + { + // First frame we started displaying the InputText widget, we expect it to take the active id. + IM_ASSERT(g.ActiveId == id); + g.TempInputId = g.ActiveId; + } + return value_changed; +} + +// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! +// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. +// However this may not be ideal for all uses, as some user code may break on out of bound values. +bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) +{ + // FIXME: May need to clarify display behavior if format doesn't contain %. + // "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405 + ImGuiContext& g = *GImGui; + const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); + char fmt_buf[32]; + char data_buf[32]; + format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + if (format[0] == 0) + format = type_info->PrintFmt; + DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); + ImStrTrimBlanks(data_buf); + + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; + g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData. + bool value_changed = false; + if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) + { + // Backup old value + size_t data_type_size = type_info->Size; + ImGuiDataTypeStorage data_backup; + memcpy(&data_backup, p_data, data_type_size); + + // Apply new value (or operations) then clamp + DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL); + if (p_clamp_min || p_clamp_max) + { + if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) + ImSwap(p_clamp_min, p_clamp_max); + DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); + } + + // Only mark as edited if new value is different + g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; + value_changed = memcmp(&data_backup, p_data, data_type_size) != 0; + if (value_changed) + MarkItemEdited(id); + } + return value_changed; +} + +void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data) +{ + ImGuiContext& g = *GImGui; + g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal; + memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size); +} + +// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. +// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. +bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! + + if (format == NULL) + format = DataTypeGetInfo(data_type)->PrintFmt; + + void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue; + + char buf[64]; + if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) + buf[0] = 0; + else + DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + + // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited. + // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; + flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; + + bool value_changed = false; + if (p_step == NULL) + { + if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + } + else + { + const float button_size = GetFrameHeight(); + + BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() + PushID(label); + SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); + if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); + + // Step buttons + const ImVec2 backup_frame_padding = style.FramePadding; + style.FramePadding.x = style.FramePadding.y; + if (flags & ImGuiInputTextFlags_ReadOnly) + BeginDisabled(); + PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("-", ImVec2(button_size, button_size))) + { + DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("+", ImVec2(button_size, button_size))) + { + DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); + value_changed = true; + } + PopItemFlag(); + if (flags & ImGuiInputTextFlags_ReadOnly) + EndDisabled(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + style.FramePadding = backup_frame_padding; + + PopID(); + EndGroup(); + } + + g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited; + if (value_changed) + MarkItemEdited(g.LastItemData.ID); + + return value_changed; +} + +bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components, CalcItemWidth()); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + if (i > 0) + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= InputScalar("", data_type, p_data, p_step, p_step_fast, format, flags); + PopID(); + PopItemWidth(); + p_data = (void*)((char*)p_data + type_size); + } + PopID(); + + const char* label_end = FindRenderedTextEnd(label); + if (label != label_end) + { + SameLine(0.0f, g.Style.ItemInnerSpacing.x); + TextEx(label, label_end); + } + + EndGroup(); + return value_changed; +} + +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) +{ + return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags); +} + +bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); +} + +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags) +{ + // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. + const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags); +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags); +} + +bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) +{ + return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint +//------------------------------------------------------------------------- +// - imstb_textedit.h include +// - InputText() +// - InputTextWithHint() +// - InputTextMultiline() +// - InputTextGetCharInfo() [Internal] +// - InputTextReindexLines() [Internal] +// - InputTextReindexLinesRange() [Internal] +// - InputTextEx() [Internal] +// - DebugNodeInputTextState() [Internal] +//------------------------------------------------------------------------- + +namespace ImStb +{ +#include "imstb_textedit.h" +} + +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); +} + +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or InputTextEx() manually if you need multi-line + hint. + return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); +} + +// This is only used in the path where the multiline widget is inactivate. +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) +{ + int line_count = 0; + const char* s = text_begin; + while (true) + { + const char* s_eol = strchr(s, '\n'); + line_count++; + if (s_eol == NULL) + { + s = s + strlen(s); + break; + } + s = s_eol + 1; + } + *out_text_end = s; + return line_count; +} + +// FIXME: Ideally we'd share code with ImFont::CalcTextSizeA() +static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line) +{ + ImGuiContext& g = *ctx; + ImFont* font = g.Font; + const float line_height = g.FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0, 0); + float line_width = 0.0f; + + const char* s = text_begin; + while (s < text_end) + { + unsigned int c = (unsigned int)*s; + if (c < 0x80) + s += 1; + else + s += ImTextCharFromUtf8(&c, s, text_end); + + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (out_offset) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) +// With our UTF-8 use of stb_textedit: +// - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine. +// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW). +// - ...but we don't use that feature. +namespace ImStb +{ +static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; } +static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextA[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextA.Data + line_start_idx + char_idx, obj->TextA.Data + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; } +static char STB_TEXTEDIT_NEWLINE = '\n'; +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) +{ + const char* text = obj->TextA.Data; + const char* text_remaining = NULL; + const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, &text_remaining, NULL, true); + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = (int)(text_remaining - (text + line_start_idx)); +} + +#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL +#define IMSTB_TEXTEDIT_GETPREVCHARINDEX IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL + +static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) +{ + if (idx >= obj->TextLen) + return obj->TextLen + 1; + unsigned int c; + return idx + ImTextCharFromUtf8(&c, obj->TextA.Data + idx, obj->TextA.Data + obj->TextLen); +} + +static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx) +{ + if (idx <= 0) + return -1; + const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, obj->TextA.Data + idx); + return (int)(p - obj->TextA.Data); +} + +static bool ImCharIsSeparatorW(unsigned int c) +{ + static const unsigned int separator_list[] = + { + ',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D, + '[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\', 0xFFE5, '/', 0x30FB, 0xFF0F, + '\n', '\r', + }; + for (unsigned int separator : separator_list) + if (c == separator) + return true; + return false; +} + +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) +{ + // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + const char* curr_p = obj->TextA.Data + idx; + const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p); + unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextA.Data + obj->TextLen); + unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextA.Data + obj->TextLen); + + bool prev_white = ImCharIsBlankW(prev_c); + bool prev_separ = ImCharIsSeparatorW(prev_c); + bool curr_white = ImCharIsBlankW(curr_c); + bool curr_separ = ImCharIsSeparatorW(curr_c); + return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) +{ + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + const char* curr_p = obj->TextA.Data + idx; + const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextA.Data, curr_p); + unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextA.Data + obj->TextLen); + unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextA.Data + obj->TextLen); + + bool prev_white = ImCharIsBlankW(prev_c); + bool prev_separ = ImCharIsSeparatorW(prev_c); + bool curr_white = ImCharIsBlankW(curr_c); + bool curr_separ = ImCharIsSeparatorW(curr_c); + return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) +{ + idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); + while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) + idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx); + return idx < 0 ? 0 : idx; +} +static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) +{ + int len = obj->TextLen; + idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); + while (idx < len && !is_word_boundary_from_left(obj, idx)) + idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); + return idx > len ? len : idx; +} +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) +{ + idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); + int len = obj->TextLen; + while (idx < len && !is_word_boundary_from_right(obj, idx)) + idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx); + return idx > len ? len : idx; +} +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + +static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) +{ + char* dst = obj->TextA.Data + pos; + + obj->Edited = true; + obj->TextLen -= n; + + // Offset remaining text (FIXME-OPT: Use memmove) + const char* src = obj->TextA.Data + pos + n; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; +} + +static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len) +{ + const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; + const int text_len = obj->TextLen; + IM_ASSERT(pos <= text_len); + + if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity)) + return false; + + // Grow internal buffer if needed + if (new_text_len + text_len + 1 > obj->TextA.Size) + { + if (!is_resizable) + return false; + obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1); + } + + char* text = obj->TextA.Data; + if (pos != text_len) + memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos)); + memcpy(text + pos, new_text, (size_t)new_text_len); + + obj->Edited = true; + obj->TextLen += new_text_len; + obj->TextA[obj->TextLen] = '\0'; + + return true; +} + +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x200000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x200001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x200002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x200003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x200004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x200005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x200006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x200007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x200008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x200009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x20000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x20000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x20000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x20000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_PGUP 0x20000E // keyboard input to move cursor up a page +#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page +#define STB_TEXTEDIT_K_SHIFT 0x400000 + +#define IMSTB_TEXTEDIT_IMPLEMENTATION +#define IMSTB_TEXTEDIT_memmove memmove +#include "imstb_textedit.h" + +// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling +// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) +{ + stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len); + ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen); + state->cursor = state->select_start = state->select_end = 0; + if (text_len <= 0) + return; + if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) + { + state->cursor = state->select_start = state->select_end = text_len; + state->has_preferred_x = 0; + return; + } + IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() +} + +} // namespace ImStb + +// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators. +ImGuiInputTextState::ImGuiInputTextState() +{ + memset(this, 0, sizeof(*this)); + Stb = IM_NEW(ImStbTexteditState); + memset(Stb, 0, sizeof(*Stb)); +} + +ImGuiInputTextState::~ImGuiInputTextState() +{ + IM_DELETE(Stb); +} + +void ImGuiInputTextState::OnKeyPressed(int key) +{ + stb_textedit_key(this, Stb, key); + CursorFollow = true; + CursorAnimReset(); +} + +void ImGuiInputTextState::OnCharPressed(unsigned int c) +{ + // Convert the key to a UTF8 byte sequence. + // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great. + char utf8[5]; + ImTextCharToUtf8(utf8, c); + stb_textedit_text(this, Stb, utf8, (int)strlen(utf8)); + CursorFollow = true; + CursorAnimReset(); +} + +// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header. +void ImGuiInputTextState::CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking +void ImGuiInputTextState::CursorClamp() { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); } +bool ImGuiInputTextState::HasSelection() const { return Stb->select_start != Stb->select_end; } +void ImGuiInputTextState::ClearSelection() { Stb->select_start = Stb->select_end = Stb->cursor; } +int ImGuiInputTextState::GetCursorPos() const { return Stb->cursor; } +int ImGuiInputTextState::GetSelectionStart() const { return Stb->select_start; } +int ImGuiInputTextState::GetSelectionEnd() const { return Stb->select_end; } +void ImGuiInputTextState::SelectAll() { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; } +void ImGuiInputTextState::ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } +void ImGuiInputTextState::ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; } +void ImGuiInputTextState::ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } + +ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() +{ + memset(this, 0, sizeof(*this)); +} + +// Public API to manipulate UTF-8 text +// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) +// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. +void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) +{ + IM_ASSERT(pos + bytes_count <= BufTextLen); + char* dst = Buf + pos; + const char* src = Buf + pos + bytes_count; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; + + if (CursorPos >= pos + bytes_count) + CursorPos -= bytes_count; + else if (CursorPos >= pos) + CursorPos = pos; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen -= bytes_count; +} + +void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +{ + // Accept null ranges + if (new_text == new_text_end) + return; + + // Grow internal buffer if needed + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; + const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); + if (new_text_len + BufTextLen >= BufSize) + { + if (!is_resizable) + return; + + // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!) + ImGuiContext& g = *Ctx; + ImGuiInputTextState* edit_state = &g.InputTextState; + IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); + IM_ASSERT(Buf == edit_state->TextA.Data); + int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; + edit_state->TextA.resize(new_buf_size + 1); + Buf = edit_state->TextA.Data; + BufSize = edit_state->BufCapacity = new_buf_size; + } + + if (BufTextLen != pos) + memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); + memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); + Buf[BufTextLen + new_text_len] = '\0'; + + if (CursorPos >= pos) + CursorPos += new_text_len; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen += new_text_len; +} + +// Return false to discard a character. +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) +{ + unsigned int c = *p_char; + + // Filter non-printable (NB: isprint is unreliable! see #2467) + bool apply_named_filters = true; + if (c < 0x20) + { + bool pass = false; + pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) + pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0; + if (!pass) + return false; + apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. + } + + if (input_source_is_clipboard == false) + { + // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) + if (c == 127) + return false; + + // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) + if (c >= 0xE000 && c <= 0xF8FF) + return false; + } + + // Filter Unicode ranges we are not handling in this build + if (c > IM_UNICODE_CODEPOINT_MAX) + return false; + + // Generic named filters + if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))) + { + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. + // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. + // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. + // Change the default decimal_point with: + // ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point; + // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. + ImGuiContext& g = *ctx; + const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint; + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)) + if (c == '.' || c == ',') + c = c_decimal_point; + + // Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) + // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may + // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) + if (c >= 0xFF01 && c <= 0xFF5E) + c = c - 0xFF01 + 0x21; + + // Allow 0-9 . - + * / + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + // Allow 0-9 . - + * / e E + if (flags & ImGuiInputTextFlags_CharsScientific) + if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) + return false; + + // Allow 0-9 a-F A-F + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + // Turn a-z into A-Z + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + c += (unsigned int)('A' - 'a'); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsBlankW(c)) + return false; + + *p_char = c; + } + + // Custom callback filter + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiContext& g = *GImGui; + ImGuiInputTextCallbackData callback_data; + callback_data.Ctx = &g; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Find the shortest single replacement we can make to get the new text from the old text. +// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end. +// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly. +static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a) +{ + const char* old_buf = state->CallbackTextBackup.Data; + const int old_length = state->CallbackTextBackup.Size - 1; + + const int shorter_length = ImMin(old_length, new_length_a); + int first_diff; + for (first_diff = 0; first_diff < shorter_length; first_diff++) + if (old_buf[first_diff] != new_buf_a[first_diff]) + break; + if (first_diff == old_length && first_diff == new_length_a) + return; + + int old_last_diff = old_length - 1; + int new_last_diff = new_length_a - 1; + for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--) + if (old_buf[old_last_diff] != new_buf_a[new_last_diff]) + break; + + const int insert_len = new_last_diff - first_diff + 1; + const int delete_len = old_last_diff - first_diff + 1; + if (insert_len > 0 || delete_len > 0) + if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len)) + for (int i = 0; i < delete_len; i++) + p[i] = old_buf[first_diff + i]; +} + +// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables) +// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714) +// It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick, +// but that more likely be attractive when we do have _NoLiveEdit flag available. +void ImGui::InputTextDeactivateHook(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiInputTextState* state = &g.InputTextState; + if (id == 0 || state->ID != id) + return; + g.InputTextDeactivatedState.ID = state->ID; + if (state->Flags & ImGuiInputTextFlags_ReadOnly) + { + g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. + } + else + { + IM_ASSERT(state->TextA.Data != 0); + g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1); + memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1); + } +} + +// Edit a string of text +// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". +// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match +// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. +// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. +// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h +// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are +// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT(buf != NULL && buf_size >= 0); + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + const ImGuiStyle& style = g.Style; + + const bool RENDER_SELECTION_WHEN_INACTIVE = false; + const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) + BeginGroup(); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line + const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size); + + ImGuiWindow* draw_window = window; + ImVec2 inner_size = frame_size; + ImGuiLastItemData item_data_backup; + if (is_multiline) + { + ImVec2 backup_pos = window->DC.CursorPos; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + { + EndGroup(); + return false; + } + item_data_backup = g.LastItemData; + window->DC.CursorPos = backup_pos; + + // Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. + if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput)) + g.NavActivateId = 0; + + // Prevent NavActivate reactivating in BeginChild() when we are already active. + const ImGuiID backup_activate_id = g.NavActivateId; + if (g.ActiveId == id) // Prevent reactivation + g.NavActivateId = 0; + + // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. + PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); + PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); + PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges + bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); + g.NavActivateId = backup_activate_id; + PopStyleVar(3); + PopStyleColor(); + if (!child_visible) + { + EndChild(); + EndGroup(); + return false; + } + draw_window = g.CurrentWindow; // Child window + draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.CursorPos += style.FramePadding; + inner_size.x -= draw_window->ScrollbarSizes.x; + } + else + { + // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) + ItemSize(total_bb, style.FramePadding.y); + if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + return false; + } + + // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417) + bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover); + if (hovered) + SetMouseCursor(ImGuiMouseCursor_TextInput); + if (hovered && g.NavHighlightItemUnderNav) + hovered = false; + + // We are only allowed to access the state if we are already the active widget. + ImGuiInputTextState* state = GetInputTextState(id); + + if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) + flags |= ImGuiInputTextFlags_ReadOnly; + const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; + if (is_resizable) + IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! + + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); + + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); + const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); + bool clear_active_id = false; + bool select_all = false; + + float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; + + const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf); + const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav); + const bool init_state = (init_make_active || user_scroll_active); + if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) + { + // Access state even if we don't own it yet. + state = &g.InputTextState; + state->CursorAnimReset(); + state->ReloadUserBuf = false; + + // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) + InputTextDeactivateHook(state->ID); + + // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) + const int buf_len = (int)strlen(buf); + if (!init_reload_from_user_buf) + { + // Take a copy of the initial buffer value. + state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); + } + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? + bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf); + if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0))) + recycle_state = false; + + // Start edition + state->ID = id; + state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string. + state->TextLen = (int)strlen(buf); + memcpy(state->TextA.Data, buf, state->TextLen + 1); + + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + state->CursorClamp(); + } + else + { + state->Scroll = ImVec2(0.0f, 0.0f); + stb_textedit_initialize_state(state->Stb, !is_multiline); + } + + if (init_reload_from_user_buf) + { + state->Stb->select_start = state->ReloadSelectionStart; + state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; + state->CursorClamp(); + } + else if (!is_multiline) + { + if (flags & ImGuiInputTextFlags_AutoSelectAll) + select_all = true; + if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) + select_all = true; + if (user_clicked && io.KeyCtrl) + select_all = true; + } + + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) + state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863) + } + + const bool is_osx = io.ConfigMacOSXBehaviors; + if (g.ActiveId != id && init_make_active) + { + IM_ASSERT(state && state->ID == id); + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + } + if (g.ActiveId == id) + { + // Declare some inputs, the other are registered and polled via Shortcut() routing system. + // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts. + const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End }; + for (ImGuiKey key : always_owned_keys) + SetKeyOwner(key, id); + if (user_clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); + g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) + { + g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + SetKeyOwner(ImGuiKey_UpArrow, id); + SetKeyOwner(ImGuiKey_DownArrow, id); + } + if (is_multiline) + { + SetKeyOwner(ImGuiKey_PageUp, id); + SetKeyOwner(ImGuiKey_PageDown, id); + } + // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu + if (is_osx) + SetKeyOwner(ImGuiMod_Alt, id); + + // Expose scroll in a manner that is agnostic to us using a child window + if (is_multiline && state != NULL) + state->Scroll.y = draw_window->Scroll.y; + } + + // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) + if (g.ActiveId == id && state == NULL) + ClearActiveID(); + + // Release focus when we click outside + if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 + clear_active_id = true; + + // Lock the decision of whether we are going to take the path displaying the cursor or selection + bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool value_changed = false; + bool validated = false; + + // Select the buffer to render. + const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state; + const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); + + // Password pushes a temporary font with only a fallback glyph + if (is_password && !is_displaying_hint) + { + const ImFontGlyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } + + // Process mouse inputs and character inputs + if (g.ActiveId == id) + { + IM_ASSERT(state != NULL); + state->Edited = false; + state->BufCapacity = buf_size; + state->Flags = flags; + + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner library-wide concept of Selected vs Active. + g.ActiveIdAllowOverlap = !io.MouseDown[0]; + + // Edit in progress + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x; + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); + + if (select_all) + { + state->SelectAll(); + state->SelectedAllMouseLock = true; + } + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) + { + stb_textedit_click(state, state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(state->Stb); + state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor); + state->Stb->select_end = state->Stb->cursor; + ImStb::stb_textedit_clamp(state, state->Stb); + } + else + { + // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n'; + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb->select_start, state->Stb->select_end); + state->Stb->cursor = state->Stb->select_end; + } + state->CursorFollow = false; + } + state->CursorAnimReset(); + } + else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) + { + if (hovered) + { + if (io.KeyShift) + stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); + else + stb_textedit_click(state, state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + } + } + else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + { + stb_textedit_drag(state, state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + state->CursorFollow = true; + } + if (state->SelectedAllMouseLock && !io.MouseDown[0]) + state->SelectedAllMouseLock = false; + + // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) + // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) + if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly) + { + if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id)) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + state->OnCharPressed(c); + } + // FIXME: Implement Shift+Tab + /* + if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id)) + { + } + */ + } + + // Process regular text input (before we check for Return because using some IME will effectively send a Return?) + // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. + const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl); + if (io.InputQueueCharacters.Size > 0) + { + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) + for (int n = 0; n < io.InputQueueCharacters.Size; n++) + { + // Insert character if they pass filtering + unsigned int c = (unsigned int)io.InputQueueCharacters[n]; + if (c == '\t') // Skip Tab, see above. + continue; + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + state->OnCharPressed(c); + } + + // Consume characters + io.InputQueueCharacters.resize(0); + } + } + + // Process other shortcuts/key-presses + bool revert_edit = false; + if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) + { + IM_ASSERT(state != NULL); + + const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); + state->Stb->row_count_per_page = row_count_per_page; + + const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl + const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + + // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText) + // Otherwise we could simply assume that we own the keys as we are active. + const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; + const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly; + const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable; + const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id))) && !is_readonly && is_undoable; + const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id); + + // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); + const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); + const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); + + // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. + // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line. + if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) + { + if (!state->HasSelection()) + { + // OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace) + if (is_wordmove_key_down) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } + state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); + } + else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) + { + if (!state->HasSelection()) + { + if (is_wordmove_key_down) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); + else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper) + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); + } + state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + } + else if (is_enter_pressed || is_gamepad_validate) + { + // Determine if we turn Enter into a \n character + bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; + if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + { + validated = true; + if (io.ConfigInputTextEnterKeepActive && !is_multiline) + state->SelectAll(); // No need to scroll + else + clear_active_id = true; + } + else if (!is_readonly) + { + unsigned int c = '\n'; // Insert new line + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) + state->OnCharPressed(c); + } + } + else if (is_cancel) + { + if (flags & ImGuiInputTextFlags_EscapeClearsAll) + { + if (buf[0] != 0) + { + revert_edit = true; + } + else + { + render_cursor = render_selection = false; + clear_active_id = true; + } + } + else + { + clear_active_id = revert_edit = true; + render_cursor = render_selection = false; + } + } + else if (is_undo || is_redo) + { + state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); + state->ClearSelection(); + } + else if (is_select_all) + { + state->SelectAll(); + state->CursorFollow = true; + } + else if (is_cut || is_copy) + { + // Cut, Copy + if (g.PlatformIO.Platform_SetClipboardTextFn != NULL) + { + const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0; + const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen; + + char backup = state->TextA.Data[ie]; + state->TextA.Data[ie] = 0; // A bit of a hack since SetClipboardText only takes null terminated strings + SetClipboardText(state->TextA.Data + ib); + state->TextA.Data[ie] = backup; + } + if (is_cut) + { + if (!state->HasSelection()) + state->SelectAll(); + state->CursorFollow = true; + stb_textedit_cut(state, state->Stb); + } + } + else if (is_paste) + { + if (const char* clipboard = GetClipboardText()) + { + // Filter pasted buffer + const int clipboard_len = (int)strlen(clipboard); + char* clipboard_filtered = (char*)IM_ALLOC(clipboard_len + 1); + int clipboard_filtered_len = 0; + for (const char* s = clipboard; *s != 0; ) + { + unsigned int c; + int len = ImTextCharFromUtf8(&c, s, NULL); + s += len; + if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) + continue; + memcpy(clipboard_filtered + clipboard_filtered_len, s - len, len); + clipboard_filtered_len += len; + } + clipboard_filtered[clipboard_filtered_len] = 0; + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + { + stb_textedit_paste(state, state->Stb, clipboard_filtered, clipboard_filtered_len); + state->CursorFollow = true; + } + MemFree(clipboard_filtered); + } + } + + // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame. + render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + } + + // Process callbacks and apply result back to user's buffer. + const char* apply_new_text = NULL; + int apply_new_text_length = 0; + if (g.ActiveId == id) + { + IM_ASSERT(state != NULL); + if (revert_edit && !is_readonly) + { + if (flags & ImGuiInputTextFlags_EscapeClearsAll) + { + // Clear input + IM_ASSERT(buf[0] != 0); + apply_new_text = ""; + apply_new_text_length = 0; + value_changed = true; + IMSTB_TEXTEDIT_CHARTYPE empty_string; + stb_textedit_replace(state, state->Stb, &empty_string, 0); + } + else if (strcmp(buf, state->TextToRevertTo.Data) != 0) + { + apply_new_text = state->TextToRevertTo.Data; + apply_new_text_length = state->TextToRevertTo.Size - 1; + + // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. + // Push records into the undo stack so we can CTRL+Z the revert operation itself + value_changed = true; + stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1); + } + } + + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer + // before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. + // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage + // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object + // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). + const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); + if (apply_edit_back_to_user_buffer) + { + // Apply new value immediately - copy modified buffer back + // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer + // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. + // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. + + // User callback + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0) + { + IM_ASSERT(callback != NULL); + + // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; + ImGuiKey event_key = ImGuiKey_None; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; + event_key = ImGuiKey_Tab; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_UpArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_DownArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited) + { + event_flag = ImGuiInputTextFlags_CallbackEdit; + } + else if (flags & ImGuiInputTextFlags_CallbackAlways) + { + event_flag = ImGuiInputTextFlags_CallbackAlways; + } + + if (event_flag) + { + ImGuiInputTextCallbackData callback_data; + callback_data.Ctx = &g; + callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = callback_user_data; + + // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 + state->CallbackTextBackup.resize(state->TextLen + 1); + memcpy(state->CallbackTextBackup.Data, state->TextA.Data, state->TextLen + 1); + + char* callback_buf = is_readonly ? buf : state->TextA.Data; + callback_data.EventKey = event_key; + callback_data.Buf = callback_buf; + callback_data.BufTextLen = state->TextLen; + callback_data.BufSize = state->BufCapacity; + callback_data.BufDirty = false; + + const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor; + const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start; + const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end; + + // Call user code + callback(&callback_data); + + // Read back what user may have modified + callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == state->BufCapacity); + IM_ASSERT(callback_data.Flags == flags); + const bool buf_dirty = callback_data.BufDirty; + if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty) { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start || buf_dirty) { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; } + if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; } + if (buf_dirty) + { + // Callback may update buffer and thus set buf_dirty even in read-only mode. + IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? + state->TextLen = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->CursorAnimReset(); + } + } + } + + // Will copy result string if modified + if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) + { + apply_new_text = state->TextA.Data; + apply_new_text_length = state->TextLen; + value_changed = true; + } + } + } + + // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details) + if (g.InputTextDeactivatedState.ID == id) + { + if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) + { + apply_new_text = g.InputTextDeactivatedState.TextA.Data; + apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; + value_changed = true; + //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); + } + g.InputTextDeactivatedState.ID = 0; + } + + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) + if (apply_new_text != NULL) + { + //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size + //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used + //// without any storage on user's side. + IM_ASSERT(apply_new_text_length >= 0); + if (is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.Ctx = &g; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); + } + //IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + } + + // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) + // Otherwise request text input ahead for next frame. + if (g.ActiveId == id && clear_active_id) + ClearActiveID(); + else if (g.ActiveId == id) + g.WantTextInputNextFrame = 1; + + // Render frame + if (!is_multiline) + { + RenderNavCursor(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + } + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.0f, 0.0f); + + // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line + // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. + // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. + const int buf_display_max_length = 2 * 1024 * 1024; + const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 + const char* buf_display_end = NULL; // We have specialized paths below for setting the length + if (is_displaying_hint) + { + buf_display = hint; + buf_display_end = hint + strlen(hint); + } + + // Render text. We currently only render selection when the widget is active or while scrolling. + // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. + if (render_cursor || render_selection) + { + IM_ASSERT(state != NULL); + if (!is_displaying_hint) + buf_display_end = buf_display + state->TextLen; + + // Render text (with cursor and selection) + // This is going to be messy. We need to: + // - Display the text (this alone can be more easily clipped) + // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) + // - Measure text height (for scrollbar) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. + const char* text_begin = state->TextA.Data; + const char* text_end = text_begin + state->TextLen; + ImVec2 cursor_offset, select_start_offset; + + { + // Find lines numbers straddling cursor and selection min position + int cursor_line_no = render_cursor ? -1 : -1000; + int selmin_line_no = render_selection ? -1 : -1000; + const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL; + const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL; + + // Count lines and find line number for cursor and selection ends + int line_count = 1; + if (is_multiline) + { + for (const char* s = text_begin; (s = (const char*)memchr(s, '\n', (size_t)(text_end - s))) != NULL; s++) + { + if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; } + if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; } + line_count++; + } + } + if (cursor_line_no == -1) + cursor_line_no = line_count; + if (selmin_line_no == -1) + selmin_line_no = line_count; + + // Calculate 2d position by finding the beginning of the line and measuring distance + cursor_offset.x = InputTextCalcTextSize(&g, ImStrbol(cursor_ptr, text_begin), cursor_ptr).x; + cursor_offset.y = cursor_line_no * g.FontSize; + if (selmin_line_no >= 0) + { + select_start_offset.x = InputTextCalcTextSize(&g, ImStrbol(selmin_ptr, text_begin), selmin_ptr).x; + select_start_offset.y = selmin_line_no * g.FontSize; + } + + // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) + if (is_multiline) + text_size = ImVec2(inner_size.x, line_count * g.FontSize); + } + + // Scroll + if (render_cursor && state->CursorFollow) + { + // Horizontal scroll in chunks of quarter width + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + { + const float scroll_increment_x = inner_size.x * 0.25f; + const float visible_width = inner_size.x - style.FramePadding.x; + if (cursor_offset.x < state->Scroll.x) + state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); + else if (cursor_offset.x - visible_width >= state->Scroll.x) + state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); + } + else + { + state->Scroll.y = 0.0f; + } + + // Vertical scroll + if (is_multiline) + { + // Test if cursor is vertically visible + if (cursor_offset.y - g.FontSize < scroll_y) + scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) + scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; + const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); + scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); + draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag + draw_window->Scroll.y = scroll_y; + } + + state->CursorFollow = false; + } + + // Draw selection + const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f); + if (render_selection) + { + const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end); + const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end); + + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. + float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; + for (const char* p = text_selected_begin; p < text_selected_end; ) + { + if (rect_pos.y > clip_rect.w + g.FontSize) + break; + if (rect_pos.y < clip_rect.y) + { + p = (const char*)memchr((void*)p, '\n', text_selected_end - p); + p = p ? p + 1 : text_selected_end; + } + else + { + ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true); + if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines + ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); + rect.ClipWith(clip_rect); + if (rect.Overlaps(clip_rect)) + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + rect_pos.x = draw_pos.x - draw_scroll.x; + } + rect_pos.y += g.FontSize; + } + } + + // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. + // FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it. + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + + // Draw blinking cursor + if (render_cursor) + { + state->CursorAnim += io.DeltaTime; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (!is_readonly) + { + g.PlatformImeData.WantVisible = true; + g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + g.PlatformImeData.InputLineHeight = g.FontSize; + } + } + } + else + { + // Render text only (no selection, no cursor) + if (is_multiline) + text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width + else if (!is_displaying_hint && g.ActiveId == id) + buf_display_end = buf_display + state->TextLen; + else if (!is_displaying_hint) + buf_display_end = buf_display + strlen(buf_display); + + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + } + + if (is_password && !is_displaying_hint) + PopFont(); + + if (is_multiline) + { + // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)... + Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); + g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + EndChild(); + item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); + + // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... + // FIXME: This quite messy/tricky, should attempt to get rid of the child window. + EndGroup(); + if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y)) + { + g.LastItemData.ID = id; + g.LastItemData.ItemFlags = item_data_backup.ItemFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; + } + } + + // Log as text + if (g.LogEnabled && (!is_password || is_displaying_hint)) + { + LogSetNextTextDecoration("{", "}"); + LogRenderedText(&draw_pos, buf_display, buf_display_end); + } + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if (value_changed) + MarkItemEdited(id); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); + if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) + return validated; + else + return value_changed; +} + +void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + ImGuiContext& g = *GImGui; + ImStb::STB_TexteditState* stb_state = state->Stb; + ImStb::StbUndoState* undo_state = &stb_state->undostate; + Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); + DebugLocateItemOnHover(state->ID); + Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end); + Text("BufCapacityA: %d", state->BufCapacity); + Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity); + Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); + Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); + if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state + { + PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++) + { + ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n]; + const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' '; + if (undo_rec_type == ' ') + BeginDisabled(); + const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0; + const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage; + Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"", + undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str); + if (undo_rec_type == ' ') + EndDisabled(); + } + PopStyleVar(); + } + EndChild(); +#else + IM_UNUSED(state); +#endif +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +//------------------------------------------------------------------------- +// - ColorEdit3() +// - ColorEdit4() +// - ColorPicker3() +// - RenderColorRectWithAlphaCheckerboard() [Internal] +// - ColorPicker4() +// - ColorButton() +// - SetColorEditOptions() +// - ColorTooltip() [Internal] +// - ColorEditOptionsPopup() [Internal] +// - ColorPickerOptionsPopup() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); +} + +static void ColorEditRestoreH(const float* col, float* H) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ColorEditCurrentID != 0); + if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + *H = g.ColorEditSavedHue; +} + +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ColorEditCurrentID != 0); + if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1)) + *H = g.ColorEditSavedHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditSavedSat; +} + +// Edit colors components (each component in 0.0f..1.0f range). +// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float square_sz = GetFrameHeight(); + const char* label_display_end = FindRenderedTextEnd(label); + float w_full = CalcItemWidth(); + g.NextItemData.ClearFlags(); + + BeginGroup(); + PushID(label); + const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); + if (set_current_color_edit_id) + g.ColorEditCurrentID = window->IDStack.back(); + + // If we're not showing any slider there's no point in doing any HSV conversions + const ImGuiColorEditFlags flags_untouched = flags; + if (flags & ImGuiColorEditFlags_NoInputs) + flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; + + // Context menu: display and modify options (before defaults are applied) + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorEditOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags_DisplayMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); + if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected + + const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; + const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; + const int components = alpha ? 4 : 3; + const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); + const float w_inputs = ImMax(w_full - w_button, 1.0f); + w_full = w_inputs + w_button; + + // Convert to the formats we need + float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; + if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) + { + // Hue is lost when converting from grayscale rgb (saturation=0). Restore it. + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); + } + int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; + + bool value_changed = false; + bool value_changed_as_float = false; + + const ImVec2 pos = window->DC.CursorPos; + const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f; + window->DC.CursorPos.x = pos.x + inputs_offset_x; + + if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB/HSV 0..255 Sliders + const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); + + const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + static const char* fmt_table_int[3][4] = + { + { "%3d", "%3d", "%3d", "%3d" }, // Short display + { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA + { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA + }; + static const char* fmt_table_float[3][4] = + { + { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display + { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA + { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA + }; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + + float prev_split = 0.0f; + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + float next_split = IM_TRUNC(w_items * (n + 1) / components); + SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); + prev_split = next_split; + + // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. + if (flags & ImGuiColorEditFlags_Float) + { + value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed_as_float |= value_changed; + } + else + { + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + } + } + else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB Hexadecimal Input + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); + SetNextItemWidth(w_inputs); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase)) + { + value_changed = true; + char* p = buf; + while (*p == '#' || ImCharIsBlankA(*p)) + p++; + i[0] = i[1] = i[2] = 0; + i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) + int r; + if (alpha) + r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + } + + ImGuiWindow* picker_active_window = NULL; + if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) + { + const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x; + window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y); + + const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); + if (ColorButton("##ColorButton", col_v4, flags)) + { + if (!(flags & ImGuiColorEditFlags_NoPicker)) + { + // Store current color and open a picker + g.ColorPickerRef = col_v4; + OpenPopup("picker"); + SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + + if (BeginPopup("picker")) + { + if (g.CurrentWindow->BeginCount == 1) + { + picker_active_window = g.CurrentWindow; + if (label != label_display_end) + { + TextEx(label, label_display_end); + Spacing(); + } + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? + value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); + } + EndPopup(); + } + } + + if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) + { + // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left), + // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this. + SameLine(0.0f, style.ItemInnerSpacing.x); + window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x); + TextEx(label, label_display_end); + } + + // Convert back + if (value_changed && picker_active_window == NULL) + { + if (!value_changed_as_float) + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) + { + g.ColorEditSavedHue = f[0]; + g.ColorEditSavedSat = f[1]; + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + g.ColorEditSavedID = g.ColorEditCurrentID; + g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); + } + if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + + if (set_current_color_edit_id) + g.ColorEditCurrentID = 0; + PopID(); + EndGroup(); + + // Drag and Drop Target + // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + { + bool accepted_drag_drop = false; + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086 + value_changed = accepted_drag_drop = true; + } + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * components); + value_changed = accepted_drag_drop = true; + } + + // Drag-drop payloads are always RGB + if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); + EndDragDropTarget(); + } + + // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). + if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) + g.LastItemData.ID = g.ActiveId; + + if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId + MarkItemEdited(g.LastItemData.ID); + + return value_changed; +} + +bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + float col4[4] = { col[0], col[1], col[2], 1.0f }; + if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) + return false; + col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; + return true; +} + +// Helper for ColorPicker4() +static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha) +{ + ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8)); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32(255,255,255,alpha8)); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32(0,0,0,alpha8)); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32(255,255,255,alpha8)); +} + +// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) +// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) +// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0) +bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImDrawList* draw_list = window->DrawList; + ImGuiStyle& style = g.Style; + ImGuiIO& io = g.IO; + + const float width = CalcItemWidth(); + const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; + g.NextItemData.ClearFlags(); + + PushID(label); + const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0); + if (set_current_color_edit_id) + g.ColorEditCurrentID = window->IDStack.back(); + BeginGroup(); + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + flags |= ImGuiColorEditFlags_NoSmallPreview; + + // Context menu: display and store options. + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorPickerOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_NoOptions)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); + + // Setup + int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; + bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); + ImVec2 picker_pos = window->DC.CursorPos; + float square_sz = GetFrameHeight(); + float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars + float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box + float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; + float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; + float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f); + + float backup_initial_col[4]; + memcpy(backup_initial_col, col, components * sizeof(float)); + + float wheel_thickness = sv_picker_size * 0.08f; + float wheel_r_outer = sv_picker_size * 0.50f; + float wheel_r_inner = wheel_r_outer - wheel_thickness; + ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); + + // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. + float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); + ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. + ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. + ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. + + float H = col[0], S = col[1], V = col[2]; + float R = col[0], G = col[1], B = col[2]; + if (flags & ImGuiColorEditFlags_InputRGB) + { + // Hue is lost when converting from grayscale rgb (saturation=0). Restore it. + ColorConvertRGBtoHSV(R, G, B, H, S, V); + ColorEditRestoreHS(col, &H, &S, &V); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + ColorConvertHSVtoRGB(H, S, V, R, G, B); + } + + bool value_changed = false, value_changed_h = false, value_changed_sv = false; + + PushItemFlag(ImGuiItemFlags_NoNav, true); + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Hue wheel + SV triangle logic + InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); + if (IsItemActive() && !is_readonly) + { + ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; + ImVec2 current_off = g.IO.MousePos - wheel_center; + float initial_dist2 = ImLengthSqr(initial_off); + if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1)) + { + // Interactive with Hue wheel + H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f; + if (H < 0.0f) + H += 1.0f; + value_changed = value_changed_h = true; + } + float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); + if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) + { + // Interacting with SV triangle + ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); + if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) + current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); + float uu, vv, ww; + ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); + V = ImClamp(1.0f - vv, 0.0001f, 1.0f); + S = ImClamp(uu / V, 0.0001f, 1.0f); + value_changed = value_changed_sv = true; + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // SV rectangle logic + InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); + if (IsItemActive() && !is_readonly) + { + S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); + V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + value_changed = value_changed_sv = true; + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + + // Hue bar logic + SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); + InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive() && !is_readonly) + { + H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + value_changed = value_changed_h = true; + } + } + + // Alpha bar logic + if (alpha_bar) + { + SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); + InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + value_changed = true; + } + } + PopItemFlag(); // ImGuiItemFlags_NoNav + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + SameLine(0, style.ItemInnerSpacing.x); + BeginGroup(); + } + + if (!(flags & ImGuiColorEditFlags_NoLabel)) + { + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + if ((flags & ImGuiColorEditFlags_NoSidePreview)) + SameLine(0, style.ItemInnerSpacing.x); + TextEx(label, label_display_end); + } + } + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if ((flags & ImGuiColorEditFlags_NoLabel)) + Text("Current"); + + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); + if (ref_col != NULL) + { + Text("Original"); + ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); + if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) + { + memcpy(col, ref_col, components * sizeof(float)); + value_changed = true; + } + } + PopItemFlag(); + EndGroup(); + } + + // Convert back color to RGB + if (value_changed_h || value_changed_sv) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); + g.ColorEditSavedHue = H; + g.ColorEditSavedSat = S; + g.ColorEditSavedID = g.ColorEditCurrentID; + g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + col[0] = H; + col[1] = S; + col[2] = V; + } + } + + // R,G,B and H,S,V slider color editor + bool value_changed_fix_hue_wrap = false; + if ((flags & ImGuiColorEditFlags_NoInputs) == 0) + { + PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) + if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) + { + // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. + // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) + value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); + value_changed = true; + } + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); + PopItemWidth(); + } + + // Try to cancel hue wrap (after ColorEdit4 call), if any + if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) + { + float new_H, new_S, new_V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); + if (new_H <= 0 && H > 0) + { + if (new_V <= 0 && V != new_V) + ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); + else if (new_S <= 0) + ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); + } + } + + if (value_changed) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + R = col[0]; + G = col[1]; + B = col[2]; + ColorConvertRGBtoHSV(R, G, B, H, S, V); + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + H = col[0]; + S = col[1]; + V = col[2]; + ColorConvertHSVtoRGB(H, S, V, R, G, B); + } + } + + const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha); + const ImU32 col_black = IM_COL32(0,0,0,style_alpha8); + const ImU32 col_white = IM_COL32(255,255,255,style_alpha8); + const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8); + const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) }; + + ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); + ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); + ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!! + + ImVec2 sv_cursor_pos; + + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Render Hue Wheel + const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). + const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); + for (int n = 0; n < 6; n++) + { + const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; + const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; + const int vert_start_idx = draw_list->VtxBuffer.Size; + draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); + draw_list->PathStroke(col_white, 0, wheel_thickness); + const int vert_end_idx = draw_list->VtxBuffer.Size; + + // Paint colors over existing vertices + ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); + ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); + ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]); + } + + // Render Cursor + preview on Hue Wheel + float cos_hue_angle = ImCos(H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(H * 2.0f * IM_PI); + ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); + float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; + int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others. + draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); + + // Render SV triangle (rotated according to hue) + ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); + ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); + ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); + ImVec2 uv_white = GetFontTexUvWhitePixel(); + draw_list->PrimReserve(3, 3); + draw_list->PrimVtx(tra, uv_white, hue_color32); + draw_list->PrimVtx(trb, uv_white, col_black); + draw_list->PrimVtx(trc, uv_white, col_white); + draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); + sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // Render SV Square + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white); + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black); + RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f); + sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S) * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much + sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); + + // Render Hue Bar + for (int i = 0; i < 6; ++i) + draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]); + float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size); + RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); + } + + // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) + float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f; + int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others. + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments); + + // Render alpha bar + if (alpha_bar) + { + float alpha = ImSaturate(col[3]); + ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); + RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); + draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK); + float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size); + RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha); + } + + EndGroup(); + + if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) + value_changed = false; + if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId + MarkItemEdited(g.LastItemData.ID); + + if (set_current_color_edit_id) + g.ColorEditCurrentID = 0; + PopID(); + + return value_changed; +} + +// A little color square. Return true when clicked. +// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. +// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. +bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(desc_id); + const float default_size = GetFrameHeight(); + const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + if (flags & ImGuiColorEditFlags_NoAlpha) + flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + + ImVec4 col_rgb = col; + if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); + + ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); + float grid_step = ImMin(size.x, size.y) / 2.99f; + float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); + ImRect bb_inner = bb; + float off = 0.0f; + if ((flags & ImGuiColorEditFlags_NoBorder) == 0) + { + off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. + bb_inner.Expand(off); + } + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) + { + float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); + RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); + } + else + { + // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; + if (col_source.w < 1.0f) + RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); + else + window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); + } + RenderNavCursor(bb, id); + if ((flags & ImGuiColorEditFlags_NoBorder) == 0) + { + if (g.Style.FrameBorderSize > 0.0f) + RenderFrameBorder(bb.Min, bb.Max, rounding); + else + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border + } + + // Drag and Drop Source + // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. + if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); + else + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); + ColorButton(desc_id, col, flags); + SameLine(); + TextEx("Color"); + EndDragDropSource(); + } + + // Tooltip + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + + return pressed; +} + +// Initialize/override default color options +void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; + if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; + if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; + if ((flags & ImGuiColorEditFlags_InputMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected + g.ColorEditOptions = flags; +} + +// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) + return; + const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; + if (text_end > text) + { + TextEx(text, text_end); + Separator(); + } + + ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); + ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + SameLine(); + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); + else + Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); + } + EndTooltip(); +} + +void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) +{ + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); + if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) + return; + + ImGuiContext& g = *GImGui; + PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); + ImGuiColorEditFlags opts = g.ColorEditOptions; + if (allow_opt_inputs) + { + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; + } + if (allow_opt_datatype) + { + if (allow_opt_inputs) Separator(); + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; + } + + if (allow_opt_inputs || allow_opt_datatype) + Separator(); + if (Button("Copy as..", ImVec2(-1, 0))) + OpenPopup("Copy"); + if (BeginPopup("Copy")) + { + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + char buf[64]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if (Selectable(buf)) + SetClipboardText(buf); + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); + if (Selectable(buf)) + SetClipboardText(buf); + if (!(flags & ImGuiColorEditFlags_NoAlpha)) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + } + EndPopup(); + } + + g.ColorEditOptions = opts; + PopItemFlag(); + EndPopup(); +} + +void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) +{ + bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); + bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); + if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) + return; + + ImGuiContext& g = *GImGui; + PushItemFlag(ImGuiItemFlags_NoMarkEdited, true); + if (allow_opt_picker) + { + ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function + PushItemWidth(picker_size.x); + for (int picker_type = 0; picker_type < 2; picker_type++) + { + // Draw small/thumbnail version of each picker type (over an invisible button for selection) + if (picker_type > 0) Separator(); + PushID(picker_type); + ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha); + if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; + ImVec2 backup_pos = GetCursorScreenPos(); + if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); + SetCursorScreenPos(backup_pos); + ImVec4 previewing_ref_col; + memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); + ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags); + PopID(); + } + PopItemWidth(); + } + if (allow_opt_alpha_bar) + { + if (allow_opt_picker) Separator(); + CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + } + PopItemFlag(); + EndPopup(); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +//------------------------------------------------------------------------- +// - TreeNode() +// - TreeNodeV() +// - TreeNodeEx() +// - TreeNodeExV() +// - TreeNodeBehavior() [Internal] +// - TreePush() +// - TreePop() +// - GetTreeNodeToLabelSpacing() +// - SetNextItemOpen() +// - CollapsingHeader() +//------------------------------------------------------------------------- + +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL); +} + +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +{ + return TreeNodeExV(str_id, 0, fmt, args); +} + +bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) +{ + return TreeNodeExV(ptr_id, 0, fmt, args); +} + +bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, flags, label, NULL); +} + +bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(str_id); + const char* label, *label_end; + ImFormatStringToTempBufferV(&label, &label_end, fmt, args); + return TreeNodeBehavior(id, flags, label, label_end); +} + +bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(ptr_id); + const char* label, *label_end; + ImFormatStringToTempBufferV(&label, &label_end, fmt, args); + return TreeNodeBehavior(id, flags, label, label_end); +} + +bool ImGui::TreeNodeGetOpen(ImGuiID storage_id) +{ + ImGuiContext& g = *GImGui; + ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; + return storage->GetInt(storage_id, 0) != 0; +} + +void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool open) +{ + ImGuiContext& g = *GImGui; + ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; + storage->SetInt(storage_id, open ? 1 : 0); +} + +bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags) +{ + if (flags & ImGuiTreeNodeFlags_Leaf) + return true; + + // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStorage* storage = window->DC.StateStorage; + + bool is_open; + if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen) + { + if (g.NextItemData.OpenCond & ImGuiCond_Always) + { + is_open = g.NextItemData.OpenVal; + TreeNodeSetOpen(storage_id, is_open); + } + else + { + // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(storage_id, -1); + if (stored_value == -1) + { + is_open = g.NextItemData.OpenVal; + TreeNodeSetOpen(storage_id, is_open); + } + else + { + is_open = stored_value != 0; + } + } + } + else + { + is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + } + + // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). + // NB- If we are above max depth we still allow manually opened nodes to be logged. + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) + is_open = true; + + return is_open; +} + +// Store ImGuiTreeNodeStackData for just submitted node. +// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. +static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1); + ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back(); + tree_node_data->ID = g.LastItemData.ID; + tree_node_data->TreeFlags = flags; + tree_node_data->ItemFlags = g.LastItemData.ItemFlags; + tree_node_data->NavRect = g.LastItemData.NavRect; + window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); +} + +// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop. +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; + const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); + + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + + const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing + const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow + + // We vertically grow up to current line height up the typical widget height. + const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); + const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); + ImRect frame_bb; + frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; + frame_bb.Min.y = window->DC.CursorPos.y; + frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanTextWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; + frame_bb.Max.y = window->DC.CursorPos.y + frame_height; + if (display_frame) + { + const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits + frame_bb.Min.x -= outer_extend; + frame_bb.Max.x += outer_extend; + } + + ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); + ItemSize(ImVec2(text_width, frame_height), padding.y); + + // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing + ImRect interact_bb = frame_bb; + if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) + interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); + + // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id; + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); + + bool is_visible; + if (span_all_columns) + { + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + is_visible = ItemAdd(interact_bb, id); + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } + else + { + is_visible = ItemAdd(interact_bb, id); + } + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; + + // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: + // Store data for the current depth to allow returning to this node from any child item. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. + bool store_tree_node_stack_data = false; + if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + { + if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive) + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + store_tree_node_stack_data = true; + } + + const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; + if (!is_visible) + { + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushOverrideID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + return is_open; + } + + if (span_all_columns) + { + TablePushBackgroundChannel(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } + + ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; + if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) + button_flags |= ImGuiButtonFlags_AllowOverlap; + if (!is_leaf) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + + // We allow clicking on the arrow section with keyboard modifiers held, in order to easily + // allow browsing a tree while preserving selection with code implementing multi-selection patterns. + // When clicking on the rest of the tree node we always disallow keyboard modifiers. + const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; + const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; + const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); + + const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default + flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow; + + // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. + // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support. + // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0) + // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0) + // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1) + // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1) + // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0) + // It is rather standard that arrow click react on Down rather than Up. + // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work. + if (is_mouse_x_over_arrow) + button_flags |= ImGuiButtonFlags_PressedOnClick; + else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + + bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; + const bool was_selected = selected; + + // Multi-selection support (header) + if (is_multi_select) + { + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); + if (is_mouse_x_over_arrow) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; + } + else + { + if (window != g.HoveredWindow || !is_mouse_x_over_arrow) + button_flags |= ImGuiButtonFlags_NoKeyModsAllowed; + } + + bool hovered, held; + bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + bool toggled = false; + if (!is_leaf) + { + if (pressed && g.DragDropHoldJustPressedId != id) + { + if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select)) + toggled = true; // Single click + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) + toggled = true; // Double click + } + else if (pressed && g.DragDropHoldJustPressedId == id) + { + IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); + if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. + toggled = true; + else + pressed = false; // Cancel press so it doesn't trigger selection. + } + + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) + { + toggled = true; + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavMoveRequestCancel(); + } + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + { + toggled = true; + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavMoveRequestCancel(); + } + + if (toggled) + { + is_open = !is_open; + window->DC.StateStorage->SetInt(storage_id, is_open); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; + } + } + + // Multi-selection support (footer) + if (is_multi_select) + { + bool pressed_copy = pressed && !toggled; + MultiSelectItemFooter(id, &selected, &pressed_copy); + if (pressed) + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); + } + + if (selected != was_selected) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + + // Render + { + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact; + if (is_multi_select) + nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle + if (display_frame) + { + // Framed type + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); + RenderNavCursor(frame_bb, id, nav_render_cursor_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); + else // Leaf without bullet, left-adjusted text + text_pos.x -= text_offset_x - padding.x; + if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) + frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) + LogSetNextTextDecoration("###", "###"); + } + else + { + // Unframed typed for tree nodes + if (hovered || selected) + { + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + } + RenderNavCursor(frame_bb, id, nav_render_cursor_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); + if (g.LogEnabled) + LogSetNextTextDecoration(">", NULL); + } + + if (span_all_columns) + TablePopBackgroundChannel(); + + // Label + if (display_frame) + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + else + RenderText(text_pos, label, label_end, false); + } + + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + return is_open; +} + +void ImGui::TreePush(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(str_id); +} + +void ImGui::TreePush(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(ptr_id); +} + +void ImGui::TreePushOverrideID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + Indent(); + window->DC.TreeDepth++; + PushOverrideID(id); +} + +void ImGui::TreePop() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + Unindent(); + + window->DC.TreeDepth--; + ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); + + if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request + { + ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back(); + IM_ASSERT(data->ID == window->IDStack.back()); + if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) + { + // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) + if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data); + } + g.TreeNodeStack.pop_back(); + window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask; + } + + IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. + PopID(); +} + +// Horizontal distance preceding label when using TreeNode() or Bullet() +float ImGui::GetTreeNodeToLabelSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + (g.Style.FramePadding.x * 2.0f); +} + +// Set next TreeNode/CollapsingHeader open state. +void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen; + g.NextItemData.OpenVal = is_open; + g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always); +} + +// Set next TreeNode/CollapsingHeader storage id. +void ImGui::SetNextItemStorageID(ImGuiID storage_id) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID; + g.NextItemData.StorageId = storage_id; +} + +// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). +// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + ImGuiID id = window->GetID(label); + return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label); +} + +// p_visible == NULL : regular collapsing header +// p_visible != NULL && *p_visible == true : show a small close button on the corner of the header, clicking the button will set *p_visible = false +// p_visible != NULL && *p_visible == false : do not show the header at all +// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen. +bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (p_visible && !*p_visible) + return false; + + ImGuiID id = window->GetID(label); + flags |= ImGuiTreeNodeFlags_CollapsingHeader; + if (p_visible) + flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; + bool is_open = TreeNodeBehavior(id, flags, label); + if (p_visible != NULL) + { + // Create a small overlapping close button + // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. + ImGuiContext& g = *GImGui; + ImGuiLastItemData last_item_backup = g.LastItemData; + float button_size = g.FontSize; + float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size); + float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y; + ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); + if (CloseButton(close_button_id, ImVec2(button_x, button_y))) + *p_visible = false; + g.LastItemData = last_item_backup; + } + + return is_open; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Selectable +//------------------------------------------------------------------------- +// - Selectable() +//------------------------------------------------------------------------- + +// Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. +// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags. +// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. +bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrLineTextBaseOffset; + ItemSize(size, 0.0f); + + // Fill horizontal space + // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. + const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; + const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; + const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) + size.x = ImMax(label_size.x, max_x - min_x); + + // Text stays at the submission position, but bounding box may be extended on both sides + const ImVec2 text_min = pos; + const ImVec2 text_max(min_x + size.x, pos.y + size.y); + + // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. + // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. + ImRect bb(min_x, pos.y, text_max.x, text_max.y); + if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) + { + const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = IM_TRUNC(spacing_x * 0.50f); + const float spacing_U = IM_TRUNC(spacing_y * 0.50f); + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); + } + //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } + + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None; + bool is_visible; + if (span_all_columns) + { + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + is_visible = ItemAdd(bb, id, NULL, extra_item_flags); + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } + else + { + is_visible = ItemAdd(bb, id, NULL, extra_item_flags); + } + + const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (!is_visible) + if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd) + return false; + + const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (disabled_item && !disabled_global) // Only testing this as an optimization + BeginDisabled(); + + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, + // which would be advantageous since most selectable are not selected. + if (span_all_columns) + { + if (g.CurrentTable) + TablePushBackgroundChannel(); + else if (window->DC.CurrentColumns) + PushColumnsBackground(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } + + // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } + if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; } + if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } + if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } + if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } + if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } + + // Multi-selection support (header) + const bool was_selected = selected; + if (is_multi_select) + { + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); + } + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + + // Multi-selection support (footer) + if (is_multi_select) + { + MultiSelectItemFooter(id, &selected, &pressed); + } + else + { + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) + if (g.NavJustMovedToId == id) + selected = pressed = true; + } + + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad + if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) + { + if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) + { + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect) + if (g.IO.ConfigNavCursorVisibleAuto) + g.NavCursorVisible = false; + } + } + if (pressed) + MarkItemEdited(id); + + if (selected != was_selected) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + + // Render + if (is_visible) + { + const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight); + if (highlighted || selected) + { + // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106) + ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + } + if (g.NavId == id) + { + ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding; + if (is_multi_select) + nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle + RenderNavCursor(bb, id, nav_render_cursor_flags); + } + } + + if (span_all_columns) + { + if (g.CurrentTable) + TablePopBackgroundChannel(); + else if (window->DC.CurrentColumns) + PopColumnsBackground(); + } + + if (is_visible) + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) + CloseCurrentPopup(); + + if (disabled_item && !disabled_global) + EndDisabled(); + + // Selectable() always returns a pressed state! + // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve + // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect(). + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; //-V1020 +} + +bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + if (Selectable(label, *p_selected, flags, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} + + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Typing-Select support +//------------------------------------------------------------------------- + +// [Experimental] Currently not exposed in public API. +// Consume character inputs and return search request, if any. +// This would typically only be called on the focused window or location you want to grab inputs for, e.g. +// if (ImGui::IsWindowFocused(...)) +// if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest()) +// focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1); +// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer). +ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiTypingSelectState* data = &g.TypingSelectState; + ImGuiTypingSelectRequest* out_request = &data->Request; + + // Clear buffer + const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config. + const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times + if (data->SearchBuffer[0] != 0) + { + bool clear_buffer = false; + clear_buffer |= (g.NavFocusScopeId != data->FocusScope); + clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time); + clear_buffer |= g.NavAnyRequest; + clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere + clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter); + clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0; + //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); } + if (clear_buffer) + data->Clear(); + } + + // Append to buffer + const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; + int buffer_len = (int)strlen(data->SearchBuffer); + bool select_request = false; + for (ImWchar w : g.IO.InputQueueCharacters) + { + const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); + if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks + continue; + char w_buf[5]; + ImTextCharToUtf8(w_buf, (unsigned int)w); + if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0) + { + select_request = true; // Same character: don't need to append to buffer. + continue; + } + if (data->SingleCharModeLock) + { + data->Clear(); // Different character: clear + buffer_len = 0; + } + memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append + buffer_len += w_len; + select_request = true; + } + g.IO.InputQueueCharacters.resize(0); + + // Handle backspace + if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat)) + { + char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); + *p = 0; + buffer_len = (int)(p - data->SearchBuffer); + } + + // Return request if any + if (buffer_len == 0) + return NULL; + if (select_request) + { + data->FocusScope = g.NavFocusScopeId; + data->LastRequestFrame = g.FrameCount; + data->LastRequestTime = (float)g.Time; + } + out_request->Flags = flags; + out_request->SearchBufferLen = buffer_len; + out_request->SearchBuffer = data->SearchBuffer; + out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount); + out_request->SingleCharMode = false; + out_request->SingleCharSize = 0; + + // Calculate if buffer contains the same character repeated. + // - This can be used to implement a special search mode on first character. + // - Performed on UTF-8 codepoint for correctness. + // - SingleCharMode is always set for first input character, because it usually leads to a "next". + if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode) + { + const char* buf_begin = out_request->SearchBuffer; + const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen; + const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end); + const char* p = buf_begin + c0_len; + for (; p < buf_end; p += c0_len) + if (memcmp(buf_begin, p, (size_t)c0_len) != 0) + break; + const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0; + out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock); + out_request->SingleCharSize = (ImS8)c0_len; + data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode. + } + + return out_request; +} + +static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) +{ + int match_len = 0; + while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++)) + match_len++; + return match_len; +} + +// Default handler for finding a result for typing-select. You may implement your own. +// You might want to display a tooltip to visualize the current request SearchBuffer +// When SingleCharMode is set: +// - it is better to NOT display a tooltip of other on-screen display indicator. +// - the index of the currently focused item is required. +// if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. +int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) +{ + if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot. + return -1; + int idx = -1; + if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode)) + idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx); + else + idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data); + if (idx != -1) + SetNavCursorVisibleAfterMove(); + return idx; +} + +// Special handling when a single character is repeated: perform search on a single letter and goes to next. +int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) +{ + // FIXME: Assume selection user data is index. Would be extremely practical. + //if (nav_item_idx == -1) + // nav_item_idx = (int)g.NavLastValidSelectionUserData; + + int first_match_idx = -1; + bool return_next_match = false; + for (int idx = 0; idx < items_count; idx++) + { + const char* item_name = get_item_name_func(user_data, idx); + if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize) + continue; + if (return_next_match) // Return next matching item after current item. + return idx; + if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value. + return idx; + if (first_match_idx == -1) // Record first match for wrapping. + first_match_idx = idx; + if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match. + return_next_match = true; + } + return first_match_idx; // First result +} + +int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data) +{ + int longest_match_idx = -1; + int longest_match_len = 0; + for (int idx = 0; idx < items_count; idx++) + { + const char* item_name = get_item_name_func(user_data, idx); + const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name); + if (match_len <= longest_match_len) + continue; + longest_match_idx = idx; + longest_match_len = match_len; + if (match_len == req->SearchBufferLen) + break; + } + return longest_match_idx; +} + +void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + Text("SearchBuffer = \"%s\"", data->SearchBuffer); + Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock); + Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame); +#else + IM_UNUSED(data); +#endif +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Box-Select support +// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet. +//------------------------------------------------------------------------- +// Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step() +//------------------------------------------------------------------------- +// - BoxSelectPreStartDrag() [Internal] +// - BoxSelectActivateDrag() [Internal] +// - BoxSelectDeactivateDrag() [Internal] +// - BoxSelectScrollWithMouseDrag() [Internal] +// - BeginBoxSelect() [Internal] +// - EndBoxSelect() [Internal] +//------------------------------------------------------------------------- + +// Call on the initial click. +static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item) +{ + ImGuiContext& g = *GImGui; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + bs->ID = id; + bs->IsStarting = true; // Consider starting box-select. + bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid); + bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid; + bs->KeyMods = g.IO.KeyMods; + bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos); + bs->ScrollAccum = ImVec2(0.0f, 0.0f); +} + +static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID); + bs->IsActive = true; + bs->Window = window; + bs->IsStarting = false; + ImGui::SetActiveID(bs->ID, window); + ImGui::SetActiveIdUsingAllKeyboardKeys(); + if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + bs->RequestClear = true; +} + +static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs) +{ + ImGuiContext& g = *GImGui; + bs->IsActive = bs->IsStarting = false; + if (g.ActiveId == bs->ID) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID); + ImGui::ClearActiveID(); + } + bs->ID = 0; +} + +static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(bs->Window == window); + for (int n = 0; n < 2; n++) // each axis + { + const float mouse_pos = g.IO.MousePos[n]; + const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f; + const float scroll_curr = window->Scroll[n]; + if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n])) + continue; + + const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance + const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime; + bs->ScrollAccum[n] += scroll_step; + + // Accumulate into a stored value so we can handle high-framerate + const float scroll_step_i = ImFloor(bs->ScrollAccum[n]); + if (scroll_step_i == 0.0f) + continue; + if (n == 0) + ImGui::SetScrollX(window, scroll_curr + scroll_step_i); + else + ImGui::SetScrollY(window, scroll_curr + scroll_step_i); + bs->ScrollAccum[n] -= scroll_step_i; + } +} + +bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + KeepAliveID(box_select_id); + if (bs->ID != box_select_id) + return false; + + // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry. + bs->UnclipMode = false; + bs->RequestClear = false; + if (bs->IsStarting && IsMouseDragPastThreshold(0)) + BoxSelectActivateDrag(bs, window); + else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false) + BoxSelectDeactivateDrag(bs); + if (!bs->IsActive) + return false; + + // Current frame absolute prev/current rectangles are used to toggle selection. + // They are derived from positions relative to scrolling space. + ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel); + ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already + ImVec2 curr_end_pos_abs = g.IO.MousePos; + if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow + curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max); + bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs); + bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs); + bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs); + + // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper) + // Storing an extra rect used by widgets supporting box-select. + if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d) + if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x) + { + bs->UnclipMode = true; + bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis. + bs->UnclipRect.Add(bs->BoxSelectRectCurr); + } + + //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); + //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f); + //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f); + return true; +} + +void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiBoxSelectState* bs = &g.BoxSelectState; + IM_ASSERT(bs->IsActive); + bs->UnclipMode = false; + + // Render selection rectangle + bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view + ImRect box_select_r = bs->BoxSelectRectCurr; + box_select_r.ClipWith(scope_rect); + window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling + window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling + + // Scroll + const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0; + if (enable_scroll) + { + ImRect scroll_r = scope_rect; + scroll_r.Expand(-g.FontSize); + //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255)); + if (!scroll_r.Contains(g.IO.MousePos)) + BoxSelectScrollWithMouseDrag(bs, window, scroll_r); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Multi-Select support +//------------------------------------------------------------------------- +// - DebugLogMultiSelectRequests() [Internal] +// - CalcScopeRect() [Internal] +// - BeginMultiSelect() +// - EndMultiSelect() +// - SetNextItemSelectionUserData() +// - MultiSelectItemHeader() [Internal] +// - MultiSelectItemFooter() [Internal] +// - DebugNodeMultiSelectState() [Internal] +//------------------------------------------------------------------------- + +static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) +{ + ImGuiContext& g = *GImGui; + IM_UNUSED(function); + for (const ImGuiSelectionRequest& req : io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); + if (req.Type == ImGuiSelectionRequestType_SetRange) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection); + } +} + +static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) + { + // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only + return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); + } + else + { + // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970) + ImRect scope_rect = window->InnerClipRect; + if (g.CurrentTable != NULL) + scope_rect = g.CurrentTable->HostClipRect; + + // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect? + scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max); + return scope_rect; + } +} + +// Return ImGuiMultiSelectIO structure. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). +// Passing 'selection_size' and 'items_count' parameters is currently optional. +// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0, +// allowing a first press to clear selection THEN the second press to leave child window and return to parent. +// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently). +// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1. +// - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically. +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size) + g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData()); + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1]; + IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. + g.CurrentMultiSelect = ms; + if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) + flags |= ImGuiMultiSelectFlags_ScopeWindow; + if (flags & ImGuiMultiSelectFlags_SingleSelect) + flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d); + if (flags & ImGuiMultiSelectFlags_BoxSelect2d) + flags &= ~ImGuiMultiSelectFlags_BoxSelect1d; + + // FIXME: BeginFocusScope() + const ImGuiID id = window->IDStack.back(); + ms->Clear(); + ms->FocusScopeId = id; + ms->Flags = flags; + ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); + ms->BackupCursorMaxPos = window->DC.CursorMaxPos; + ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; + PushFocusScope(ms->FocusScopeId); + if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items. + window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; + + // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. + ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods; + if (flags & ImGuiMultiSelectFlags_NoRangeSelect) + ms->KeyMods &= ~ImGuiMod_Shift; + + // Bind storage + ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id); + storage->ID = id; + storage->LastFrameActive = g.FrameCount; + storage->LastSelectionSize = selection_size; + storage->Window = window; + ms->Storage = storage; + + // Output to user + ms->IO.Requests.resize(0); + ms->IO.RangeSrcItem = storage->RangeSrcItem; + ms->IO.NavIdItem = storage->NavIdItem; + ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + ms->IO.ItemsCount = items_count; + + // Clear when using Navigation to move within the scope + // (we compare FocusScopeId so it possible to use multiple selections inside a same window) + bool request_clear = false; + bool request_select_all = false; + if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) + { + if (ms->KeyMods & ImGuiMod_Shift) + ms->IsKeyboardSetRange = true; + if (ms->IsKeyboardSetRange) + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear? + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) + request_clear = true; + } + else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId) + { + // Also clear on leaving scope (may be optional?) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0) + request_clear = true; + } + + // Box-select handling: update active state. + ImGuiBoxSelectState* bs = &g.BoxSelectState; + if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) + { + ms->BoxSelectId = GetID("##BoxSelect"); + if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags)) + request_clear |= bs->RequestClear; + } + + if (ms->IsFocused) + { + // Shortcut: Clear selection (Escape) + // - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. + // - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock. + if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + { + if (selection_size != 0 || bs->IsActive) + if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0)) + { + request_clear = true; + if (bs->IsActive) + BoxSelectDeactivateDrag(bs); + } + } + + // Shortcut: Select all (CTRL+A) + if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll)) + if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A)) + request_select_all = true; + } + + if (request_clear || request_select_all) + { + MultiSelectAddSetAll(ms, request_select_all); + if (!request_select_all) + storage->LastSelectionSize = 0; + } + ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1; + ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid; + + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); + + return &ms->IO; +} + +// Return updated ImGuiMultiSelectIO structure. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). +ImGuiMultiSelectIO* ImGui::EndMultiSelect() +{ + ImGuiContext& g = *GImGui; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + ImGuiMultiSelectState* storage = ms->Storage; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!"); + IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); + IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); + + ImRect scope_rect = CalcScopeRect(ms, window); + if (ms->IsFocused) + { + // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. + if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. + storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; + } + if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid) + { + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); + storage->NavIdItem = ImGuiSelectionUserData_Invalid; + storage->NavIdSelected = -1; + } + + if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId)) + EndBoxSelect(scope_rect, ms->Flags); + } + + if (ms->IsEndIO == false) + ms->IO.Requests.resize(0); + + // Clear selection when clicking void? + // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! + // The InnerRect test is necessary for non-child/decorated windows. + bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos); + if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)) + scope_hovered &= scope_rect.Contains(g.IO.MousePos); + if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0) + { + if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) + { + if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1) + { + BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid); + FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); + SetHoveredID(ms->BoxSelectId); + if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) + SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select. + } + } + + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) + if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) + MultiSelectAddSetAll(ms, false); + } + + // Courtesy nav wrapping helper flag + if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX) + { + IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope + ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX); + } + + // Unwind + window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); + PopFocusScope(); + + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); + + ms->FocusScopeId = 0; + ms->Flags = ImGuiMultiSelectFlags_None; + g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL; + + return &ms->IO; +} + +void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) +{ + // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! + // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. + ImGuiContext& g = *GImGui; + g.NextItemData.SelectionUserData = selection_user_data; + g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; + + if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) + { + // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; + if (ms->IO.RangeSrcItem == selection_user_data) + ms->RangeSrcPassedBy = true; + } + else + { + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + } +} + +// In charge of: +// - Applying SetAll for submitted items. +// - Applying SetRange for submitted items and record end points. +// - Altering button behavior flags to facilitate use with drag and drop. +void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + + bool selected = *p_selected; + if (ms->IsFocused) + { + ImGuiMultiSelectState* storage = ms->Storage; + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; + IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); + + // Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect(). + // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper. + // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect() + if (ms->LoopRequestSetAll != -1) + selected = (ms->LoopRequestSetAll == 1); + + // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection) + // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function) + if (ms->IsKeyboardSetRange) + { + IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0); + const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. + if (is_range_dst) + ms->RangeDstPassedBy = true; + if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected ? 1 : 0; + } + const bool is_range_src = storage->RangeSrcItem == item_data; + if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy) + { + // Apply range-select value to visible items + IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1); + selected = (storage->RangeSelected != 0); + } + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) + { + // Clear other items + selected = false; + } + } + *p_selected = selected; + } + + // Alter button behavior flags + // To handle drag and drop of multiple items we need to avoid clearing selection on click. + // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items. + if (p_button_flags != NULL) + { + ImGuiButtonFlags button_flags = *p_button_flags; + button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; + if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; + else + button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + *p_button_flags = button_flags; + } +} + +// In charge of: +// - Auto-select on navigation. +// - Box-select toggle handling. +// - Right-click handling. +// - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse. +// - Record current selection state for RangeSrc +// This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite. +void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + bool selected = *p_selected; + bool pressed = *p_pressed; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + ImGuiMultiSelectState* storage = ms->Storage; + if (pressed) + ms->IsFocused = true; + + bool hovered = false; + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) + hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (!ms->IsFocused && !hovered) + return; + + ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData; + + ImGuiMultiSelectFlags flags = ms->Flags; + const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0; + bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; + bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; + + bool apply_to_range_src = false; + + if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) + apply_to_range_src = true; + if (ms->IsEndIO == false) + { + ms->IO.Requests.resize(0); + ms->IsEndIO = true; + } + + // Auto-select as you navigate a list + if (g.NavJustMovedToId == id) + { + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + { + if (is_ctrl && is_shift) + pressed = true; + else if (!is_ctrl) + selected = pressed = true; + } + else + { + // With NoAutoSelect, using Shift+keyboard performs a write/copy + if (is_shift) + pressed = true; + else if (!is_ctrl) + apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this + } + } + + if (apply_to_range_src) + { + storage->RangeSrcItem = item_data; + storage->RangeSelected = selected; // Will be updated at the end of this function anyway. + } + + // Box-select toggle handling + if (ms->BoxSelectId != 0) + if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId)) + { + const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect); + const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect); + if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr)) + { + if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce) + { + pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway) + bs->IsStartedSetNavIdOnce = false; + } + else + { + selected = !selected; + MultiSelectAddSetRange(ms, selected, +1, item_data, item_data); + } + storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1); + } + } + + // Right-click handling. + // FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816 + if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + { + if (g.ActiveId != 0 && g.ActiveId != id) + ClearActiveID(); + SetFocusID(id, window); + if (!pressed && !selected) + { + pressed = true; + is_ctrl = is_shift = false; + } + } + + // Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected. + // The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something + // (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional? + const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput); + + // Alter selection + if (pressed && (!enter_pressed || !selected)) + { + // Box-select + ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) + if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) + BoxSelectPreStartDrag(ms->BoxSelectId, item_data); + + //---------------------------------------------------------------------------------------- + // ACTION | Begin | Pressed/Activated | End + //---------------------------------------------------------------------------------------- + // Keys Navigated: | Clear | Src=item, Sel=1 SetRange 1 + // Keys Navigated: Ctrl | n/a | n/a + // Keys Navigated: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 + // Keys Navigated: Ctrl+Shift | n/a | Dst=item, Sel=Src => Clear + SetRange Src-Dst + // Keys Activated: | n/a | Src=item, Sel=1 => Clear + SetRange 1 + // Keys Activated: Ctrl | n/a | Src=item, Sel=!Sel => SetSange 1 + // Keys Activated: Shift | n/a | Dst=item, Sel=1 => Clear + SetSange 1 + //---------------------------------------------------------------------------------------- + // Mouse Pressed: | n/a | Src=item, Sel=1, => Clear + SetRange 1 + // Mouse Pressed: Ctrl | n/a | Src=item, Sel=!Sel => SetRange 1 + // Mouse Pressed: Shift | n/a | Dst=item, Sel=1, => Clear + SetRange 1 + // Mouse Pressed: Ctrl+Shift | n/a | Dst=item, Sel=!Sel => SetRange Src-Dst + //---------------------------------------------------------------------------------------- + + if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0) + { + bool request_clear = false; + if (is_singleselect) + request_clear = true; + else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl) + request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true; + else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl) + request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. + if (request_clear) + MultiSelectAddSetAll(ms, false); + } + + int range_direction; + bool range_selected; + if (is_shift && !is_singleselect) + { + //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue); + if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) + storage->RangeSrcItem = item_data; + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + { + // Shift+Arrow always select + // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected) + range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + } + else + { + // Shift+Arrow copy source selection state + // Shift+Click always copy from target selection state + if (ms->IsKeyboardSetRange) + range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true; + else + range_selected = !selected; + } + range_direction = ms->RangeSrcPassedBy ? +1 : -1; + } + else + { + // Ctrl inverts selection, otherwise always select + if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0) + selected = is_ctrl ? !selected : true; + else + selected = !selected; + storage->RangeSrcItem = item_data; + range_selected = selected; + range_direction = +1; + } + MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data); + } + + // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) + if (storage->RangeSrcItem == item_data) + storage->RangeSelected = selected ? 1 : 0; + + // Update/store the selection state of focused item + if (g.NavId == id) + { + storage->NavIdItem = item_data; + storage->NavIdSelected = selected ? 1 : 0; + } + if (storage->NavIdItem == item_data) + ms->NavIdPassedBy = true; + ms->LastSubmittedItem = item_data; + + *p_selected = selected; + *p_pressed = pressed; +} + +void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected) +{ + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid }; + ms->IO.Requests.resize(0); // Can always clear previous requests + ms->IO.Requests.push_back(req); // Add new request +} + +void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item) +{ + // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges) + if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0) + { + ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1]; + if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected) + { + prev->RangeLastItem = last_item; + return; + } + } + + ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item }; + ms->IO.Requests.push_back(req); // Add new request +} + +void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. + if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } + bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name : "N/A", is_active ? "" : " *Inactive*"); + if (!is_active) { PopStyleColor(); } + if (!open) + return; + Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected); + Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected); + Text("LastSelectionSize = %d", storage->LastSelectionSize); // Provided by user + TreePop(); +#else + IM_UNUSED(storage); +#endif +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Multi-Select helpers +//------------------------------------------------------------------------- +// - ImGuiSelectionBasicStorage +// - ImGuiSelectionExternalStorage +//------------------------------------------------------------------------- + +ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() +{ + Size = 0; + PreserveOrder = false; + UserData = NULL; + AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; + _SelectionOrder = 1; // Always >0 +} + +void ImGuiSelectionBasicStorage::Clear() +{ + Size = 0; + _SelectionOrder = 1; // Always >0 + _Storage.Data.resize(0); +} + +void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) +{ + ImSwap(Size, r.Size); + ImSwap(_SelectionOrder, r._SelectionOrder); + _Storage.Data.swap(r._Storage.Data); +} + +bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const +{ + return _Storage.GetInt(id, 0) != 0; +} + +static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs) +{ + int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i; + int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i; + return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0); +} + +// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. +// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector instead of ImGuiStorage...) +bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) +{ + ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; + ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; + if (PreserveOrder && it == NULL && it_end != NULL) + ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt() + if (it == NULL) + it = _Storage.Data.Data; + IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); + if (it != it_end) + while (it->val_i == 0 && it < it_end) + it++; + const bool has_more = (it != it_end); + *opaque_it = has_more ? (void**)(it + 1) : (void**)(it); + *out_id = has_more ? it->key : 0; + if (PreserveOrder && !has_more) + _Storage.BuildSortByKey(); + return has_more; +} + +void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) +{ + int* p_int = _Storage.GetIntRef(id, 0); + if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; } + else if (!selected && *p_int != 0) { *p_int = 0; Size--; } +} + +// Optimized for batch edits (with same value of 'selected') +static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order) +{ + ImGuiStorage* storage = &selection->_Storage; + ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); + const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id); + if (selected == (is_contained && it->val_i != 0)) + return; + if (selected && !is_contained) + storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() + else if (is_contained) + it->val_i = selected ? selection_order : 0; // Modify in-place. + selection->Size += selected ? +1 : -1; +} + +static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends) +{ + ImGuiStorage* storage = &selection->_Storage; + if (selected && selection->Size != size_before_amends) + storage->BuildSortByKey(); // When done selecting: sort everything +} + +// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). +// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen. +// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem. +// - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection. +// - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform +// a lookup in order to have some way to iterate/interpolate between two items. +// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices +// and constructing a view index <> object id/ptr data structure anyway. +// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC. +// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters. +// The most simple implementation (using indices everywhere) would look like: +// for (ImGuiSelectionRequest& req : ms_io->Requests) +// { +// if (req.Type == ImGuiSelectionRequestType_SetAll) { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } } +// if (req.Type == ImGuiSelectionRequestType_SetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } } +// } +void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) +{ + // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional. + // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect(). + // Other scheme may handle SetAll differently. + IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!"); + IM_ASSERT(AdapterIndexToStorageId != NULL); + + // This is optimized/specialized to cope with very large selections (e.g. 100k+ items) + // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish(). + // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass. + // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector to reduce bandwidth, but this is a reasonable trade off to reuse code. + // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling + // left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.) + // FIXME-OPT: For each block of consecutive SetRange request: + // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage. + // - rewrite sorted storage a single time. + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + { + Clear(); + if (req.Selected) + { + _Storage.Data.reserve(ms_io->ItemsCount); + const int size_before_amends = _Storage.Data.Size; + for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++) + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder); + ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); + } + } + else if (req.Type == ImGuiSelectionRequestType_SetRange) + { + const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1; + //ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected); + if (selection_changes == 1 || (selection_changes < Size / 100)) + { + // Multiple sorted insertion + copy likely to be faster. + // Technically we could do a single copy with a little more work (sort sequential SetRange requests) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + SetItemSelected(GetStorageIdFromIndex(idx), req.Selected); + } + else + { + // Append insertion + single sort likely be faster. + // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1 + const int size_before_amends = _Storage.Data.Size; + int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0); + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection) + ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order); + if (req.Selected) + _SelectionOrder += selection_changes; + ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); + } + } + } +} + +//------------------------------------------------------------------------- + +ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage() +{ + UserData = NULL; + AdapterSetItemSelected = NULL; +} + +// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). +// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage +// This makes no assumption about underlying storage. +void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) +{ + IM_ASSERT(AdapterSetItemSelected); + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + for (int idx = 0; idx < ms_io->ItemsCount; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + } +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ListBox +//------------------------------------------------------------------------- +// - BeginListBox() +// - EndListBox() +// - ListBox() +//------------------------------------------------------------------------- + +// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. +// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as: +// if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle)) +// { .... } +// ImGui::EndChild(); +// ImGui::SameLine(); +// ImGui::AlignTextToFramePadding(); +// ImGui::Text("Label"); +// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" +// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). +bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Size default to hold ~7.25 items. + // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); + ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + g.NextItemData.ClearFlags(); + + if (!IsRectVisible(bb.Min, bb.Max)) + { + ItemSize(bb.GetSize(), style.FramePadding.y); + ItemAdd(bb, 0, &frame_bb); + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + return false; + } + + // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well. + BeginGroup(); + if (label_size.x > 0.0f) + { + ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); + RenderText(label_pos, label); + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); + AlignTextToFramePadding(); + } + + BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle); + return true; +} + +void ImGui::EndListBox() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); + IM_UNUSED(window); + + EndChild(); + EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label +} + +bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +// This is merely a helper around BeginListBox(), EndListBox(). +// Considering using those directly to submit custom data or store selection differently. +bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) +{ + ImGuiContext& g = *GImGui; + + // Calculate size from "height_in_items" + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items + 0.25f; + ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); + + if (!BeginListBox(label, size)) + return false; + + // Assume all items have even height (= 1 line of text). If you need items of different height, + // you can create a custom version of ListBox() in your code without using the clipper. + bool value_changed = false; + ImGuiListClipper clipper; + clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. + clipper.IncludeItemByIndex(*current_item); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const char* item_text = getter(user_data, i); + if (item_text == NULL) + item_text = "*Unknown item*"; + + PushID(i); + const bool item_selected = (i == *current_item); + if (Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + EndListBox(); + + if (value_changed) + MarkItemEdited(g.LastItemData.ID); + + return value_changed; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: PlotLines, PlotHistogram +//------------------------------------------------------------------------- +// - PlotEx() [Internal] +// - PlotLines() +// - PlotHistogram() +//------------------------------------------------------------------------- +// Plot/Graph widgets are not very good. +// Consider writing your own, or using a third-party one, see: +// - ImPlot https://github.com/epezent/implot +// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions +//------------------------------------------------------------------------- + +int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return -1; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav)) + return -1; + bool hovered; + ButtonBehavior(frame_bb, id, &hovered, NULL); + + // Determine scale from values if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v_min = FLT_MAX; + float v_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + const float v = values_getter(data, i); + if (v != v) // Ignore NaN values + continue; + v_min = ImMin(v_min, v); + v_max = ImMax(v_max, v); + } + if (scale_min == FLT_MAX) + scale_min = v_min; + if (scale_max == FLT_MAX) + scale_max = v_max; + } + + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; + int idx_hovered = -1; + if (values_count >= values_count_min) + { + int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + + // Tooltip on hover + if (hovered && inner_bb.Contains(g.IO.MousePos)) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * item_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = values_getter(data, (v_idx + values_offset) % values_count); + const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); + if (plot_type == ImGuiPlotType_Lines) + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); + else if (plot_type == ImGuiPlotType_Histogram) + SetTooltip("%d: %8.4g", v_idx, v0); + idx_hovered = v_idx; + } + + const float t_step = 1.0f / (float)res_w; + const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); + + float v0 = values_getter(data, (0 + values_offset) % values_count); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); + + for (int n = 0; n < res_w; n++) + { + const float t1 = t0 + t_step; + const int v1_idx = (int)(t0 * item_count + 0.5f); + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); + const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); + + // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); + if (plot_type == ImGuiPlotType_Lines) + { + window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); + } + else if (plot_type == ImGuiPlotType_Histogram) + { + if (pos1.x >= pos0.x + 2.0f) + pos1.x -= 1.0f; + window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base); + } + + t0 = t1; + tp0 = tp1; + } + } + + // Text overlay + if (overlay_text) + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + + // Return hovered index or -1 if none are hovered. + // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx(). + return idx_hovered; +} + +struct ImGuiPlotArrayGetterData +{ + const float* Values; + int Stride; + + ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } +}; + +static float Plot_ArrayGetter(void* data, int idx) +{ + ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; + const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); + return v; +} + +void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Value helpers +// Those is not very useful, legacy API. +//------------------------------------------------------------------------- +// - Value() +//------------------------------------------------------------------------- + +void ImGui::Value(const char* prefix, bool b) +{ + Text("%s: %s", prefix, (b ? "true" : "false")); +} + +void ImGui::Value(const char* prefix, int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, unsigned int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, float v, const char* float_format) +{ + if (float_format) + { + char fmt[64]; + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + Text(fmt, prefix, v); + } + else + { + Text("%s: %.3f", prefix, v); + } +} + +//------------------------------------------------------------------------- +// [SECTION] MenuItem, BeginMenu, EndMenu, etc. +//------------------------------------------------------------------------- +// - ImGuiMenuColumns [Internal] +// - BeginMenuBar() +// - EndMenuBar() +// - BeginMainMenuBar() +// - EndMainMenuBar() +// - BeginMenu() +// - EndMenu() +// - MenuItemEx() [Internal] +// - MenuItem() +//------------------------------------------------------------------------- + +// Helpers for internal use +void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) +{ + if (window_reappearing) + memset(Widths, 0, sizeof(Widths)); + Spacing = (ImU16)spacing; + CalcNextTotalWidth(true); + memset(Widths, 0, sizeof(Widths)); + TotalWidth = NextTotalWidth; + NextTotalWidth = 0; +} + +void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) +{ + ImU16 offset = 0; + bool want_spacing = false; + for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) + { + ImU16 width = Widths[i]; + if (want_spacing && width > 0) + offset += Spacing; + want_spacing |= (width > 0); + if (update_offsets) + { + if (i == 1) { OffsetLabel = offset; } + if (i == 2) { OffsetShortcut = offset; } + if (i == 3) { OffsetMark = offset; } + } + offset += width; + } + NextTotalWidth = offset; +} + +float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) +{ + Widths[0] = ImMax(Widths[0], (ImU16)w_icon); + Widths[1] = ImMax(Widths[1], (ImU16)w_label); + Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); + Widths[3] = ImMax(Widths[3], (ImU16)w_mark); + CalcNextTotalWidth(false); + return (float)ImMax(TotalWidth, NextTotalWidth); +} + +// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. +// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer. +// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary. +// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars. +bool ImGui::BeginMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!(window->Flags & ImGuiWindowFlags_MenuBar)) + return false; + + IM_ASSERT(!window->DC.MenuBarAppending); + BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore + PushID("##menubar"); + + // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. + // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. + ImRect bar_rect = window->MenuBarRect(); + ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize), IM_ROUND(bar_rect.Min.y + window->WindowBorderSize), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), IM_ROUND(bar_rect.Max.y)); + clip_rect.ClipWith(window->OuterRectClipped); + PushClipRect(clip_rect.Min, clip_rect.Max, false); + + // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags). + window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.IsSameLine = false; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.MenuBarAppending = true; + AlignTextToFramePadding(); + return true; +} + +void ImGui::EndMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. + if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + { + // Try to find out if the request is for one of our child menu + ImGuiWindow* nav_earliest_child = g.NavWindow; + while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) + nav_earliest_child = nav_earliest_child->ParentWindow; + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + { + // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) + const ImGuiNavLayer layer = ImGuiNavLayer_Menu; + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary) + FocusWindow(window); + SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); + // FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto? + if (g.NavCursorVisible) + { + g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again + g.NavCursorHideFrames = 2; + } + g.NavHighlightItemUnderNav = g.NavMousePosDirty = true; + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat + } + } + + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + PopClipRect(); + PopID(); + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + + // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here. + ImGuiGroupData& group_data = g.GroupStack.back(); + group_data.EmitItem = false; + ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos; + window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent. + EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.IsSameLine = false; + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.MenuBarAppending = false; + window->DC.CursorMaxPos = restore_cursor_max_pos; +} + +// Important: calling order matters! +// FIXME: Somehow overlapping with docking tech. +// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) +bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) +{ + IM_ASSERT(dir != ImGuiDir_None); + + ImGuiWindow* bar_window = FindWindowByName(name); + if (bar_window == NULL || bar_window->BeginCount == 0) + { + // Calculate and set window size/position + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); + ImRect avail_rect = viewport->GetBuildWorkRect(); + ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; + ImVec2 pos = avail_rect.Min; + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + pos[axis] = avail_rect.Max[axis] - axis_size; + ImVec2 size = avail_rect.GetSize(); + size[axis] = axis_size; + SetNextWindowPos(pos); + SetNextWindowSize(size); + + // Report our size into work area (for next frame) using actual window size + if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) + viewport->BuildWorkInsetMin[axis] += axis_size; + else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) + viewport->BuildWorkInsetMax[axis] += axis_size; + } + + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint + bool is_open = Begin(name, NULL, window_flags); + PopStyleVar(2); + + return is_open; +} + +bool ImGui::BeginMainMenuBar() +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. + // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? + // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + float height = GetFrameHeight(); + bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); + + if (is_open) + BeginMenuBar(); + else + End(); + return is_open; +} + +void ImGui::EndMainMenuBar() +{ + EndMenuBar(); + + // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + // FIXME: With this strategy we won't be able to restore a NULL focus. + ImGuiContext& g = *GImGui; + if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) + FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); + + End(); +} + +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others + // (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PushID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu. + // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer. + // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still + // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart + // it likely won't be a problem anyone runs into. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer) + return false; + return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true); +} + +bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); + + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. + ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + window_flags |= ImGuiWindowFlags_ChildWindow; + + // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). + // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. + // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. + if (g.MenusIdSubmittedThisFrame.contains(id)) + { + if (menu_is_open) + menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + else + g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values + return menu_is_open; + } + + // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu + g.MenusIdSubmittedThisFrame.push_back(id); + + ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + // This is only done for items for the menu set and not the full parent window. + const bool menuset_is_open = IsRootOfOpenMenuSet(); + if (menuset_is_open) + PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); + + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, + // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). + // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. + ImVec2 popup_pos, pos = window->DC.CursorPos; + PushID(label); + if (!enabled) + BeginDisabled(); + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; + + // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Menu inside an horizontal menu bar + // Selectable extend their highlight by half ItemSpacing in each direction. + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() + popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); + PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); + float w = label_size.x; + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); + LogSetNextTextDecoration("[", "]"); + RenderText(text_pos, label); + PopStyleVar(); + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + // Menu inside a regular/vertical menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); + LogSetNextTextDecoration("", ">"); + RenderText(text_pos, label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); + } + if (!enabled) + EndDisabled(); + + const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; + if (menuset_is_open) + PopItemFlag(); + + bool want_open = false; + bool want_open_nav_init = false; + bool want_close = false; + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_toward_child_menu = false; + ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below) + ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL; + if (g.HoveredWindow == window && child_menu_window != NULL) + { + const float ref_unit = g.FontSize; // FIXME-DPI + const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; + const ImRect next_window_rect = child_menu_window->Rect(); + ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); + ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack. + ta.x += child_dir * -0.5f; + tb.x += child_dir * ref_unit; + tc.x += child_dir * ref_unit; + tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus + tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f); + moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + } + + // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) + // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. + // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) + if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0) + want_close = true; + + // Open + // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test) + if (!menu_is_open && pressed) // Click/activate to open + want_open = true; + else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open + want_open = true; + else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback) + want_open = true; + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + { + want_open = want_open_nav_init = true; + NavMoveRequestCancel(); + SetNavCursorVisibleAfterMove(); + } + } + else + { + // Menu bar + if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others + { + want_open = true; + } + else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + { + want_open = true; + NavMoveRequestCancel(); + } + } + + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) + ClosePopupToLevel(g.BeginPopupStack.Size, true); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + PopID(); + + if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) + { + // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. + OpenPopup(label); + } + else if (want_open) + { + menu_is_open = true; + OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0)); + } + + if (menu_is_open) + { + ImGuiLastItemData last_item_in_parent = g.LastItemData; + SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding + menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + PopStyleVar(); + if (menu_is_open) + { + // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do: + // Perform an init request in the case the popup was already open (via a previous mouse hover) + if (want_open && want_open_nav_init && !g.NavInitRequest) + { + FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal); + NavInitWindow(g.CurrentWindow, false); + } + + // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu() + // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck) + g.LastItemData = last_item_in_parent; + if (g.HoveredWindow == window) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + } + } + else + { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + } + + return menu_is_open; +} + +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + return BeginMenuEx(label, NULL, enabled); +} + +void ImGui::EndMenu() +{ + // Nav: When a left move request our menu failed, close ourselves. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls + ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. + if (window->BeginCount == window->BeginCountPreviousFrame) + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) + if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical) + { + ClosePopupToLevel(g.BeginPopupStack.Size - 1, true); + NavMoveRequestCancel(); + } + + EndPopup(); +} + +bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = CalcTextSize(label, NULL, true); + + // See BeginMenuEx() for comments about this. + const bool menuset_is_open = IsRootOfOpenMenuSet(); + if (menuset_is_open) + PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); + + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), + // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. + bool pressed; + PushID(label); + if (!enabled) + BeginDisabled(); + + // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. + float w = label_size.x; + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); + PopStyleVar(); + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) + RenderText(text_pos, label); + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + // Menu item inside a vertical menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; + float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame + float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) + { + RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + if (shortcut_w > 0.0f) + { + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + LogSetNextTextDecoration("(", ")"); + RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + if (selected) + RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); + } + } + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + if (!enabled) + EndDisabled(); + PopID(); + if (menuset_is_open) + PopItemFlag(); + + return pressed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + return MenuItemEx(label, NULL, shortcut, selected, enabled); +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) +{ + if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +//------------------------------------------------------------------------- +// - BeginTabBar() +// - BeginTabBarEx() [Internal] +// - EndTabBar() +// - TabBarLayout() [Internal] +// - TabBarCalcTabID() [Internal] +// - TabBarCalcMaxTabWidth() [Internal] +// - TabBarFindTabById() [Internal] +// - TabBarFindTabByOrder() [Internal] +// - TabBarGetCurrentTab() [Internal] +// - TabBarGetTabName() [Internal] +// - TabBarRemoveTab() [Internal] +// - TabBarCloseTab() [Internal] +// - TabBarScrollClamp() [Internal] +// - TabBarScrollToTab() [Internal] +// - TabBarQueueFocus() [Internal] +// - TabBarQueueReorder() [Internal] +// - TabBarProcessReorderFromMousePos() [Internal] +// - TabBarProcessReorder() [Internal] +// - TabBarScrollingButtons() [Internal] +// - TabBarTabListPopupButton() [Internal] +//------------------------------------------------------------------------- + +struct ImGuiTabBarSection +{ + int TabCount; // Number of tabs in this section. + float Width; // Sum of width of tabs in this section (after shrinking down) + float Spacing; // Horizontal spacing at the end of the section. + + ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); } +}; + +namespace ImGui +{ + static void TabBarLayout(ImGuiTabBar* tab_bar); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); + static float TabBarCalcMaxTabWidth(); + static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); + static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); + static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); + static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); +} + +ImGuiTabBar::ImGuiTabBar() +{ + memset(this, 0, sizeof(*this)); + CurrFrameVisible = PrevFrameVisible = -1; + LastTabItemIdx = -1; +} + +static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) +{ + return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; +} + +static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) +{ + const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; + const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; + const int a_section = TabItemGetSectionIdx(a); + const int b_section = TabItemGetSectionIdx(b); + if (a_section != b_section) + return a_section - b_section; + return (int)(a->IndexDuringLayout - b->IndexDuringLayout); +} + +static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs) +{ + const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; + const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; + return (int)(a->BeginOrder - b->BeginOrder); +} + +static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref) +{ + ImGuiContext& g = *GImGui; + return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index); +} + +static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + if (g.TabBars.Contains(tab_bar)) + return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar)); + return ImGuiPtrOrIndex(tab_bar); +} + +bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(str_id); + ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); + ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); + tab_bar->ID = id; + tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); + tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); + //if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false)) + flags |= ImGuiTabBarFlags_IsFocused; + return BeginTabBarEx(tab_bar, tab_bar_bb, flags); +} + +bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + IM_ASSERT(tab_bar->ID != 0); + if ((flags & ImGuiTabBarFlags_DockNode) == 0) + PushOverrideID(tab_bar->ID); + + // Add to stack + g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); + g.CurrentTabBar = tab_bar; + tab_bar->Window = window; + + // Append with multiple BeginTabBar()/EndTabBar() pairs. + tab_bar->BackupCursorPos = window->DC.CursorPos; + if (tab_bar->CurrFrameVisible == g.FrameCount) + { + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); + tab_bar->BeginCount++; + return true; + } + + // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable + if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + tab_bar->TabsAddedNew = false; + + // Flags + if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + + tab_bar->Flags = flags; + tab_bar->BarRect = tab_bar_bb; + tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() + tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; + tab_bar->CurrFrameVisible = g.FrameCount; + tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight; + tab_bar->CurrTabsContentsHeight = 0.0f; + tab_bar->ItemSpacingY = g.Style.ItemSpacing.y; + tab_bar->FramePadding = g.Style.FramePadding; + tab_bar->TabsActiveCount = 0; + tab_bar->LastTabItemIdx = -1; + tab_bar->BeginCount = 1; + + // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); + + // Draw separator + // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable) + const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected); + if (g.Style.TabBarBorderSize > 0.0f) + { + const float y = tab_bar->BarRect.Max.y; + window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col); + } + return true; +} + +void ImGui::EndTabBar() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); + return; + } + + // Fallback in case no TabItem have been submitted + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) + { + tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight); + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight; + } + else + { + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight; + } + if (tab_bar->BeginCount > 1) + window->DC.CursorPos = tab_bar->BackupCursorPos; + + tab_bar->LastTabItemIdx = -1; + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + PopID(); + + g.CurrentTabBarStack.pop_back(); + g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); +} + +// Scrolling happens only in the central section (leading/trailing sections are not scrolling) +static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) +{ + return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; +} + +// This is called only once a frame before by the first call to ItemTab() +// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. +static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + tab_bar->WantLayout = false; + + // Garbage collect by compacting list + // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section) + int tab_dst_n = 0; + bool need_sort_by_section = false; + ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing + for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; + if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose) + { + // Remove tab + if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; } + if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; } + if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; } + continue; + } + if (tab_dst_n != tab_src_n) + tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; + + tab = &tab_bar->Tabs[tab_dst_n]; + tab->IndexDuringLayout = (ImS16)tab_dst_n; + + // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) + int curr_tab_section_n = TabItemGetSectionIdx(tab); + if (tab_dst_n > 0) + { + ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; + int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); + if (curr_tab_section_n == 0 && prev_tab_section_n != 0) + need_sort_by_section = true; + if (prev_tab_section_n == 2 && curr_tab_section_n != 2) + need_sort_by_section = true; + } + + sections[curr_tab_section_n].TabCount++; + tab_dst_n++; + } + if (tab_bar->Tabs.Size != tab_dst_n) + tab_bar->Tabs.resize(tab_dst_n); + + if (need_sort_by_section) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection); + + // Calculate spacing between sections + sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; + sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; + + // Setup next selected tab + ImGuiID scroll_to_tab_id = 0; + if (tab_bar->NextSelectedTabId) + { + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; + tab_bar->NextSelectedTabId = 0; + scroll_to_tab_id = tab_bar->SelectedTabId; + } + + // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). + if (tab_bar->ReorderRequestTabId != 0) + { + if (TabBarProcessReorder(tab_bar)) + if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) + scroll_to_tab_id = tab_bar->ReorderRequestTabId; + tab_bar->ReorderRequestTabId = 0; + } + + // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!) + const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; + if (tab_list_popup_button) + if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x! + scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central + // (whereas our tabs are stored as: leading, central, trailing) + int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount }; + g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size); + + // Compute ideal tabs widths + store them into shrink buffer + ImGuiTabItem* most_recently_selected_tab = NULL; + int curr_section_n = -1; + bool found_selected_tab_id = false; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); + + if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button)) + most_recently_selected_tab = tab; + if (tab->ID == tab_bar->SelectedTabId) + found_selected_tab_id = true; + if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID) + scroll_to_tab_id = tab->ID; + + // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. + // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, + // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. + const char* tab_name = TabBarGetTabName(tab_bar, tab); + const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); + tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; + + int section_n = TabItemGetSectionIdx(tab); + ImGuiTabBarSection* section = §ions[section_n]; + section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); + curr_section_n = section_n; + + // Store data so we can build an array sorted by width if we need to shrink tabs down + IM_MSVC_WARNING_SUPPRESS(6385); + ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++]; + shrink_width_item->Index = tab_n; + shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth; + tab->Width = ImMax(tab->ContentWidth, 1.0f); + } + + // Compute total ideal width (used for e.g. auto-resizing a window) + tab_bar->WidthAllTabsIdeal = 0.0f; + for (int section_n = 0; section_n < 3; section_n++) + tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing; + + // Horizontal scrolling buttons + // (note that TabBarScrollButtons() will alter BarRect.Max.x) + if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)) + if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) + { + scroll_to_tab_id = scroll_and_select_tab->ID; + if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) + tab_bar->SelectedTabId = scroll_to_tab_id; + } + + // Shrink widths if full tabs don't fit in their allocated space + float section_0_w = sections[0].Width + sections[0].Spacing; + float section_1_w = sections[1].Width + sections[1].Spacing; + float section_2_w = sections[2].Width + sections[2].Spacing; + bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth(); + float width_excess; + if (central_section_is_visible) + width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section + else + width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section + + // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore + if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) + { + int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); + int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); + ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess); + + // Apply shrunk values into tabs and sections + for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index]; + float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width); + if (shrinked_width < 0.0f) + continue; + + shrinked_width = ImMax(1.0f, shrinked_width); + int section_n = TabItemGetSectionIdx(tab); + sections[section_n].Width -= (tab->Width - shrinked_width); + tab->Width = shrinked_width; + } + } + + // Layout all active tabs + int section_tab_index = 0; + float tab_offset = 0.0f; + tab_bar->WidthAllTabs = 0.0f; + for (int section_n = 0; section_n < 3; section_n++) + { + ImGuiTabBarSection* section = §ions[section_n]; + if (section_n == 2) + tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset); + + for (int tab_n = 0; tab_n < section->TabCount; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n]; + tab->Offset = tab_offset; + tab->NameOffset = -1; + tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f); + } + tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f); + tab_offset += section->Spacing; + section_tab_index += section->TabCount; + } + + // Clear name buffers + tab_bar->TabsNames.Buf.resize(0); + + // If we have lost the selected tab, select the next most recently active one + if (found_selected_tab_id == false) + tab_bar->SelectedTabId = 0; + if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) + scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; + + // Lock in visible tab + tab_bar->VisibleTabId = tab_bar->SelectedTabId; + tab_bar->VisibleTabWasSubmitted = false; + + // Apply request requests + if (scroll_to_tab_id != 0) + TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); + else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) + { + const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; + const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; + if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) + { + const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); + } + SetKeyOwner(wheel_key, tab_bar->ID); + } + + // Update scrolling + tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); + if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) + { + // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. + // Teleport if we are aiming far off the visible line + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); + const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); + tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); + } + else + { + tab_bar->ScrollingSpeed = 0.0f; + } + tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing; + tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing; + + // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) + ImGuiWindow* window = g.CurrentWindow; + window->DC.CursorPos = tab_bar->BarRect.Min; + ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); + window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); +} + +// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) +{ + IM_ASSERT(docked_window == NULL); // master branch only + IM_UNUSED(docked_window); + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + ImGuiID id = ImHashStr(label); + KeepAliveID(id); + return id; + } + else + { + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(label); + } +} + +static float ImGui::TabBarCalcMaxTabWidth() +{ + ImGuiContext& g = *GImGui; + return g.FontSize * 20.0f; +} + +ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (tab_id != 0) + for (int n = 0; n < tab_bar->Tabs.Size; n++) + if (tab_bar->Tabs[n].ID == tab_id) + return &tab_bar->Tabs[n]; + return NULL; +} + +// Order = visible order, not submission order! (which is tab->BeginOrder) +ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order) +{ + if (order < 0 || order >= tab_bar->Tabs.Size) + return NULL; + return &tab_bar->Tabs[order]; +} + +ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) +{ + if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) + return NULL; + return &tab_bar->Tabs[tab_bar->LastTabItemIdx]; +} + +const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if (tab->NameOffset == -1) + return "N/A"; + IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size); + return tab_bar->TabsNames.Buf.Data + tab->NameOffset; +} + +// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. +void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) + tab_bar->Tabs.erase(tab); + if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } + if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } + if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } +} + +// Called on manual closure attempt +void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if (tab->Flags & ImGuiTabItemFlags_Button) + return; // A button appended with TabItemButton(). + + if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0) + { + // This will remove a frame of lag for selecting another tab on closure. + // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure + tab->WantClose = true; + if (tab_bar->VisibleTabId == tab->ID) + { + tab->LastFrameVisible = -1; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; + } + } + else + { + // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup) + if (tab_bar->VisibleTabId != tab->ID) + TabBarQueueFocus(tab_bar, tab); + } +} + +static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) +{ + scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth()); + return ImMax(scrolling, 0.0f); +} + +// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys +static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections) +{ + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); + if (tab == NULL) + return; + if (tab->Flags & ImGuiTabItemFlags_SectionMask_) + return; + + ImGuiContext& g = *GImGui; + float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + int order = TabBarGetTabOrder(tab_bar, tab); + + // Scrolling happens only in the central section (leading/trailing sections are not scrolling) + float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); + + // We make all tabs positions all relative Sections[0].Width to make code simpler + float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f); + float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f); + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width)) + { + // Scroll to the left + tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); + tab_bar->ScrollingTarget = tab_x1; + } + else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width) + { + // Scroll to the right + tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f); + tab_bar->ScrollingTarget = tab_x2 - scrollable_width; + } +} + +void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + tab_bar->NextSelectedTabId = tab->ID; +} + +void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name) +{ + IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars + ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL); + tab_bar->NextSelectedTabId = tab_id; +} + +void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset) +{ + IM_ASSERT(offset != 0); + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + tab_bar->ReorderRequestTabId = tab->ID; + tab_bar->ReorderRequestOffset = (ImS16)offset; +} + +void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) + return; + + const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; + const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); + + // Count number of contiguous tabs we are crossing over + const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; + const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); + int dst_idx = src_idx; + for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) + { + // Reordered tabs must share the same section + const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; + if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) + break; + if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) + break; + dst_idx = i; + + // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. + const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); + if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) + break; + } + + if (dst_idx != src_idx) + TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); +} + +bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) +{ + ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); + if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder)) + return false; + + //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools + int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset; + if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) + return false; + + // Reordered tabs must share the same section + // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) + ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; + if (tab2->Flags & ImGuiTabItemFlags_NoReorder) + return false; + if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) + return false; + + ImGuiTabItem item_tmp = *tab1; + ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; + ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; + const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; + memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); + *tab2 = item_tmp; + + if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) + MarkIniSettingsDirty(); + return true; +} + +static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); + const float scrolling_buttons_width = arrow_button_size.x * 2.0f; + + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); + + int select_dir = 0; + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); + const float backup_repeat_delay = g.IO.KeyRepeatDelay; + const float backup_repeat_rate = g.IO.KeyRepeatRate; + g.IO.KeyRepeatDelay = 0.250f; + g.IO.KeyRepeatRate = 0.200f; + float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width); + window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) + select_dir = -1; + window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick)) + select_dir = +1; + PopItemFlag(); + PopStyleColor(2); + g.IO.KeyRepeatRate = backup_repeat_rate; + g.IO.KeyRepeatDelay = backup_repeat_delay; + + ImGuiTabItem* tab_to_scroll_to = NULL; + if (select_dir != 0) + if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + { + int selected_order = TabBarGetTabOrder(tab_bar, tab_item); + int target_order = selected_order + select_dir; + + // Skip tab item buttons until another tab item is found or end is reached + while (tab_to_scroll_to == NULL) + { + // If we are at the end of the list, still scroll to make our tab visible + tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; + + // Cross through buttons + // (even if first/last item is a button, return it so we can update the scroll) + if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button) + { + target_order += select_dir; + selected_order += select_dir; + tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL; + } + } + } + window->DC.CursorPos = backup_cursor_pos; + tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; + + return tab_to_scroll_to; +} + +static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We use g.Style.FramePadding.y to match the square ArrowButton size + const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y; + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y); + tab_bar->BarRect.Min.x += tab_list_popup_button_width; + + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest); + PopStyleColor(2); + + ImGuiTabItem* tab_to_select = NULL; + if (open) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + if (tab->Flags & ImGuiTabItemFlags_Button) + continue; + + const char* tab_name = TabBarGetTabName(tab_bar, tab); + if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID)) + tab_to_select = tab; + } + EndCombo(); + } + + window->DC.CursorPos = backup_cursor_pos; + return tab_to_select; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. +//------------------------------------------------------------------------- +// - BeginTabItem() +// - EndTabItem() +// - TabItemButton() +// - TabItemEx() [Internal] +// - SetTabItemClosed() +// - TabItemCalcSize() [Internal] +// - TabItemBackground() [Internal] +// - TabItemLabelAndCloseButton() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return false; + } + IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! + + bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); + if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label) + } + return ret; +} + +void ImGui::EndTabItem() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + IM_ASSERT(tab_bar->LastTabItemIdx >= 0); + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) + PopID(); +} + +bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return false; + } + return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); +} + +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) +{ + // Layout whole tab bar if not already done + ImGuiContext& g = *GImGui; + if (tab_bar->WantLayout) + { + ImGuiNextItemData backup_next_item_data = g.NextItemData; + TabBarLayout(tab_bar); + g.NextItemData = backup_next_item_data; + } + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); + + // If the user called us with *p_open == false, we early out and don't render. + // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + if (p_open && !*p_open) + { + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); + return false; + } + + IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button)); + IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing + + // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented) + if (flags & ImGuiTabItemFlags_NoCloseButton) + p_open = NULL; + else if (p_open == NULL) + flags |= ImGuiTabItemFlags_NoCloseButton; + + // Acquire tab data + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); + bool tab_is_new = false; + if (tab == NULL) + { + tab_bar->Tabs.push_back(ImGuiTabItem()); + tab = &tab_bar->Tabs.back(); + tab->ID = id; + tab_bar->TabsAddedNew = tab_is_new = true; + } + tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); + + // Calculate tab contents size + ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); + tab->RequestedWidth = -1.0f; + if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth) + size.x = tab->RequestedWidth = g.NextItemData.Width; + if (tab_is_new) + tab->Width = ImMax(1.0f, size.x); + tab->ContentWidth = size.x; + tab->BeginOrder = tab_bar->TabsActiveCount++; + + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; + const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument); + const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; + tab->LastFrameVisible = g.FrameCount; + tab->Flags = flags; + + // Append name _WITH_ the zero-terminator + if (docked_window != NULL) + { + IM_ASSERT(docked_window == NULL); // master branch only + } + else + { + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); + } + + // Update selected tab + if (!is_tab_button) + { + if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) + if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) + TabBarQueueFocus(tab_bar, tab); // New tabs gets activated + if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar + TabBarQueueFocus(tab_bar, tab); + } + + // Lock visibility + // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!) + bool tab_contents_visible = (tab_bar->VisibleTabId == id); + if (tab_contents_visible) + tab_bar->VisibleTabWasSubmitted = true; + + // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches + if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) + if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) + tab_contents_visible = true; + + // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted + // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. + if (tab_appearing && (!tab_bar_appearing || tab_is_new)) + { + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); + if (is_tab_button) + return false; + return tab_contents_visible; + } + + if (tab_bar->SelectedTabId == id) + tab->LastFrameSelected = g.FrameCount; + + // Backup current layout position + const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; + + // Layout + const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; + size.x = tab->Width; + if (is_central_section) + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); + else + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + size); + + // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) + const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX); + if (want_clip_rect) + PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true); + + ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos; + ItemSize(bb.GetSize(), style.FramePadding.y); + window->DC.CursorMaxPos = backup_cursor_max_pos; + + if (!ItemAdd(bb, id)) + { + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + return tab_contents_visible; + } + + // Click to Select a tab + // Allow the close button to overlap + ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); + if (g.DragDropActive) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + if (pressed && !is_tab_button) + TabBarQueueFocus(tab_bar, tab); + + // Drag and drop: re-order tabs + if (held && !tab_appearing && IsMouseDragging(0)) + { + if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x + if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) + { + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); + } + else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) + { + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); + } + } + } + +#if 0 + if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth) + { + // Enlarge tab display when hovering + bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f))); + display_draw_list = GetForegroundDrawList(window); + TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); + } +#endif + + // Render tab shape + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + { + float x_offset = IM_TRUNC(0.4f * style.TabRounding); + if (x_offset < 2.0f * g.CurrentDpiScale) + x_offset = 0.0f; + float y_offset = 1.0f * g.CurrentDpiScale; + display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize); + } + RenderNavCursor(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + TabBarQueueFocus(tab_bar, tab); + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; + bool just_closed; + bool text_clipped; + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + if (just_closed && p_open != NULL) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Restore main window position so user can draw there + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? + if (text_clipped && g.HoveredId == id && !held) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + + IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected + if (is_tab_button) + return pressed; + return tab_contents_visible; +} + +// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. +// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar(). +// Tabs closed by the close button will automatically be flagged to avoid this issue. +void ImGui::SetTabItemClosed(const char* label) +{ + ImGuiContext& g = *GImGui; + bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); + if (is_within_manual_tab_bar) + { + ImGuiTabBar* tab_bar = g.CurrentTabBar; + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) + tab->WantClose = true; // Will be processed by next call to TabBarLayout() + } +} + +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); + if (has_close_button_or_unsaved_marker) + size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. + else + size.x += g.Style.FramePadding.x + 1.0f; + return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); +} + +ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*) +{ + IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch. + return ImVec2(0.0f, 0.0f); +} + +void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) +{ + // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. + ImGuiContext& g = *GImGui; + const float width = bb.GetWidth(); + IM_UNUSED(flags); + IM_ASSERT(width > 0.0f); + const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); + const float y1 = bb.Min.y + 1.0f; + const float y2 = bb.Max.y - g.Style.TabBarBorderSize; + draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); + draw_list->PathFillConvex(col); + if (g.Style.TabBorderSize > 0.0f) + { + draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); + draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize); + } +} + +// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic +// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter. +void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + + if (out_just_closed) + *out_just_closed = false; + if (out_text_clipped) + *out_text_clipped = false; + + if (bb.GetWidth() <= 1.0f) + return; + + // In Style V2 we'll have full override of all colors per state (e.g. focused, selected) + // But right now if you want to alter text color of tabs this is what you need to do. +#if 0 + const float backup_alpha = g.Style.Alpha; + if (!is_contents_visible) + g.Style.Alpha *= 0.7f; +#endif + + // Render text label (with clipping + alpha gradient) + unsaved marker + ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); + ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; + + // Return clipped state ignoring the close button + if (out_text_clipped) + { + *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x; + //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); + } + + const float button_sz = g.FontSize; + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y); + + // Close Button & Unsaved Marker + // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() + // 'hovered' will be true when hovering the Tab but NOT when hovering the close button + // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button + // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false + bool close_button_pressed = false; + bool close_button_visible = false; + if (close_button_id != 0) + if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) + if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) + close_button_visible = true; + bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); + + if (close_button_visible) + { + ImGuiLastItemData last_item_backup = g.LastItemData; + if (CloseButton(close_button_id, button_pos)) + close_button_pressed = true; + g.LastItemData = last_item_backup; + + // Close with middle mouse button + if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) + close_button_pressed = true; + } + else if (unsaved_marker_visible) + { + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); + RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); + } + + // This is all rather complicated + // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) + // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. + float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + if (close_button_visible || unsaved_marker_visible) + { + text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); + text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; + ellipsis_max_x = text_pixel_clip_bb.Max.x; + } + LogSetNextTextDecoration("/", "\\"); + RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); + +#if 0 + if (!is_contents_visible) + g.Style.Alpha = backup_alpha; +#endif + + if (out_just_closed) + *out_just_closed = close_button_pressed; +} + + +#endif // #ifndef IMGUI_DISABLE diff --git a/lib/imgui/imstb_rectpack.h b/lib/imgui/imstb_rectpack.h new file mode 100644 index 0000000..f6917e7 --- /dev/null +++ b/lib/imgui/imstb_rectpack.h @@ -0,0 +1,627 @@ +// [DEAR IMGUI] +// This is a slightly modified version of stb_rect_pack.h 1.01. +// Grep for [DEAR IMGUI] to find the changes. +// +// stb_rect_pack.h - v1.01 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Before #including, +// +// #define STB_RECT_PACK_IMPLEMENTATION +// +// in the file that you want to have the implementation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// github:IntellectualKitty +// +// Bugfixes / warning fixes +// Jeremy Jaussaud +// Fabian Giesen +// +// Version history: +// +// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section +// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles +// 0.99 (2019-02-07) warning fixes +// 0.11 (2017-03-03) return packing success/fail result +// 0.10 (2016-10-25) remove cast-away-const to avoid warnings +// 0.09 (2016-08-27) fix compiler warnings +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// See end of file for license information. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +typedef int stbrp_coord; + +#define STBRP__MAXVAL 0x7fffffff +// Mostly for internal use, but this is the maximum supported coordinate value. + +STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. +// +// The function returns 1 if all of the rectangles were successfully +// packed and 0 otherwise. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +#ifdef _MSC_VER +#define STBRP__NOTUSED(v) (void)(v) +#define STBRP__CDECL __cdecl +#else +#define STBRP__NOTUSED(v) (void)sizeof(v) +#define STBRP__CDECL +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; + context->extra[1].y = (1<<30); + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + + STBRP__NOTUSED(c); + + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + // if it can't possibly fit, bail immediately + if (width > c->width || height > c->height) { + fr.prev_link = NULL; + fr.x = fr.y = 0; + return fr; + } + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height <= c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + //STBRP_ASSERT(y <= best_y); [DEAR IMGUI] + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + int count=0; + cur = context->active_head; + while (cur) { + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int STBRP__CDECL rect_height_compare(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int STBRP__CDECL rect_original_order(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i, all_rects_packed = 1; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags and all_rects_packed status + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); + if (!rects[i].was_packed) + all_rects_packed = 0; + } + + // return the all_rects_packed status + return all_rects_packed; +} +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/lib/imgui/imstb_textedit.h b/lib/imgui/imstb_textedit.h new file mode 100644 index 0000000..b7a761c --- /dev/null +++ b/lib/imgui/imstb_textedit.h @@ -0,0 +1,1469 @@ +// [DEAR IMGUI] +// This is a slightly modified version of stb_textedit.h 1.14. +// Those changes would need to be pushed into nothings/stb: +// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) +// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) +// - Added name to struct or it may be forward declared in our code. +// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925) +// Grep for [DEAR IMGUI] to find the changes. +// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* + +// stb_textedit.h - v1.14 - public domain - Sean Barrett +// Development of this library was sponsored by RAD Game Tools +// +// This C header file implements the guts of a multi-line text-editing +// widget; you implement display, word-wrapping, and low-level string +// insertion/deletion, and stb_textedit will map user inputs into +// insertions & deletions, plus updates to the cursor position, +// selection state, and undo state. +// +// It is intended for use in games and other systems that need to build +// their own custom widgets and which do not have heavy text-editing +// requirements (this library is not recommended for use for editing large +// texts, as its performance does not scale and it has limited undo). +// +// Non-trivial behaviors are modelled after Windows text controls. +// +// +// LICENSE +// +// See end of file for license information. +// +// +// DEPENDENCIES +// +// Uses the C runtime function 'memmove', which you can override +// by defining IMSTB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. +// +// +// VERSION HISTORY +// +// 1.14 (2021-07-11) page up/down, various fixes +// 1.13 (2019-02-07) fix bug in undo size management +// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash +// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield +// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.9 (2016-08-27) customizable move-by-word +// 1.8 (2016-04-02) better keyboard handling when mouse button is down +// 1.7 (2015-09-13) change y range handling in case baseline is non-0 +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove +// 1.5 (2014-09-10) add support for secondary keys for OS X +// 1.4 (2014-08-17) fix signed/unsigned warnings +// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary +// 1.2 (2014-05-27) fix some RAD types that had crept into the new code +// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) +// 1.0 (2012-07-26) improve documentation, initial public release +// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode +// 0.2 (2011-11-28) fixes to undo/redo +// 0.1 (2010-07-08) initial version +// +// ADDITIONAL CONTRIBUTORS +// +// Ulf Winklemann: move-by-word in 1.1 +// Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 +// Louis Schnellbach: page up/down in 1.14 +// +// Bugfixes: +// Scott Graham +// Daniel Keller +// Omar Cornut +// Dan Thompson +// +// USAGE +// +// This file behaves differently depending on what symbols you define +// before including it. +// +// +// Header-file mode: +// +// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, +// it will operate in "header file" mode. In this mode, it declares a +// single public symbol, STB_TexteditState, which encapsulates the current +// state of a text widget (except for the string, which you will store +// separately). +// +// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a +// primitive type that defines a single character (e.g. char, wchar_t, etc). +// +// To save space or increase undo-ability, you can optionally define the +// following things that are used by the undo system: +// +// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// If you don't define these, they are set to permissive types and +// moderate sizes. The undo system does no memory allocations, so +// it grows STB_TexteditState by the worst-case storage which is (in bytes): +// +// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT +// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHARCOUNT +// +// +// Implementation mode: +// +// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it +// will compile the implementation of the text edit widget, depending +// on a large number of symbols which must be defined before the include. +// +// The implementation is defined only as static functions. You will then +// need to provide your own APIs in the same file which will access the +// static functions. +// +// The basic concept is that you provide a "string" object which +// behaves like an array of characters. stb_textedit uses indices to +// refer to positions in the string, implicitly representing positions +// in the displayed textedit. This is true for both plain text and +// rich text; even with rich text stb_truetype interacts with your +// code as if there was an array of all the displayed characters. +// +// Symbols that must be the same in header-file and implementation mode: +// +// STB_TEXTEDIT_CHARTYPE the character type +// STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// Symbols you must define for implementation mode: +// +// STB_TEXTEDIT_STRING the type of object representing a string being edited, +// typically this is a wrapper object with other data you need +// +// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) +// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters +// starting from character #n (see discussion below) +// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character +// to the xpos of the i+1'th char for a line of characters +// starting at character #n (i.e. accounts for kerning +// with previous char) +// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character +// (return type is int, -1 means not valid to insert) +// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based +// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize +// as manually wordwrapping for end-of-line positioning +// +// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i +// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// +// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key +// +// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left +// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right +// STB_TEXTEDIT_K_UP keyboard input to move cursor up +// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down +// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page +// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page +// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME +// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END +// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME +// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END +// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor +// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor +// STB_TEXTEDIT_K_UNDO keyboard input to perform undo +// STB_TEXTEDIT_K_REDO keyboard input to perform redo +// +// Optional: +// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode +// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), +// required for default WORDLEFT/WORDRIGHT handlers +// STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to +// STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to +// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT +// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line +// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line +// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text +// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text +// +// Keyboard input must be encoded as a single integer value; e.g. a character code +// and some bitflags that represent shift states. to simplify the interface, SHIFT must +// be a bitflag, so we can test the shifted state of cursor movements to allow selection, +// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. +// +// You can encode other things, such as CONTROL or ALT, in additional bits, and +// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, +// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN +// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, +// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the +// API below. The control keys will only match WM_KEYDOWN events because of the +// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN +// bit so it only decodes WM_CHAR events. +// +// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed +// row of characters assuming they start on the i'th character--the width and +// the height and the number of characters consumed. This allows this library +// to traverse the entire layout incrementally. You need to compute word-wrapping +// here. +// +// Each textfield keeps its own insert mode state, which is not how normal +// applications work. To keep an app-wide insert mode, update/copy the +// "insert_mode" field of STB_TexteditState before/after calling API functions. +// +// API +// +// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +// +// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) +// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len) +// +// Each of these functions potentially updates the string and updates the +// state. +// +// initialize_state: +// set the textedit state to a known good default state when initially +// constructing the textedit. +// +// click: +// call this with the mouse x,y on a mouse down; it will update the cursor +// and reset the selection start/end to the cursor point. the x,y must +// be relative to the text widget, with (0,0) being the top left. +// +// drag: +// call this with the mouse x,y on a mouse drag/up; it will update the +// cursor and the selection end point +// +// cut: +// call this to delete the current selection; returns true if there was +// one. you should FIRST copy the current selection to the system paste buffer. +// (To copy, just copy the current selection out of the string yourself.) +// +// paste: +// call this to paste text at the current cursor point or over the current +// selection if there is one. +// +// key: +// call this for keyboard inputs sent to the textfield. you can use it +// for "key down" events or for "translated" key events. if you need to +// do both (as in Win32), or distinguish Unicode characters from control +// inputs, set a high bit to distinguish the two; then you can define the +// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit +// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is +// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to +// anything other type you want before including. +// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are +// transformed into text and stb_textedit_text() is automatically called. +// +// text: [DEAR IMGUI] added 2024-09 +// call this to text inputs sent to the textfield. +// +// +// When rendering, you can read the cursor position and selection state from +// the STB_TexteditState. +// +// +// Notes: +// +// This is designed to be usable in IMGUI, so it allows for the possibility of +// running in an IMGUI that has NOT cached the multi-line layout. For this +// reason, it provides an interface that is compatible with computing the +// layout incrementally--we try to make sure we make as few passes through +// as possible. (For example, to locate the mouse pointer in the text, we +// could define functions that return the X and Y positions of characters +// and binary search Y and then X, but if we're doing dynamic layout this +// will run the layout algorithm many times, so instead we manually search +// forward in one pass. Similar logic applies to e.g. up-arrow and +// down-arrow movement.) +// +// If it's run in a widget that *has* cached the layout, then this is less +// efficient, but it's not horrible on modern computers. But you wouldn't +// want to edit million-line files with it. + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Header-file mode +//// +//// + +#ifndef INCLUDE_IMSTB_TEXTEDIT_H +#define INCLUDE_IMSTB_TEXTEDIT_H + +//////////////////////////////////////////////////////////////////////// +// +// STB_TexteditState +// +// Definition of STB_TexteditState which you should store +// per-textfield; it includes cursor position, selection state, +// and undo state. +// + +#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT +#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 +#endif +#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT +#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 +#endif +#ifndef IMSTB_TEXTEDIT_CHARTYPE +#define IMSTB_TEXTEDIT_CHARTYPE int +#endif +#ifndef IMSTB_TEXTEDIT_POSITIONTYPE +#define IMSTB_TEXTEDIT_POSITIONTYPE int +#endif + +typedef struct +{ + // private data + IMSTB_TEXTEDIT_POSITIONTYPE where; + IMSTB_TEXTEDIT_POSITIONTYPE insert_length; + IMSTB_TEXTEDIT_POSITIONTYPE delete_length; + int char_storage; +} StbUndoRecord; + +typedef struct +{ + // private data + StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT]; + IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point, redo_point; + int undo_char_point, redo_char_point; +} StbUndoState; + +typedef struct STB_TexteditState +{ + ///////////////////// + // + // public data + // + + int cursor; + // position of the text cursor within the string + + int select_start; // selection start point + int select_end; + // selection start and end point in characters; if equal, no selection. + // note that start may be less than or greater than end (e.g. when + // dragging the mouse, start is where the initial click was, and you + // can drag in either direction) + + unsigned char insert_mode; + // each textfield keeps its own insert mode state. to keep an app-wide + // insert mode, copy this value in/out of the app state + + int row_count_per_page; + // page size in number of row. + // this value MUST be set to >0 for pageup or pagedown in multilines documents. + + ///////////////////// + // + // private data + // + unsigned char cursor_at_end_of_line; // not implemented yet + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char padding1, padding2, padding3; + float preferred_x; // this determines where the cursor up/down tries to seek to along x + StbUndoState undostate; +} STB_TexteditState; + + +//////////////////////////////////////////////////////////////////////// +// +// StbTexteditRow +// +// Result of layout query, used by stb_textedit to determine where +// the text in each row is. + +// result of layout query +typedef struct +{ + float x0,x1; // starting x location, end x location (allows for align=right, etc) + float baseline_y_delta; // position of baseline relative to previous row's baseline + float ymin,ymax; // height of row above and below baseline + int num_chars; +} StbTexteditRow; +#endif //INCLUDE_IMSTB_TEXTEDIT_H + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Implementation mode +//// +//// + + +// implementation isn't include-guarded, since it might have indirectly +// included just the "header" portion +#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION + +#ifndef IMSTB_TEXTEDIT_memmove +#include +#define IMSTB_TEXTEDIT_memmove memmove +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Mouse input handling +// + +// traverse the layout to locate the nearest character to a display position +static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) +{ + StbTexteditRow r; + int n = STB_TEXTEDIT_STRINGLEN(str); + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + // search rows to find one that straddles 'y' + while (i < n) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + // below all text, return 'after' last character + if (i >= n) + return n; + + // check if it's before the beginning of the line + if (x < r.x0) + return i; + + // check if it's before the end of the line + if (x < r.x1) { + // search characters in row for one that straddles 'x' + prev_x = r.x0; + for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { + float w = STB_TEXTEDIT_GETWIDTH(str, i, k); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else + return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k); + } + prev_x += w; + } + // shouldn't happen, but if it does, fall through to end-of-line case + } + + // if the last character is a newline, return that. otherwise return 'after' the last character + if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) + return i+r.num_chars-1; + else + return i+r.num_chars; +} + +// API click: on mouse down, move the cursor to the clicked location, and reset the selection +static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse + // goes off the top or bottom of the text + if( state->single_line ) + { + StbTexteditRow r; + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + y = r.ymin; + } + + state->cursor = stb_text_locate_coord(str, x, y); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location +static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + int p = 0; + + // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse + // goes off the top or bottom of the text + if( state->single_line ) + { + StbTexteditRow r; + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + y = r.ymin; + } + + if (state->select_start == state->select_end) + state->select_start = state->cursor; + + p = stb_text_locate_coord(str, x, y); + state->cursor = state->select_end = p; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Keyboard input handling +// + +// forward declarations +static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); + +typedef struct +{ + float x,y; // position of n'th character + float height; // height of line + int first_char, length; // first char of row, and length + int prev_first; // first char of previous row +} StbFindState; + +// find the x/y location of a character, and remember info about the previous row in +// case we get a move-up event (for page up, we'll have to rescan) +static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line) +{ + StbTexteditRow r; + int prev_start = 0; + int z = STB_TEXTEDIT_STRINGLEN(str); + int i=0, first; + + if (n == z && single_line) { + // special case if it's at the end (may not be needed?) + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + return; + } + + // search rows to find the one that straddles character n + find->y = 0; + + for(;;) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (n < i + r.num_chars) + break; + if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line + break; // [DEAR IMGUI] + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + if (i == z) // [DEAR IMGUI] + { + r.num_chars = 0; // [DEAR IMGUI] + break; // [DEAR IMGUI] + } + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + // now scan to find xpos + find->x = r.x0; + for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first) + find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); +} + +#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +// make the selection/cursor state valid if client altered the string +static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + int n = STB_TEXTEDIT_STRINGLEN(str); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + // if clamping forced them to be equal, move the cursor to match + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +// delete characters while updating undo +static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) +{ + stb_text_makeundo_delete(str, state, where, len); + STB_TEXTEDIT_DELETECHARS(str, where, len); + state->has_preferred_x = 0; +} + +// delete the section +static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + stb_textedit_clamp(str, state); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +// canoncialize the selection so start <= end +static void stb_textedit_sortselection(STB_TexteditState *state) +{ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +// move cursor to first character of selection +static void stb_textedit_move_to_first(STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +// move cursor to last character of selection +static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +// [DEAR IMGUI] +// Functions must be implemented for UTF8 support +// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit. +// There is not necessarily a '[DEAR IMGUI]' at the usage sites. +#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX +#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1) +#endif +#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX +#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1) +#endif + +#ifdef STB_TEXTEDIT_IS_SPACE +static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) +{ + return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; +} + +#ifndef STB_TEXTEDIT_MOVEWORDLEFT +static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c ) +{ + --c; // always move at least one character + while( c >= 0 && !is_word_boundary( str, c ) ) + --c; + + if( c < 0 ) + c = 0; + + return c; +} +#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous +#endif + +#ifndef STB_TEXTEDIT_MOVEWORDRIGHT +static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c ) +{ + const int len = STB_TEXTEDIT_STRINGLEN(str); + ++c; // always move at least one character + while( c < len && !is_word_boundary( str, c ) ) + ++c; + + if( c > len ) + c = len; + + return c; +} +#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next +#endif + +#endif + +// update selection and cursor to match each other +static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) +{ + if (!STB_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else + state->cursor = state->select_end; +} + +// API cut: delete selection +static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_delete_selection(str,state); // implicitly clamps + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +// API paste: replace existing selection with passed-in text +static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len) +{ + // if there's a selection, the paste should delete it + stb_textedit_clamp(str, state); + stb_textedit_delete_selection(str,state); + // try to insert the characters + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { + stb_text_makeundo_insert(state, state->cursor, len); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) + return 0; +} + +#ifndef STB_TEXTEDIT_KEYTYPE +#define STB_TEXTEDIT_KEYTYPE int +#endif + +// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility. +static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) +{ + // can't add newline in single-line mode + if (text[0] == '\n' && state->single_line) + return; + + if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { + stb_text_makeundo_replace(str, state, state->cursor, 1, 1); + STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + state->cursor += text_len; + state->has_preferred_x = 0; + } + } + else { + stb_textedit_delete_selection(str, state); // implicitly clamps + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) { + stb_text_makeundo_insert(state, state->cursor, text_len); + state->cursor += text_len; + state->has_preferred_x = 0; + } + } +} + +// API key: process a keyboard input +static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) +{ +retry: + switch (key) { + default: { +#ifdef STB_TEXTEDIT_KEYTOTEXT + int c = STB_TEXTEDIT_KEYTOTEXT(key); + if (c > 0) { + IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c; + stb_textedit_text(str, state, &ch, 1); + } +#endif + break; + } + +#ifdef STB_TEXTEDIT_K_INSERT + case STB_TEXTEDIT_K_INSERT: + state->insert_mode = !state->insert_mode; + break; +#endif + + case STB_TEXTEDIT_K_UNDO: + stb_text_undo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_REDO: + stb_text_redo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT: + // if currently there's a selection, move cursor to start of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else + if (state->cursor > 0) + state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_RIGHT: + // if currently there's a selection, move cursor to end of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else + state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); + stb_textedit_clamp(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + // move selection left + if (state->select_end > 0) + state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end); + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_MOVEWORDLEFT + case STB_TEXTEDIT_K_WORDLEFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else { + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + +#ifdef STB_TEXTEDIT_MOVEWORDRIGHT + case STB_TEXTEDIT_K_WORDRIGHT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else { + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + + case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + // move selection right + state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end); + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_DOWN: + case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: + case STB_TEXTEDIT_K_PGDOWN: + case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN; + int row_count = is_page ? state->row_count_per_page : 1; + + if (!is_page && state->single_line) { + // on windows, up&down in single-line behave like left&right + key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + for (j = 0; j < row_count; ++j) { + float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + if (find.length == 0) + break; + + // [DEAR IMGUI] + // going down while being on the last line shouldn't bring us to that line end + if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE) + break; + + // now find character position down a row + state->cursor = start; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); + #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + + // go to next line + find.first_char = find.first_char + find.length; + find.length = row.num_chars; + } + break; + } + + case STB_TEXTEDIT_K_UP: + case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: + case STB_TEXTEDIT_K_PGUP: + case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP; + int row_count = is_page ? state->row_count_per_page : 1; + + if (!is_page && state->single_line) { + // on windows, up&down become left&right + key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + for (j = 0; j < row_count; ++j) { + float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + // can only go up if there's a previous row + if (find.prev_first == find.first_char) + break; + + // now find character position up a row + state->cursor = find.prev_first; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); + #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + + // go to previous line + // (we need to scan previous line the hard way. maybe we could expose this as a new API function?) + prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0; + while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE) + --prev_scan; + find.first_char = find.prev_first; + find.prev_first = prev_scan; + } + break; + } + + case STB_TEXTEDIT_K_DELETE: + case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + int n = STB_TEXTEDIT_STRINGLEN(str); + if (state->cursor < n) + stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor); + } + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_BACKSPACE: + case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + stb_textedit_clamp(str, state); + if (state->cursor > 0) { + int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); + stb_textedit_delete(str, state, prev, state->cursor - prev); + state->cursor = prev; + } + } + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2: +#endif + case STB_TEXTEDIT_K_TEXTSTART: + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2: +#endif + case STB_TEXTEDIT_K_TEXTEND: + state->cursor = STB_TEXTEDIT_STRINGLEN(str); + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); + state->has_preferred_x = 0; + break; + + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2: +#endif + case STB_TEXTEDIT_K_LINESTART: + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + if (state->single_line) + state->cursor = 0; + else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2: +#endif + case STB_TEXTEDIT_K_LINEEND: { + int n = STB_TEXTEDIT_STRINGLEN(str); + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + if (state->single_line) + state->cursor = n; + else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) + ++state->cursor; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + if (state->single_line) + state->cursor = 0; + else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { + int n = STB_TEXTEDIT_STRINGLEN(str); + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + if (state->single_line) + state->cursor = n; + else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) + ++state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; + break; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Undo processing +// +// @OPTIMIZE: the undo/redo buffer should be circular + +static void stb_textedit_flush_redo(StbUndoState *state) +{ + state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; +} + +// discard the oldest entry in the undo list +static void stb_textedit_discard_undo(StbUndoState *state) +{ + if (state->undo_point > 0) { + // if the 0th undo state has characters, clean those up + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + // delete n characters from all other records + state->undo_char_point -= n; + IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); + for (i=0; i < state->undo_point; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it + } + --state->undo_point; + IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); + } +} + +// discard the oldest entry in the redo list--it's bad if this +// ever happens, but because undo & redo have to store the actual +// characters in different cases, the redo character buffer can +// fill up even though the undo buffer didn't +static void stb_textedit_discard_redo(StbUndoState *state) +{ + int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1; + + if (state->redo_point <= k) { + // if the k'th undo state has characters, clean those up + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + // move the remaining redo character data to the end of the buffer + state->redo_char_point += n; + IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); + // adjust the position of all the other records to account for above memmove + for (i=state->redo_point; i < k; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage += n; + } + // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' + // [DEAR IMGUI] + size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); + const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; + const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; + IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); + IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); + IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); + + // now move redo_point to point to the new one + ++state->redo_point; + } +} + +static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) +{ + // any time we create a new undo record, we discard redo + stb_textedit_flush_redo(state); + + // if we have no free records, we have to make room, by sliding the + // existing records down + if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) + stb_textedit_discard_undo(state); + + // if the characters to store won't possibly fit in the buffer, we can't undo + if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return NULL; + } + + // if we don't have enough free characters in the buffer, we have to make room + while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) + stb_textedit_discard_undo(state); + + return &state->undo_rec[state->undo_point++]; +} + +static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) +{ + StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); + if (r == NULL) + return NULL; + + r->where = pos; + r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len; + r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return NULL; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point += insert_len; + return &state->undo_char[r->char_storage]; + } +} + +static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord u, *r; + if (s->undo_point == 0) + return; + + // we need to do two things: apply the undo record, and create a redo record + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) { + // if the undo record says to delete characters, then the redo record will + // need to re-insert the characters that get deleted, so we need to store + // them. + + // there are three cases: + // there's enough room to store the characters + // characters stored for *redoing* don't leave room for redo + // characters stored for *undoing* don't leave room for redo + // if the last is true, we have to bail + + if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) { + // the undo records take up too much character space; there's no space to store the redo characters + r->insert_length = 0; + } else { + int i; + + // there's definitely room to store the characters eventually + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + // should never happen: + if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) + return; + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); + } + r = &s->undo_rec[s->redo_point-1]; + + r->char_storage = s->redo_char_point - u.delete_length; + s->redo_char_point = s->redo_char_point - u.delete_length; + + // now save the characters + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); + } + + // now we can carry out the deletion + STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); + } + + // check type of recorded action: + if (u.insert_length) { + // easy case: was a deletion, so we need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point -= u.insert_length; + } + + state->cursor = u.where + u.insert_length; + + s->undo_point--; + s->redo_point--; +} + +static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord *u, r; + if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) + return; + + // we need to do two things: apply the redo record, and create an undo record + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + // we KNOW there must be room for the undo record, because the redo record + // was derived from an undo record + + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + // the redo record requires us to delete characters, so the undo record + // needs to store the characters + + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = s->undo_char_point + u->insert_length; + + // now save the characters + for (i=0; i < u->insert_length; ++i) + s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); + } + + STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); + } + + if (r.insert_length) { + // easy case: need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + s->redo_char_point += r.insert_length; + } + + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) +{ + stb_text_createundo(&state->undostate, where, 0, length); +} + +static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) +{ + int i; + IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) +{ + int i; + IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +// reset the state to default +static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) +{ + state->undostate.undo_point = 0; + state->undostate.undo_char_point = 0; + state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; + state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char) is_single_line; + state->insert_mode = 0; + state->row_count_per_page = 0; +} + +// API initialize +static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +{ + stb_textedit_clear_state(state, is_single_line); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len) +{ + return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif//IMSTB_TEXTEDIT_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/lib/imgui/imstb_truetype.h b/lib/imgui/imstb_truetype.h new file mode 100644 index 0000000..976f09c --- /dev/null +++ b/lib/imgui/imstb_truetype.h @@ -0,0 +1,5085 @@ +// [DEAR IMGUI] +// This is a slightly modified version of stb_truetype.h 1.26. +// Mostly fixing for compiler and static analyzer warnings. +// Grep for [DEAR IMGUI] to find the changes. + +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph received the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422 + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + int denom = (x2 - (x1+1)); + y_final = y_bottom; + if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316) + dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i, j, n, return_value; // [DEAR IMGUI] removed = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/lib/tiny_obj_loader.h b/lib/tiny_obj_loader.h deleted file mode 100644 index ee44076..0000000 --- a/lib/tiny_obj_loader.h +++ /dev/null @@ -1,2029 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2012-2016 Syoyo Fujita and many contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -// -// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) -// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) -// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) -// version 1.0.3 : Support parsing texture options(#85) -// version 1.0.2 : Improve parsing speed by about a factor of 2 for large -// files(#105) -// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) -// version 1.0.0 : Change data structure. Change license from BSD to MIT. -// - -// -// Use this in *one* .cc -// #define TINYOBJLOADER_IMPLEMENTATION -// #include "tiny_obj_loader.h" -// - -#ifndef TINY_OBJ_LOADER_H_ -#define TINY_OBJ_LOADER_H_ - -#include -#include -#include - -namespace tinyobj { - -// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... -// -// -blendu on | off # set horizontal texture blending -// (default on) -// -blendv on | off # set vertical texture blending -// (default on) -// -boost real_value # boost mip-map sharpness -// -mm base_value gain_value # modify texture map values (default -// 0 1) -// # base_value = brightness, -// gain_value = contrast -// -o u [v [w]] # Origin offset (default -// 0 0 0) -// -s u [v [w]] # Scale (default -// 1 1 1) -// -t u [v [w]] # Turbulence (default -// 0 0 0) -// -texres resolution # texture resolution to create -// -clamp on | off # only render texels in the clamped -// 0-1 range (default off) -// # When unclamped, textures are -// repeated across a surface, -// # when clamped, only texels which -// fall within the 0-1 -// # range are rendered. -// -bm mult_value # bump multiplier (for bump maps -// only) -// -// -imfchan r | g | b | m | l | z # specifies which channel of the file -// is used to -// # create a scalar or bump texture. -// r:red, g:green, -// # b:blue, m:matte, l:luminance, -// z:z-depth.. -// # (the default for bump is 'l' and -// for decal is 'm') -// bump -imfchan r bumpmap.tga # says to use the red channel of -// bumpmap.tga as the bumpmap -// -// For reflection maps... -// -// -type sphere # specifies a sphere for a "refl" -// reflection map -// -type cube_top | cube_bottom | # when using a cube map, the texture -// file for each -// cube_front | cube_back | # side of the cube is specified -// separately -// cube_left | cube_right - -#ifdef TINYOBJLOADER_USE_DOUBLE - //#pragma message "using double" - typedef double real_t; -#else - //#pragma message "using float" - typedef float real_t; -#endif - -typedef enum { - TEXTURE_TYPE_NONE, // default - TEXTURE_TYPE_SPHERE, - TEXTURE_TYPE_CUBE_TOP, - TEXTURE_TYPE_CUBE_BOTTOM, - TEXTURE_TYPE_CUBE_FRONT, - TEXTURE_TYPE_CUBE_BACK, - TEXTURE_TYPE_CUBE_LEFT, - TEXTURE_TYPE_CUBE_RIGHT -} texture_type_t; - -typedef struct { - texture_type_t type; // -type (default TEXTURE_TYPE_NONE) - real_t sharpness; // -boost (default 1.0?) - real_t brightness; // base_value in -mm option (default 0) - real_t contrast; // gain_value in -mm option (default 1) - real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) - real_t scale[3]; // -s u [v [w]] (default 1 1 1) - real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) - // int texture_resolution; // -texres resolution (default = ?) TODO - bool clamp; // -clamp (default false) - char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') - bool blendu; // -blendu (default on) - bool blendv; // -blendv (default on) - real_t bump_multiplier; // -bm (for bump maps only, default 1.0) -} texture_option_t; - -typedef struct { - std::string name; - - real_t ambient[3]; - real_t diffuse[3]; - real_t specular[3]; - real_t transmittance[3]; - real_t emission[3]; - real_t shininess; - real_t ior; // index of refraction - real_t dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; - - int dummy; // Suppress padding warning. - - std::string ambient_texname; // map_Ka - std::string diffuse_texname; // map_Kd - std::string specular_texname; // map_Ks - std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, bump - std::string displacement_texname; // disp - std::string alpha_texname; // map_d - - texture_option_t ambient_texopt; - texture_option_t diffuse_texopt; - texture_option_t specular_texopt; - texture_option_t specular_highlight_texopt; - texture_option_t bump_texopt; - texture_option_t displacement_texopt; - texture_option_t alpha_texopt; - - // PBR extension - // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - real_t roughness; // [0, 1] default 0 - real_t metallic; // [0, 1] default 0 - real_t sheen; // [0, 1] default 0 - real_t clearcoat_thickness; // [0, 1] default 0 - real_t clearcoat_roughness; // [0, 1] default 0 - real_t anisotropy; // aniso. [0, 1] default 0 - real_t anisotropy_rotation; // anisor. [0, 1] default 0 - real_t pad0; - real_t pad1; - std::string roughness_texname; // map_Pr - std::string metallic_texname; // map_Pm - std::string sheen_texname; // map_Ps - std::string emissive_texname; // map_Ke - std::string normal_texname; // norm. For normal mapping. - - texture_option_t roughness_texopt; - texture_option_t metallic_texopt; - texture_option_t sheen_texopt; - texture_option_t emissive_texopt; - texture_option_t normal_texopt; - - int pad2; - - std::map unknown_parameter; -} material_t; - -typedef struct { - std::string name; - - std::vector intValues; - std::vector floatValues; - std::vector stringValues; -} tag_t; - -// Index struct to support different indices for vtx/normal/texcoord. -// -1 means not used. -typedef struct { - int vertex_index; - int normal_index; - int texcoord_index; -} index_t; - -typedef struct { - std::vector indices; - std::vector num_face_vertices; // The number of vertices per - // face. 3 = polygon, 4 = quad, - // ... Up to 255. - std::vector material_ids; // per-face material ID - std::vector tags; // SubD tag -} mesh_t; - -typedef struct { - std::string name; - mesh_t mesh; -} shape_t; - -// Vertex attributes -typedef struct { - std::vector vertices; // 'v' - std::vector normals; // 'vn' - std::vector texcoords; // 'vt' -} attrib_t; - -typedef struct callback_t_ { - // W is optional and set to 1 if there is no `w` item in `v` line - void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); - void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); - - // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in - // `vt` line. - void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); - - // called per 'f' line. num_indices is the number of face indices(e.g. 3 for - // triangle, 4 for quad) - // 0 will be passed for undefined index in index_t members. - void (*index_cb)(void *user_data, index_t *indices, int num_indices); - // `name` material name, `material_id` = the array index of material_t[]. -1 - // if - // a material not found in .mtl - void (*usemtl_cb)(void *user_data, const char *name, int material_id); - // `materials` = parsed material data. - void (*mtllib_cb)(void *user_data, const material_t *materials, - int num_materials); - // There may be multiple group names - void (*group_cb)(void *user_data, const char **names, int num_names); - void (*object_cb)(void *user_data, const char *name); - - callback_t_() - : vertex_cb(NULL), - normal_cb(NULL), - texcoord_cb(NULL), - index_cb(NULL), - usemtl_cb(NULL), - mtllib_cb(NULL), - group_cb(NULL), - object_cb(NULL) {} -} callback_t; - -class MaterialReader { - public: - MaterialReader() {} - virtual ~MaterialReader(); - - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *err) = 0; -}; - -class MaterialFileReader : public MaterialReader { - public: - explicit MaterialFileReader(const std::string &mtl_basedir) - : m_mtlBaseDir(mtl_basedir) {} - virtual ~MaterialFileReader() {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *err); - - private: - std::string m_mtlBaseDir; -}; - -class MaterialStreamReader : public MaterialReader { - public: - explicit MaterialStreamReader(std::istream &inStream) - : m_inStream(inStream) {} - virtual ~MaterialStreamReader() {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *err); - - private: - std::istream &m_inStream; -}; - -/// Loads .obj from a file. -/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data -/// 'shapes' will be filled with parsed shape data -/// Returns true when loading .obj become success. -/// Returns warning and error message into `err` -/// 'mtl_basedir' is optional, and used for base directory for .mtl file. -/// In default(`NULL'), .mtl file is searched from an application's working -/// directory. -/// 'triangulate' is optional, and used whether triangulate polygon face in .obj -/// or not. -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - const char *filename, const char *mtl_basedir = NULL, - bool triangulate = true); - -/// Loads .obj from a file with custom user callback. -/// .mtl is loaded as usual and parsed material_t data will be passed to -/// `callback.mtllib_cb`. -/// Returns true when loading .obj/.mtl become success. -/// Returns warning and error message into `err` -/// See `examples/callback_api/` for how to use this function. -bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data = NULL, - MaterialReader *readMatFn = NULL, - std::string *err = NULL); - -/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve -/// std::istream for materials. -/// Returns true when loading .obj become success. -/// Returns warning and error message into `err` -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - std::istream *inStream, MaterialReader *readMatFn = NULL, - bool triangulate = true); - -/// Loads materials into std::map -void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning); - -} // namespace tinyobj - -#endif // TINY_OBJ_LOADER_H_ - -#ifdef TINYOBJLOADER_IMPLEMENTATION -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace tinyobj { - -MaterialReader::~MaterialReader() {} - -#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) - -struct vertex_index { - int v_idx, vt_idx, vn_idx; - vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} - explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} - vertex_index(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} -}; - -struct tag_sizes { - tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} - int num_ints; - int num_reals; - int num_strings; -}; - -struct obj_shape { - std::vector v; - std::vector vn; - std::vector vt; -}; - -// See -// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf -static std::istream &safeGetline(std::istream &is, std::string &t) { - t.clear(); - - // The characters in the stream are read one-by-one using a std::streambuf. - // That is faster than reading them one-by-one using the std::istream. - // Code that uses streambuf this way must be guarded by a sentry object. - // The sentry object performs various tasks, - // such as thread synchronization and updating the stream state. - - std::istream::sentry se(is, true); - std::streambuf *sb = is.rdbuf(); - - for (;;) { - int c = sb->sbumpc(); - switch (c) { - case '\n': - return is; - case '\r': - if (sb->sgetc() == '\n') sb->sbumpc(); - return is; - case EOF: - // Also handle the case when the last line has no line ending - if (t.empty()) is.setstate(std::ios::eofbit); - return is; - default: - t += static_cast(c); - } - } -} - -#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) -#define IS_DIGIT(x) \ - (static_cast((x) - '0') < static_cast(10)) -#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) - -// Make index zero-base, and also support relative index. -static inline int fixIndex(int idx, int n) { - if (idx > 0) return idx - 1; - if (idx == 0) return 0; - return n + idx; // negative value = relative -} - -static inline std::string parseString(const char **token) { - std::string s; - (*token) += strspn((*token), " \t"); - size_t e = strcspn((*token), " \t\r"); - s = std::string((*token), &(*token)[e]); - (*token) += e; - return s; -} - -static inline int parseInt(const char **token) { - (*token) += strspn((*token), " \t"); - int i = atoi((*token)); - (*token) += strcspn((*token), " \t\r"); - return i; -} - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) { - if (s >= s_end) { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') { - sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - goto fail; - } - - // Read the integer part. - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - - // We must make sure we actually got something. - if (read == 0) goto fail; - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) goto assemble; - - // Read the decimal part. - if (*curr == '.') { - curr++; - read = 1; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - static const double pow_lut[] = { - 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, - }; - const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; - - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * - (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); - read++; - curr++; - end_not_reached = (curr != s_end); - } - } else if (*curr == 'e' || *curr == 'E') { - } else { - goto assemble; - } - - if (!end_not_reached) goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') { - curr++; - // Figure out if a sign is present and if it is. - end_not_reached = (curr != s_end); - if (end_not_reached && (*curr == '+' || *curr == '-')) { - exp_sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - // Empty E is not allowed. - goto fail; - } - - read = 0; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - exponent *= (exp_sign == '+' ? 1 : -1); - if (read == 0) goto fail; - } - -assemble: - *result = - (sign == '+' ? 1 : -1) * - (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa); - return true; -fail: - return false; -} - -static inline real_t parseReal(const char **token, double default_value = 0.0) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - double val = default_value; - tryParseDouble((*token), end, &val); - real_t f = static_cast(val); - (*token) = end; - return f; -} - -static inline void parseReal2(real_t *x, real_t *y, const char **token, - const double default_x = 0.0, - const double default_y = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); -} - -static inline void parseReal3(real_t *x, real_t *y, real_t *z, const char **token, - const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); -} - -static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, - const char **token, const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0, - const double default_w = 1.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - (*w) = parseReal(token, default_w); -} - -static inline bool parseOnOff(const char **token, bool default_value = true) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - - bool ret = default_value; - if ((0 == strncmp((*token), "on", 2))) { - ret = true; - } else if ((0 == strncmp((*token), "off", 3))) { - ret = false; - } - - (*token) = end; - return ret; -} - -static inline texture_type_t parseTextureType( - const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - texture_type_t ty = default_value; - - if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { - ty = TEXTURE_TYPE_CUBE_TOP; - } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { - ty = TEXTURE_TYPE_CUBE_BOTTOM; - } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { - ty = TEXTURE_TYPE_CUBE_LEFT; - } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { - ty = TEXTURE_TYPE_CUBE_RIGHT; - } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { - ty = TEXTURE_TYPE_CUBE_FRONT; - } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { - ty = TEXTURE_TYPE_CUBE_BACK; - } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { - ty = TEXTURE_TYPE_SPHERE; - } - - (*token) = end; - return ty; -} - -static tag_sizes parseTagTriple(const char **token) { - tag_sizes ts; - - ts.num_ints = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - (*token)++; - - ts.num_reals = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - (*token)++; - - ts.num_strings = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r") + 1; - - return ts; -} - -// Parse triples with index offsets: i, i/j/k, i//k, i/j -static vertex_index parseTriple(const char **token, int vsize, int vnsize, - int vtsize) { - vertex_index vi(-1); - - vi.v_idx = fixIndex(atoi((*token)), vsize); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - vi.vn_idx = fixIndex(atoi((*token)), vnsize); - (*token) += strcspn((*token), "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = fixIndex(atoi((*token)), vtsize); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - - // i/j/k - (*token)++; // skip '/' - vi.vn_idx = fixIndex(atoi((*token)), vnsize); - (*token) += strcspn((*token), "/ \t\r"); - return vi; -} - -// Parse raw triples: i, i/j/k, i//k, i/j -static vertex_index parseRawTriple(const char **token) { - vertex_index vi(static_cast(0)); // 0 is an invalid index in OBJ - - vi.v_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - - // i/j/k - (*token)++; // skip '/' - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; -} - -static bool ParseTextureNameAndOption(std::string *texname, - texture_option_t *texopt, - const char *linebuf, const bool is_bump) { - // @todo { write more robust lexer and parser. } - bool found_texname = false; - std::string texture_name; - - // Fill with default value for texopt. - if (is_bump) { - texopt->imfchan = 'l'; - } else { - texopt->imfchan = 'm'; - } - texopt->bump_multiplier = 1.0f; - texopt->clamp = false; - texopt->blendu = true; - texopt->blendv = true; - texopt->sharpness = 1.0f; - texopt->brightness = 0.0f; - texopt->contrast = 1.0f; - texopt->origin_offset[0] = 0.0f; - texopt->origin_offset[1] = 0.0f; - texopt->origin_offset[2] = 0.0f; - texopt->scale[0] = 1.0f; - texopt->scale[1] = 1.0f; - texopt->scale[2] = 1.0f; - texopt->turbulence[0] = 0.0f; - texopt->turbulence[1] = 0.0f; - texopt->turbulence[2] = 0.0f; - texopt->type = TEXTURE_TYPE_NONE; - - const char *token = linebuf; // Assume line ends with NULL - - while (!IS_NEW_LINE((*token))) { - if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendu = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendv = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->clamp = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->sharpness = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { - token += 4; - texopt->bump_multiplier = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), - &(texopt->origin_offset[2]), &token); - } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), - &token, 1.0, 1.0, 1.0); - } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), - &(texopt->turbulence[2]), &token); - } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { - token += 5; - texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); - } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { - token += 9; - token += strspn(token, " \t"); - const char *end = token + strcspn(token, " \t\r"); - if ((end - token) == 1) { // Assume one char for -imfchan - texopt->imfchan = (*token); - } - token = end; - } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { - token += 4; - parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); - } else { - // Assume texture filename - token += strspn(token, " \t"); // skip space - size_t len = strcspn(token, " \t\r"); // untile next space - texture_name = std::string(token, token + len); - token += len; - - token += strspn(token, " \t"); // skip space - - found_texname = true; - } - } - - if (found_texname) { - (*texname) = texture_name; - return true; - } else { - return false; - } -} - -static void InitMaterial(material_t *material) { - material->name = ""; - material->ambient_texname = ""; - material->diffuse_texname = ""; - material->specular_texname = ""; - material->specular_highlight_texname = ""; - material->bump_texname = ""; - material->displacement_texname = ""; - material->alpha_texname = ""; - for (int i = 0; i < 3; i++) { - material->ambient[i] = 0.f; - material->diffuse[i] = 0.f; - material->specular[i] = 0.f; - material->transmittance[i] = 0.f; - material->emission[i] = 0.f; - } - material->illum = 0; - material->dissolve = 1.f; - material->shininess = 1.f; - material->ior = 1.f; - - material->roughness = 0.f; - material->metallic = 0.f; - material->sheen = 0.f; - material->clearcoat_thickness = 0.f; - material->clearcoat_roughness = 0.f; - material->anisotropy_rotation = 0.f; - material->anisotropy = 0.f; - material->roughness_texname = ""; - material->metallic_texname = ""; - material->sheen_texname = ""; - material->emissive_texname = ""; - material->normal_texname = ""; - - material->unknown_parameter.clear(); -} - -static bool exportFaceGroupToShape( - shape_t *shape, const std::vector > &faceGroup, - const std::vector &tags, const int material_id, - const std::string &name, bool triangulate) { - if (faceGroup.empty()) { - return false; - } - - // Flatten vertices and indices - for (size_t i = 0; i < faceGroup.size(); i++) { - const std::vector &face = faceGroup[i]; - - vertex_index i0 = face[0]; - vertex_index i1(-1); - vertex_index i2 = face[1]; - - size_t npolys = face.size(); - - if (triangulate) { - // Polygon -> triangle fan conversion - for (size_t k = 2; k < npolys; k++) { - i1 = i2; - i2 = face[k]; - - index_t idx0, idx1, idx2; - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - } - } else { - for (size_t k = 0; k < npolys; k++) { - index_t idx; - idx.vertex_index = face[k].v_idx; - idx.normal_index = face[k].vn_idx; - idx.texcoord_index = face[k].vt_idx; - shape->mesh.indices.push_back(idx); - } - - shape->mesh.num_face_vertices.push_back( - static_cast(npolys)); - shape->mesh.material_ids.push_back(material_id); // per face - } - } - - shape->name = name; - shape->mesh.tags = tags; - - return true; -} - -// Split a string with specified delimiter character. -// http://stackoverflow.com/questions/236129/split-a-string-in-c -static void SplitString(const std::string &s, char delim, - std::vector &elems) { - std::stringstream ss; - ss.str(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } -} - -void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning) { - // Create a default material anyway. - material_t material; - InitMaterial(&material); - - // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. - bool has_d = false; - bool has_tr = false; - - std::stringstream ss; - - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - - // Trim trailing whitespace. - if (linebuf.size() > 0) { - linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); - } - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { - // flush previous material. - if (!material.name.empty()) { - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - } - - // initial temporary material - InitMaterial(&material); - - has_d = false; - has_tr = false; - - // set new mtl name - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - material.name = namebuf; - continue; - } - - // ambient - if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.ambient[0] = r; - material.ambient[1] = g; - material.ambient[2] = b; - continue; - } - - // diffuse - if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.diffuse[0] = r; - material.diffuse[1] = g; - material.diffuse[2] = b; - continue; - } - - // specular - if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.specular[0] = r; - material.specular[1] = g; - material.specular[2] = b; - continue; - } - - // transmittance - if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || - (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.transmittance[0] = r; - material.transmittance[1] = g; - material.transmittance[2] = b; - continue; - } - - // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { - token += 2; - material.ior = parseReal(&token); - continue; - } - - // emission - if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.emission[0] = r; - material.emission[1] = g; - material.emission[2] = b; - continue; - } - - // shininess - if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.shininess = parseReal(&token); - continue; - } - - // illum model - if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { - token += 6; - material.illum = parseInt(&token); - continue; - } - - // dissolve - if ((token[0] == 'd' && IS_SPACE(token[1]))) { - token += 1; - material.dissolve = parseReal(&token); - - if (has_tr) { - ss << "WARN: Both `d` and `Tr` parameters defined for \"" - << material.name << "\". Use the value of `d` for dissolve." - << std::endl; - } - has_d = true; - continue; - } - if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - if (has_d) { - // `d` wins. Ignore `Tr` value. - ss << "WARN: Both `d` and `Tr` parameters defined for \"" - << material.name << "\". Use the value of `d` for dissolve." - << std::endl; - } else { - // We invert value of Tr(assume Tr is in range [0, 1]) - // NOTE: Interpretation of Tr is application(exporter) dependent. For - // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) - material.dissolve = 1.0f - parseReal(&token); - } - has_tr = true; - continue; - } - - // PBR: roughness - if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - material.roughness = parseReal(&token); - continue; - } - - // PBR: metallic - if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { - token += 2; - material.metallic = parseReal(&token); - continue; - } - - // PBR: sheen - if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.sheen = parseReal(&token); - continue; - } - - // PBR: clearcoat thickness - if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { - token += 2; - material.clearcoat_thickness = parseReal(&token); - continue; - } - - // PBR: clearcoat roughness - if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { - token += 4; - material.clearcoat_roughness = parseReal(&token); - continue; - } - - // PBR: anisotropy - if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { - token += 6; - material.anisotropy = parseReal(&token); - continue; - } - - // PBR: anisotropy rotation - if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { - token += 7; - material.anisotropy_rotation = parseReal(&token); - continue; - } - - // ambient texture - if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.ambient_texname), - &(material.ambient_texopt), token, - /* is_bump */ false); - continue; - } - - // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.diffuse_texname), - &(material.diffuse_texopt), token, - /* is_bump */ false); - continue; - } - - // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_texname), - &(material.specular_texopt), token, - /* is_bump */ false); - continue; - } - - // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_highlight_texname), - &(material.specular_highlight_texopt), token, - /* is_bump */ false); - continue; - } - - // bump texture - if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token, - /* is_bump */ true); - continue; - } - - // bump texture - if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token, - /* is_bump */ true); - continue; - } - - // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { - token += 6; - material.alpha_texname = token; - ParseTextureNameAndOption(&(material.alpha_texname), - &(material.alpha_texopt), token, - /* is_bump */ false); - continue; - } - - // displacement texture - if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.displacement_texname), - &(material.displacement_texopt), token, - /* is_bump */ false); - continue; - } - - // PBR: roughness texture - if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.roughness_texname), - &(material.roughness_texopt), token, - /* is_bump */ false); - continue; - } - - // PBR: metallic texture - if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.metallic_texname), - &(material.metallic_texopt), token, - /* is_bump */ false); - continue; - } - - // PBR: sheen texture - if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.sheen_texname), - &(material.sheen_texopt), token, - /* is_bump */ false); - continue; - } - - // PBR: emissive texture - if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.emissive_texname), - &(material.emissive_texopt), token, - /* is_bump */ false); - continue; - } - - // PBR: normal map texture - if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption( - &(material.normal_texname), &(material.normal_texopt), token, - /* is_bump */ false); // @fixme { is_bump will be true? } - continue; - } - - // unknown parameter - const char *_space = strchr(token, ' '); - if (!_space) { - _space = strchr(token, '\t'); - } - if (_space) { - std::ptrdiff_t len = _space - token; - std::string key(token, static_cast(len)); - std::string value = _space + 1; - material.unknown_parameter.insert( - std::pair(key, value)); - } - } - // flush last material. - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - - if (warning) { - (*warning) = ss.str(); - } -} - -bool MaterialFileReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *err) { - std::string filepath; - - if (!m_mtlBaseDir.empty()) { - filepath = std::string(m_mtlBaseDir) + matId; - } else { - filepath = matId; - } - - std::ifstream matIStream(filepath.c_str()); - if (!matIStream) { - std::stringstream ss; - ss << "WARN: Material file [ " << filepath << " ] not found." << std::endl; - if (err) { - (*err) += ss.str(); - } - return false; - } - - std::string warning; - LoadMtl(matMap, materials, &matIStream, &warning); - - if (!warning.empty()) { - if (err) { - (*err) += warning; - } - } - - return true; -} - -bool MaterialStreamReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *err) { - (void)matId; - if (!m_inStream) { - std::stringstream ss; - ss << "WARN: Material stream in error state. " << std::endl; - if (err) { - (*err) += ss.str(); - } - return false; - } - - std::string warning; - LoadMtl(matMap, materials, &m_inStream, &warning); - - if (!warning.empty()) { - if (err) { - (*err) += warning; - } - } - - return true; -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - const char *filename, const char *mtl_basedir, bool trianglulate) { - attrib->vertices.clear(); - attrib->normals.clear(); - attrib->texcoords.clear(); - shapes->clear(); - - std::stringstream errss; - - std::ifstream ifs(filename); - if (!ifs) { - errss << "Cannot open file [" << filename << "]" << std::endl; - if (err) { - (*err) = errss.str(); - } - return false; - } - - std::string baseDir; - if (mtl_basedir) { - baseDir = mtl_basedir; - } - MaterialFileReader matFileReader(baseDir); - - return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, - trianglulate); -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - std::istream *inStream, MaterialReader *readMatFn /*= NULL*/, - bool triangulate) { - std::stringstream errss; - - std::vector v; - std::vector vn; - std::vector vt; - std::vector tags; - std::vector > faceGroup; - std::string name; - - // material - std::map material_map; - int material = -1; - - shape_t shape; - - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - v.push_back(x); - v.push_back(y); - v.push_back(z); - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - vn.push_back(x); - vn.push_back(y); - vn.push_back(z); - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y; - parseReal2(&x, &y, &token); - vt.push_back(x); - vt.push_back(y); - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - std::vector face; - face.reserve(3); - - while (!IS_NEW_LINE(token[0])) { - vertex_index vi = parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2)); - face.push_back(vi); - size_t n = strspn(token, " \t\r"); - token += n; - } - - // replace with emplace_back + std::move on C++11 - faceGroup.push_back(std::vector()); - faceGroup[faceGroup.size() - 1].swap(face); - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - - int newMaterialId = -1; - if (material_map.find(namebuf) != material_map.end()) { - newMaterialId = material_map[namebuf]; - } else { - // { error!! material not found } - } - - if (newMaterialId != material) { - // Create per-face material. Thus we don't add `shape` to `shapes` at - // this time. - // just clear `faceGroup` after `exportFaceGroupToShape()` call. - exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); - faceGroup.clear(); - material = newMaterialId; - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', filenames); - - if (filenames.empty()) { - if (err) { - (*err) += - "WARN: Looks like empty filename for mtllib. Use default " - "material. \n"; - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), materials, - &material_map, &err_mtl); - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; // This should be warn message. - } - - if (ok) { - found = true; - break; - } - } - - if (!found) { - if (err) { - (*err) += - "WARN: Failed to load material file(s). Use default " - "material.\n"; - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); - if (ret) { - shapes->push_back(shape); - } - - shape = shape_t(); - - // material = -1; - faceGroup.clear(); - - std::vector names; - names.reserve(2); - - while (!IS_NEW_LINE(token[0])) { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - assert(names.size() > 0); - - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; - } else { - name = ""; - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); - if (ret) { - shapes->push_back(shape); - } - - // material = -1; - faceGroup.clear(); - shape = shape_t(); - - // @todo { multiple object name? } - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - name = std::string(namebuf); - - continue; - } - - if (token[0] == 't' && IS_SPACE(token[1])) { - tag_t tag; - - char namebuf[4096]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - tag.name = std::string(namebuf); - - token += tag.name.size() + 1; - - tag_sizes ts = parseTagTriple(&token); - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - char stringValueBuffer[4096]; - -#ifdef _MSC_VER - sscanf_s(token, "%s", stringValueBuffer, - (unsigned)_countof(stringValueBuffer)); -#else - std::sscanf(token, "%s", stringValueBuffer); -#endif - tag.stringValues[i] = stringValueBuffer; - token += tag.stringValues[i].size() + 1; - } - - tags.push_back(tag); - } - - // Ignore unknown command. - } - - bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); - // exportFaceGroupToShape return false when `usemtl` is called in the last - // line. - // we also add `shape` to `shapes` when `shape.mesh` has already some - // faces(indices) - if (ret || shape.mesh.indices.size()) { - shapes->push_back(shape); - } - faceGroup.clear(); // for safety - - if (err) { - (*err) += errss.str(); - } - - attrib->vertices.swap(v); - attrib->normals.swap(vn); - attrib->texcoords.swap(vt); - - return true; -} - -bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data /*= NULL*/, - MaterialReader *readMatFn /*= NULL*/, - std::string *err /*= NULL*/) { - std::stringstream errss; - - // material - std::map material_map; - int material_id = -1; // -1 = invalid - - std::vector indices; - std::vector materials; - std::vector names; - names.reserve(2); - std::string name; - std::vector names_out; - - std::string linebuf; - while (inStream.peek() != -1) { - safeGetline(inStream, linebuf); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - real_t x, y, z, w; // w is optional. default = 1.0 - parseV(&x, &y, &z, &w, &token); - if (callback.vertex_cb) { - callback.vertex_cb(user_data, x, y, z, w); - } - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - if (callback.normal_cb) { - callback.normal_cb(user_data, x, y, z); - } - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; // y and z are optional. default = 0.0 - parseReal3(&x, &y, &z, &token); - if (callback.texcoord_cb) { - callback.texcoord_cb(user_data, x, y, z); - } - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - indices.clear(); - while (!IS_NEW_LINE(token[0])) { - vertex_index vi = parseRawTriple(&token); - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - indices.push_back(idx); - size_t n = strspn(token, " \t\r"); - token += n; - } - - if (callback.index_cb && indices.size() > 0) { - callback.index_cb(user_data, &indices.at(0), - static_cast(indices.size())); - } - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, - static_cast(_countof(namebuf))); -#else - std::sscanf(token, "%s", namebuf); -#endif - - int newMaterialId = -1; - if (material_map.find(namebuf) != material_map.end()) { - newMaterialId = material_map[namebuf]; - } else { - // { error!! material not found } - } - - if (newMaterialId != material_id) { - material_id = newMaterialId; - } - - if (callback.usemtl_cb) { - callback.usemtl_cb(user_data, namebuf, material_id); - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', filenames); - - if (filenames.empty()) { - if (err) { - (*err) += - "WARN: Looks like empty filename for mtllib. Use default " - "material. \n"; - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), &materials, - &material_map, &err_mtl); - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; // This should be warn message. - } - - if (ok) { - found = true; - break; - } - } - - if (!found) { - if (err) { - (*err) += - "WARN: Failed to load material file(s). Use default " - "material.\n"; - } - } else { - if (callback.mtllib_cb) { - callback.mtllib_cb(user_data, &materials.at(0), - static_cast(materials.size())); - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - names.clear(); - - while (!IS_NEW_LINE(token[0])) { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - assert(names.size() > 0); - - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; - } else { - name.clear(); - } - - if (callback.group_cb) { - if (names.size() > 1) { - // create const char* array. - names_out.resize(names.size() - 1); - for (size_t j = 0; j < names_out.size(); j++) { - names_out[j] = names[j + 1].c_str(); - } - callback.group_cb(user_data, &names_out.at(0), - static_cast(names_out.size())); - - } else { - callback.group_cb(user_data, NULL, 0); - } - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // @todo { multiple object name? } - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - std::string object_name = std::string(namebuf); - - if (callback.object_cb) { - callback.object_cb(user_data, object_name.c_str()); - } - - continue; - } - -#if 0 // @todo - if (token[0] == 't' && IS_SPACE(token[1])) { - tag_t tag; - - char namebuf[4096]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - tag.name = std::string(namebuf); - - token += tag.name.size() + 1; - - tag_sizes ts = parseTagTriple(&token); - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - char stringValueBuffer[4096]; - -#ifdef _MSC_VER - sscanf_s(token, "%s", stringValueBuffer, - (unsigned)_countof(stringValueBuffer)); -#else - std::sscanf(token, "%s", stringValueBuffer); -#endif - tag.stringValues[i] = stringValueBuffer; - token += tag.stringValues[i].size() + 1; - } - - tags.push_back(tag); - } -#endif - - // Ignore unknown command. - } - - if (err) { - (*err) += errss.str(); - } - - return true; -} -} // namespace tinyobj - -#endif diff --git a/lib/volk.c b/lib/volk.c new file mode 100644 index 0000000..dfcdf41 --- /dev/null +++ b/lib/volk.c @@ -0,0 +1,3221 @@ +/* This file is part of volk library; see volk.h for version/license details */ +/* clang-format off */ +#include "volk.h" + +#ifdef _WIN32 + typedef const char* LPCSTR; + typedef struct HINSTANCE__* HINSTANCE; + typedef HINSTANCE HMODULE; + #if defined(_MINWINDEF_) + /* minwindef.h defines FARPROC, and attempting to redefine it may conflict with -Wstrict-prototypes */ + #elif defined(_WIN64) + typedef __int64 (__stdcall* FARPROC)(void); + #else + typedef int (__stdcall* FARPROC)(void); + #endif +#else +# include +#endif + +#ifdef __APPLE__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +__declspec(dllimport) HMODULE __stdcall LoadLibraryA(LPCSTR); +__declspec(dllimport) FARPROC __stdcall GetProcAddress(HMODULE, LPCSTR); +__declspec(dllimport) int __stdcall FreeLibrary(HMODULE); +#endif + +#if defined(__GNUC__) +# define VOLK_DISABLE_GCC_PEDANTIC_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") +# define VOLK_RESTORE_GCC_PEDANTIC_WARNINGS \ + _Pragma("GCC diagnostic pop") +#else +# define VOLK_DISABLE_GCC_PEDANTIC_WARNINGS +# define VOLK_RESTORE_GCC_PEDANTIC_WARNINGS +#endif + +static void* loadedModule = NULL; +static VkInstance loadedInstance = VK_NULL_HANDLE; +static VkDevice loadedDevice = VK_NULL_HANDLE; + +static void volkGenLoadLoader(void* context, PFN_vkVoidFunction (*load)(void*, const char*)); +static void volkGenLoadInstance(void* context, PFN_vkVoidFunction (*load)(void*, const char*)); +static void volkGenLoadDevice(void* context, PFN_vkVoidFunction (*load)(void*, const char*)); +static void volkGenLoadDeviceTable(struct VolkDeviceTable* table, void* context, PFN_vkVoidFunction (*load)(void*, const char*)); + +static PFN_vkVoidFunction vkGetInstanceProcAddrStub(void* context, const char* name) +{ + return vkGetInstanceProcAddr((VkInstance)context, name); +} + +static PFN_vkVoidFunction vkGetDeviceProcAddrStub(void* context, const char* name) +{ + return vkGetDeviceProcAddr((VkDevice)context, name); +} + +static PFN_vkVoidFunction nullProcAddrStub(void* context, const char* name) +{ + (void)context; + (void)name; + return NULL; +} + +VkResult volkInitialize(void) +{ +#if defined(_WIN32) + HMODULE module = LoadLibraryA("vulkan-1.dll"); + if (!module) + return VK_ERROR_INITIALIZATION_FAILED; + + // note: function pointer is cast through void function pointer to silence cast-function-type warning on gcc8 + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)(void(*)(void))GetProcAddress(module, "vkGetInstanceProcAddr"); +#elif defined(__APPLE__) + void* module = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); + if (!module) + module = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL); + if (!module) + module = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL); + // Add support for using Vulkan and MoltenVK in a Framework. App store rules for iOS + // strictly enforce no .dylib's. If they aren't found it just falls through + if (!module) + module = dlopen("vulkan.framework/vulkan", RTLD_NOW | RTLD_LOCAL); + if (!module) + module = dlopen("MoltenVK.framework/MoltenVK", RTLD_NOW | RTLD_LOCAL); + // modern versions of macOS don't search /usr/local/lib automatically contrary to what man dlopen says + // Vulkan SDK uses this as the system-wide installation location, so we're going to fallback to this if all else fails + if (!module && getenv("DYLD_FALLBACK_LIBRARY_PATH") == NULL) + module = dlopen("/usr/local/lib/libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); + if (!module) + return VK_ERROR_INITIALIZATION_FAILED; + + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(module, "vkGetInstanceProcAddr"); +#else + void* module = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); + if (!module) + module = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); + if (!module) + return VK_ERROR_INITIALIZATION_FAILED; + VOLK_DISABLE_GCC_PEDANTIC_WARNINGS + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(module, "vkGetInstanceProcAddr"); + VOLK_RESTORE_GCC_PEDANTIC_WARNINGS +#endif + + loadedModule = module; + volkGenLoadLoader(NULL, vkGetInstanceProcAddrStub); + + return VK_SUCCESS; +} + +void volkInitializeCustom(PFN_vkGetInstanceProcAddr handler) +{ + vkGetInstanceProcAddr = handler; + + loadedModule = NULL; + volkGenLoadLoader(NULL, vkGetInstanceProcAddrStub); +} + +void volkFinalize(void) +{ + if (loadedModule) + { +#if defined(_WIN32) + FreeLibrary((HMODULE)loadedModule); +#else + dlclose(loadedModule); +#endif + } + + vkGetInstanceProcAddr = NULL; + volkGenLoadLoader(NULL, nullProcAddrStub); + volkGenLoadInstance(NULL, nullProcAddrStub); + volkGenLoadDevice(NULL, nullProcAddrStub); + + loadedModule = NULL; + loadedInstance = VK_NULL_HANDLE; + loadedDevice = VK_NULL_HANDLE; +} + +uint32_t volkGetInstanceVersion(void) +{ +#if defined(VK_VERSION_1_1) + uint32_t apiVersion = 0; + if (vkEnumerateInstanceVersion && vkEnumerateInstanceVersion(&apiVersion) == VK_SUCCESS) + return apiVersion; +#endif + + if (vkCreateInstance) + return VK_API_VERSION_1_0; + + return 0; +} + +void volkLoadInstance(VkInstance instance) +{ + loadedInstance = instance; + volkGenLoadInstance(instance, vkGetInstanceProcAddrStub); + volkGenLoadDevice(instance, vkGetInstanceProcAddrStub); +} + +void volkLoadInstanceOnly(VkInstance instance) +{ + loadedInstance = instance; + volkGenLoadInstance(instance, vkGetInstanceProcAddrStub); +} + +VkInstance volkGetLoadedInstance(void) +{ + return loadedInstance; +} + +void volkLoadDevice(VkDevice device) +{ + loadedDevice = device; + volkGenLoadDevice(device, vkGetDeviceProcAddrStub); +} + +VkDevice volkGetLoadedDevice(void) +{ + return loadedDevice; +} + +void volkLoadDeviceTable(struct VolkDeviceTable* table, VkDevice device) +{ + volkGenLoadDeviceTable(table, device, vkGetDeviceProcAddrStub); +} + +static void volkGenLoadLoader(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) +{ + /* VOLK_GENERATE_LOAD_LOADER */ +#if defined(VK_VERSION_1_0) + vkCreateInstance = (PFN_vkCreateInstance)load(context, "vkCreateInstance"); + vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)load(context, "vkEnumerateInstanceExtensionProperties"); + vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)load(context, "vkEnumerateInstanceLayerProperties"); +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) + vkEnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion)load(context, "vkEnumerateInstanceVersion"); +#endif /* defined(VK_VERSION_1_1) */ + /* VOLK_GENERATE_LOAD_LOADER */ +} + +static void volkGenLoadInstance(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) +{ + /* VOLK_GENERATE_LOAD_INSTANCE */ +#if defined(VK_VERSION_1_0) + vkCreateDevice = (PFN_vkCreateDevice)load(context, "vkCreateDevice"); + vkDestroyInstance = (PFN_vkDestroyInstance)load(context, "vkDestroyInstance"); + vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties)load(context, "vkEnumerateDeviceExtensionProperties"); + vkEnumerateDeviceLayerProperties = (PFN_vkEnumerateDeviceLayerProperties)load(context, "vkEnumerateDeviceLayerProperties"); + vkEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)load(context, "vkEnumeratePhysicalDevices"); + vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)load(context, "vkGetDeviceProcAddr"); + vkGetPhysicalDeviceFeatures = (PFN_vkGetPhysicalDeviceFeatures)load(context, "vkGetPhysicalDeviceFeatures"); + vkGetPhysicalDeviceFormatProperties = (PFN_vkGetPhysicalDeviceFormatProperties)load(context, "vkGetPhysicalDeviceFormatProperties"); + vkGetPhysicalDeviceImageFormatProperties = (PFN_vkGetPhysicalDeviceImageFormatProperties)load(context, "vkGetPhysicalDeviceImageFormatProperties"); + vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)load(context, "vkGetPhysicalDeviceMemoryProperties"); + vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)load(context, "vkGetPhysicalDeviceProperties"); + vkGetPhysicalDeviceQueueFamilyProperties = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)load(context, "vkGetPhysicalDeviceQueueFamilyProperties"); + vkGetPhysicalDeviceSparseImageFormatProperties = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties)load(context, "vkGetPhysicalDeviceSparseImageFormatProperties"); +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) + vkEnumeratePhysicalDeviceGroups = (PFN_vkEnumeratePhysicalDeviceGroups)load(context, "vkEnumeratePhysicalDeviceGroups"); + vkGetPhysicalDeviceExternalBufferProperties = (PFN_vkGetPhysicalDeviceExternalBufferProperties)load(context, "vkGetPhysicalDeviceExternalBufferProperties"); + vkGetPhysicalDeviceExternalFenceProperties = (PFN_vkGetPhysicalDeviceExternalFenceProperties)load(context, "vkGetPhysicalDeviceExternalFenceProperties"); + vkGetPhysicalDeviceExternalSemaphoreProperties = (PFN_vkGetPhysicalDeviceExternalSemaphoreProperties)load(context, "vkGetPhysicalDeviceExternalSemaphoreProperties"); + vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2)load(context, "vkGetPhysicalDeviceFeatures2"); + vkGetPhysicalDeviceFormatProperties2 = (PFN_vkGetPhysicalDeviceFormatProperties2)load(context, "vkGetPhysicalDeviceFormatProperties2"); + vkGetPhysicalDeviceImageFormatProperties2 = (PFN_vkGetPhysicalDeviceImageFormatProperties2)load(context, "vkGetPhysicalDeviceImageFormatProperties2"); + vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2)load(context, "vkGetPhysicalDeviceMemoryProperties2"); + vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2)load(context, "vkGetPhysicalDeviceProperties2"); + vkGetPhysicalDeviceQueueFamilyProperties2 = (PFN_vkGetPhysicalDeviceQueueFamilyProperties2)load(context, "vkGetPhysicalDeviceQueueFamilyProperties2"); + vkGetPhysicalDeviceSparseImageFormatProperties2 = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties2)load(context, "vkGetPhysicalDeviceSparseImageFormatProperties2"); +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_3) + vkGetPhysicalDeviceToolProperties = (PFN_vkGetPhysicalDeviceToolProperties)load(context, "vkGetPhysicalDeviceToolProperties"); +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_EXT_acquire_drm_display) + vkAcquireDrmDisplayEXT = (PFN_vkAcquireDrmDisplayEXT)load(context, "vkAcquireDrmDisplayEXT"); + vkGetDrmDisplayEXT = (PFN_vkGetDrmDisplayEXT)load(context, "vkGetDrmDisplayEXT"); +#endif /* defined(VK_EXT_acquire_drm_display) */ +#if defined(VK_EXT_acquire_xlib_display) + vkAcquireXlibDisplayEXT = (PFN_vkAcquireXlibDisplayEXT)load(context, "vkAcquireXlibDisplayEXT"); + vkGetRandROutputDisplayEXT = (PFN_vkGetRandROutputDisplayEXT)load(context, "vkGetRandROutputDisplayEXT"); +#endif /* defined(VK_EXT_acquire_xlib_display) */ +#if defined(VK_EXT_calibrated_timestamps) + vkGetPhysicalDeviceCalibrateableTimeDomainsEXT = (PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)load(context, "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT"); +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_debug_report) + vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)load(context, "vkCreateDebugReportCallbackEXT"); + vkDebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)load(context, "vkDebugReportMessageEXT"); + vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)load(context, "vkDestroyDebugReportCallbackEXT"); +#endif /* defined(VK_EXT_debug_report) */ +#if defined(VK_EXT_debug_utils) + vkCmdBeginDebugUtilsLabelEXT = (PFN_vkCmdBeginDebugUtilsLabelEXT)load(context, "vkCmdBeginDebugUtilsLabelEXT"); + vkCmdEndDebugUtilsLabelEXT = (PFN_vkCmdEndDebugUtilsLabelEXT)load(context, "vkCmdEndDebugUtilsLabelEXT"); + vkCmdInsertDebugUtilsLabelEXT = (PFN_vkCmdInsertDebugUtilsLabelEXT)load(context, "vkCmdInsertDebugUtilsLabelEXT"); + vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)load(context, "vkCreateDebugUtilsMessengerEXT"); + vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)load(context, "vkDestroyDebugUtilsMessengerEXT"); + vkQueueBeginDebugUtilsLabelEXT = (PFN_vkQueueBeginDebugUtilsLabelEXT)load(context, "vkQueueBeginDebugUtilsLabelEXT"); + vkQueueEndDebugUtilsLabelEXT = (PFN_vkQueueEndDebugUtilsLabelEXT)load(context, "vkQueueEndDebugUtilsLabelEXT"); + vkQueueInsertDebugUtilsLabelEXT = (PFN_vkQueueInsertDebugUtilsLabelEXT)load(context, "vkQueueInsertDebugUtilsLabelEXT"); + vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)load(context, "vkSetDebugUtilsObjectNameEXT"); + vkSetDebugUtilsObjectTagEXT = (PFN_vkSetDebugUtilsObjectTagEXT)load(context, "vkSetDebugUtilsObjectTagEXT"); + vkSubmitDebugUtilsMessageEXT = (PFN_vkSubmitDebugUtilsMessageEXT)load(context, "vkSubmitDebugUtilsMessageEXT"); +#endif /* defined(VK_EXT_debug_utils) */ +#if defined(VK_EXT_direct_mode_display) + vkReleaseDisplayEXT = (PFN_vkReleaseDisplayEXT)load(context, "vkReleaseDisplayEXT"); +#endif /* defined(VK_EXT_direct_mode_display) */ +#if defined(VK_EXT_directfb_surface) + vkCreateDirectFBSurfaceEXT = (PFN_vkCreateDirectFBSurfaceEXT)load(context, "vkCreateDirectFBSurfaceEXT"); + vkGetPhysicalDeviceDirectFBPresentationSupportEXT = (PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT)load(context, "vkGetPhysicalDeviceDirectFBPresentationSupportEXT"); +#endif /* defined(VK_EXT_directfb_surface) */ +#if defined(VK_EXT_display_surface_counter) + vkGetPhysicalDeviceSurfaceCapabilities2EXT = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)load(context, "vkGetPhysicalDeviceSurfaceCapabilities2EXT"); +#endif /* defined(VK_EXT_display_surface_counter) */ +#if defined(VK_EXT_full_screen_exclusive) + vkGetPhysicalDeviceSurfacePresentModes2EXT = (PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT)load(context, "vkGetPhysicalDeviceSurfacePresentModes2EXT"); +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_headless_surface) + vkCreateHeadlessSurfaceEXT = (PFN_vkCreateHeadlessSurfaceEXT)load(context, "vkCreateHeadlessSurfaceEXT"); +#endif /* defined(VK_EXT_headless_surface) */ +#if defined(VK_EXT_metal_surface) + vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)load(context, "vkCreateMetalSurfaceEXT"); +#endif /* defined(VK_EXT_metal_surface) */ +#if defined(VK_EXT_sample_locations) + vkGetPhysicalDeviceMultisamplePropertiesEXT = (PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)load(context, "vkGetPhysicalDeviceMultisamplePropertiesEXT"); +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_tooling_info) + vkGetPhysicalDeviceToolPropertiesEXT = (PFN_vkGetPhysicalDeviceToolPropertiesEXT)load(context, "vkGetPhysicalDeviceToolPropertiesEXT"); +#endif /* defined(VK_EXT_tooling_info) */ +#if defined(VK_FUCHSIA_imagepipe_surface) + vkCreateImagePipeSurfaceFUCHSIA = (PFN_vkCreateImagePipeSurfaceFUCHSIA)load(context, "vkCreateImagePipeSurfaceFUCHSIA"); +#endif /* defined(VK_FUCHSIA_imagepipe_surface) */ +#if defined(VK_GGP_stream_descriptor_surface) + vkCreateStreamDescriptorSurfaceGGP = (PFN_vkCreateStreamDescriptorSurfaceGGP)load(context, "vkCreateStreamDescriptorSurfaceGGP"); +#endif /* defined(VK_GGP_stream_descriptor_surface) */ +#if defined(VK_KHR_android_surface) + vkCreateAndroidSurfaceKHR = (PFN_vkCreateAndroidSurfaceKHR)load(context, "vkCreateAndroidSurfaceKHR"); +#endif /* defined(VK_KHR_android_surface) */ +#if defined(VK_KHR_calibrated_timestamps) + vkGetPhysicalDeviceCalibrateableTimeDomainsKHR = (PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR)load(context, "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR"); +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_cooperative_matrix) + vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR = (PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR)load(context, "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR"); +#endif /* defined(VK_KHR_cooperative_matrix) */ +#if defined(VK_KHR_device_group_creation) + vkEnumeratePhysicalDeviceGroupsKHR = (PFN_vkEnumeratePhysicalDeviceGroupsKHR)load(context, "vkEnumeratePhysicalDeviceGroupsKHR"); +#endif /* defined(VK_KHR_device_group_creation) */ +#if defined(VK_KHR_display) + vkCreateDisplayModeKHR = (PFN_vkCreateDisplayModeKHR)load(context, "vkCreateDisplayModeKHR"); + vkCreateDisplayPlaneSurfaceKHR = (PFN_vkCreateDisplayPlaneSurfaceKHR)load(context, "vkCreateDisplayPlaneSurfaceKHR"); + vkGetDisplayModePropertiesKHR = (PFN_vkGetDisplayModePropertiesKHR)load(context, "vkGetDisplayModePropertiesKHR"); + vkGetDisplayPlaneCapabilitiesKHR = (PFN_vkGetDisplayPlaneCapabilitiesKHR)load(context, "vkGetDisplayPlaneCapabilitiesKHR"); + vkGetDisplayPlaneSupportedDisplaysKHR = (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)load(context, "vkGetDisplayPlaneSupportedDisplaysKHR"); + vkGetPhysicalDeviceDisplayPlanePropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)load(context, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR"); + vkGetPhysicalDeviceDisplayPropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)load(context, "vkGetPhysicalDeviceDisplayPropertiesKHR"); +#endif /* defined(VK_KHR_display) */ +#if defined(VK_KHR_external_fence_capabilities) + vkGetPhysicalDeviceExternalFencePropertiesKHR = (PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR)load(context, "vkGetPhysicalDeviceExternalFencePropertiesKHR"); +#endif /* defined(VK_KHR_external_fence_capabilities) */ +#if defined(VK_KHR_external_memory_capabilities) + vkGetPhysicalDeviceExternalBufferPropertiesKHR = (PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR)load(context, "vkGetPhysicalDeviceExternalBufferPropertiesKHR"); +#endif /* defined(VK_KHR_external_memory_capabilities) */ +#if defined(VK_KHR_external_semaphore_capabilities) + vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = (PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)load(context, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"); +#endif /* defined(VK_KHR_external_semaphore_capabilities) */ +#if defined(VK_KHR_fragment_shading_rate) + vkGetPhysicalDeviceFragmentShadingRatesKHR = (PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR)load(context, "vkGetPhysicalDeviceFragmentShadingRatesKHR"); +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_display_properties2) + vkGetDisplayModeProperties2KHR = (PFN_vkGetDisplayModeProperties2KHR)load(context, "vkGetDisplayModeProperties2KHR"); + vkGetDisplayPlaneCapabilities2KHR = (PFN_vkGetDisplayPlaneCapabilities2KHR)load(context, "vkGetDisplayPlaneCapabilities2KHR"); + vkGetPhysicalDeviceDisplayPlaneProperties2KHR = (PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR)load(context, "vkGetPhysicalDeviceDisplayPlaneProperties2KHR"); + vkGetPhysicalDeviceDisplayProperties2KHR = (PFN_vkGetPhysicalDeviceDisplayProperties2KHR)load(context, "vkGetPhysicalDeviceDisplayProperties2KHR"); +#endif /* defined(VK_KHR_get_display_properties2) */ +#if defined(VK_KHR_get_physical_device_properties2) + vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR)load(context, "vkGetPhysicalDeviceFeatures2KHR"); + vkGetPhysicalDeviceFormatProperties2KHR = (PFN_vkGetPhysicalDeviceFormatProperties2KHR)load(context, "vkGetPhysicalDeviceFormatProperties2KHR"); + vkGetPhysicalDeviceImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)load(context, "vkGetPhysicalDeviceImageFormatProperties2KHR"); + vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR)load(context, "vkGetPhysicalDeviceMemoryProperties2KHR"); + vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR)load(context, "vkGetPhysicalDeviceProperties2KHR"); + vkGetPhysicalDeviceQueueFamilyProperties2KHR = (PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)load(context, "vkGetPhysicalDeviceQueueFamilyProperties2KHR"); + vkGetPhysicalDeviceSparseImageFormatProperties2KHR = (PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)load(context, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR"); +#endif /* defined(VK_KHR_get_physical_device_properties2) */ +#if defined(VK_KHR_get_surface_capabilities2) + vkGetPhysicalDeviceSurfaceCapabilities2KHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)load(context, "vkGetPhysicalDeviceSurfaceCapabilities2KHR"); + vkGetPhysicalDeviceSurfaceFormats2KHR = (PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)load(context, "vkGetPhysicalDeviceSurfaceFormats2KHR"); +#endif /* defined(VK_KHR_get_surface_capabilities2) */ +#if defined(VK_KHR_performance_query) + vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR = (PFN_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR)load(context, "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR"); + vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR = (PFN_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR)load(context, "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR"); +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_surface) + vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)load(context, "vkDestroySurfaceKHR"); + vkGetPhysicalDeviceSurfaceCapabilitiesKHR = (PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)load(context, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + vkGetPhysicalDeviceSurfaceFormatsKHR = (PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)load(context, "vkGetPhysicalDeviceSurfaceFormatsKHR"); + vkGetPhysicalDeviceSurfacePresentModesKHR = (PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)load(context, "vkGetPhysicalDeviceSurfacePresentModesKHR"); + vkGetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR)load(context, "vkGetPhysicalDeviceSurfaceSupportKHR"); +#endif /* defined(VK_KHR_surface) */ +#if defined(VK_KHR_video_encode_queue) + vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR = (PFN_vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR)load(context, "vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR"); +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) + vkGetPhysicalDeviceVideoCapabilitiesKHR = (PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR)load(context, "vkGetPhysicalDeviceVideoCapabilitiesKHR"); + vkGetPhysicalDeviceVideoFormatPropertiesKHR = (PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR)load(context, "vkGetPhysicalDeviceVideoFormatPropertiesKHR"); +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_KHR_wayland_surface) + vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)load(context, "vkCreateWaylandSurfaceKHR"); + vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)load(context, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); +#endif /* defined(VK_KHR_wayland_surface) */ +#if defined(VK_KHR_win32_surface) + vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)load(context, "vkCreateWin32SurfaceKHR"); + vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)load(context, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); +#endif /* defined(VK_KHR_win32_surface) */ +#if defined(VK_KHR_xcb_surface) + vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)load(context, "vkCreateXcbSurfaceKHR"); + vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)load(context, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); +#endif /* defined(VK_KHR_xcb_surface) */ +#if defined(VK_KHR_xlib_surface) + vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)load(context, "vkCreateXlibSurfaceKHR"); + vkGetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)load(context, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); +#endif /* defined(VK_KHR_xlib_surface) */ +#if defined(VK_MVK_ios_surface) + vkCreateIOSSurfaceMVK = (PFN_vkCreateIOSSurfaceMVK)load(context, "vkCreateIOSSurfaceMVK"); +#endif /* defined(VK_MVK_ios_surface) */ +#if defined(VK_MVK_macos_surface) + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)load(context, "vkCreateMacOSSurfaceMVK"); +#endif /* defined(VK_MVK_macos_surface) */ +#if defined(VK_NN_vi_surface) + vkCreateViSurfaceNN = (PFN_vkCreateViSurfaceNN)load(context, "vkCreateViSurfaceNN"); +#endif /* defined(VK_NN_vi_surface) */ +#if defined(VK_NV_acquire_winrt_display) + vkAcquireWinrtDisplayNV = (PFN_vkAcquireWinrtDisplayNV)load(context, "vkAcquireWinrtDisplayNV"); + vkGetWinrtDisplayNV = (PFN_vkGetWinrtDisplayNV)load(context, "vkGetWinrtDisplayNV"); +#endif /* defined(VK_NV_acquire_winrt_display) */ +#if defined(VK_NV_cooperative_matrix) + vkGetPhysicalDeviceCooperativeMatrixPropertiesNV = (PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)load(context, "vkGetPhysicalDeviceCooperativeMatrixPropertiesNV"); +#endif /* defined(VK_NV_cooperative_matrix) */ +#if defined(VK_NV_coverage_reduction_mode) + vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV = (PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV)load(context, "vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV"); +#endif /* defined(VK_NV_coverage_reduction_mode) */ +#if defined(VK_NV_external_memory_capabilities) + vkGetPhysicalDeviceExternalImageFormatPropertiesNV = (PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)load(context, "vkGetPhysicalDeviceExternalImageFormatPropertiesNV"); +#endif /* defined(VK_NV_external_memory_capabilities) */ +#if defined(VK_NV_optical_flow) + vkGetPhysicalDeviceOpticalFlowImageFormatsNV = (PFN_vkGetPhysicalDeviceOpticalFlowImageFormatsNV)load(context, "vkGetPhysicalDeviceOpticalFlowImageFormatsNV"); +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_QNX_screen_surface) + vkCreateScreenSurfaceQNX = (PFN_vkCreateScreenSurfaceQNX)load(context, "vkCreateScreenSurfaceQNX"); + vkGetPhysicalDeviceScreenPresentationSupportQNX = (PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX)load(context, "vkGetPhysicalDeviceScreenPresentationSupportQNX"); +#endif /* defined(VK_QNX_screen_surface) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + vkGetPhysicalDevicePresentRectanglesKHR = (PFN_vkGetPhysicalDevicePresentRectanglesKHR)load(context, "vkGetPhysicalDevicePresentRectanglesKHR"); +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ + /* VOLK_GENERATE_LOAD_INSTANCE */ +} + +static void volkGenLoadDevice(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) +{ + /* VOLK_GENERATE_LOAD_DEVICE */ +#if defined(VK_VERSION_1_0) + vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers)load(context, "vkAllocateCommandBuffers"); + vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets)load(context, "vkAllocateDescriptorSets"); + vkAllocateMemory = (PFN_vkAllocateMemory)load(context, "vkAllocateMemory"); + vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer)load(context, "vkBeginCommandBuffer"); + vkBindBufferMemory = (PFN_vkBindBufferMemory)load(context, "vkBindBufferMemory"); + vkBindImageMemory = (PFN_vkBindImageMemory)load(context, "vkBindImageMemory"); + vkCmdBeginQuery = (PFN_vkCmdBeginQuery)load(context, "vkCmdBeginQuery"); + vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass)load(context, "vkCmdBeginRenderPass"); + vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets)load(context, "vkCmdBindDescriptorSets"); + vkCmdBindIndexBuffer = (PFN_vkCmdBindIndexBuffer)load(context, "vkCmdBindIndexBuffer"); + vkCmdBindPipeline = (PFN_vkCmdBindPipeline)load(context, "vkCmdBindPipeline"); + vkCmdBindVertexBuffers = (PFN_vkCmdBindVertexBuffers)load(context, "vkCmdBindVertexBuffers"); + vkCmdBlitImage = (PFN_vkCmdBlitImage)load(context, "vkCmdBlitImage"); + vkCmdClearAttachments = (PFN_vkCmdClearAttachments)load(context, "vkCmdClearAttachments"); + vkCmdClearColorImage = (PFN_vkCmdClearColorImage)load(context, "vkCmdClearColorImage"); + vkCmdClearDepthStencilImage = (PFN_vkCmdClearDepthStencilImage)load(context, "vkCmdClearDepthStencilImage"); + vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)load(context, "vkCmdCopyBuffer"); + vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage)load(context, "vkCmdCopyBufferToImage"); + vkCmdCopyImage = (PFN_vkCmdCopyImage)load(context, "vkCmdCopyImage"); + vkCmdCopyImageToBuffer = (PFN_vkCmdCopyImageToBuffer)load(context, "vkCmdCopyImageToBuffer"); + vkCmdCopyQueryPoolResults = (PFN_vkCmdCopyQueryPoolResults)load(context, "vkCmdCopyQueryPoolResults"); + vkCmdDispatch = (PFN_vkCmdDispatch)load(context, "vkCmdDispatch"); + vkCmdDispatchIndirect = (PFN_vkCmdDispatchIndirect)load(context, "vkCmdDispatchIndirect"); + vkCmdDraw = (PFN_vkCmdDraw)load(context, "vkCmdDraw"); + vkCmdDrawIndexed = (PFN_vkCmdDrawIndexed)load(context, "vkCmdDrawIndexed"); + vkCmdDrawIndexedIndirect = (PFN_vkCmdDrawIndexedIndirect)load(context, "vkCmdDrawIndexedIndirect"); + vkCmdDrawIndirect = (PFN_vkCmdDrawIndirect)load(context, "vkCmdDrawIndirect"); + vkCmdEndQuery = (PFN_vkCmdEndQuery)load(context, "vkCmdEndQuery"); + vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass)load(context, "vkCmdEndRenderPass"); + vkCmdExecuteCommands = (PFN_vkCmdExecuteCommands)load(context, "vkCmdExecuteCommands"); + vkCmdFillBuffer = (PFN_vkCmdFillBuffer)load(context, "vkCmdFillBuffer"); + vkCmdNextSubpass = (PFN_vkCmdNextSubpass)load(context, "vkCmdNextSubpass"); + vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier)load(context, "vkCmdPipelineBarrier"); + vkCmdPushConstants = (PFN_vkCmdPushConstants)load(context, "vkCmdPushConstants"); + vkCmdResetEvent = (PFN_vkCmdResetEvent)load(context, "vkCmdResetEvent"); + vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool)load(context, "vkCmdResetQueryPool"); + vkCmdResolveImage = (PFN_vkCmdResolveImage)load(context, "vkCmdResolveImage"); + vkCmdSetBlendConstants = (PFN_vkCmdSetBlendConstants)load(context, "vkCmdSetBlendConstants"); + vkCmdSetDepthBias = (PFN_vkCmdSetDepthBias)load(context, "vkCmdSetDepthBias"); + vkCmdSetDepthBounds = (PFN_vkCmdSetDepthBounds)load(context, "vkCmdSetDepthBounds"); + vkCmdSetEvent = (PFN_vkCmdSetEvent)load(context, "vkCmdSetEvent"); + vkCmdSetLineWidth = (PFN_vkCmdSetLineWidth)load(context, "vkCmdSetLineWidth"); + vkCmdSetScissor = (PFN_vkCmdSetScissor)load(context, "vkCmdSetScissor"); + vkCmdSetStencilCompareMask = (PFN_vkCmdSetStencilCompareMask)load(context, "vkCmdSetStencilCompareMask"); + vkCmdSetStencilReference = (PFN_vkCmdSetStencilReference)load(context, "vkCmdSetStencilReference"); + vkCmdSetStencilWriteMask = (PFN_vkCmdSetStencilWriteMask)load(context, "vkCmdSetStencilWriteMask"); + vkCmdSetViewport = (PFN_vkCmdSetViewport)load(context, "vkCmdSetViewport"); + vkCmdUpdateBuffer = (PFN_vkCmdUpdateBuffer)load(context, "vkCmdUpdateBuffer"); + vkCmdWaitEvents = (PFN_vkCmdWaitEvents)load(context, "vkCmdWaitEvents"); + vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp)load(context, "vkCmdWriteTimestamp"); + vkCreateBuffer = (PFN_vkCreateBuffer)load(context, "vkCreateBuffer"); + vkCreateBufferView = (PFN_vkCreateBufferView)load(context, "vkCreateBufferView"); + vkCreateCommandPool = (PFN_vkCreateCommandPool)load(context, "vkCreateCommandPool"); + vkCreateComputePipelines = (PFN_vkCreateComputePipelines)load(context, "vkCreateComputePipelines"); + vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool)load(context, "vkCreateDescriptorPool"); + vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout)load(context, "vkCreateDescriptorSetLayout"); + vkCreateEvent = (PFN_vkCreateEvent)load(context, "vkCreateEvent"); + vkCreateFence = (PFN_vkCreateFence)load(context, "vkCreateFence"); + vkCreateFramebuffer = (PFN_vkCreateFramebuffer)load(context, "vkCreateFramebuffer"); + vkCreateGraphicsPipelines = (PFN_vkCreateGraphicsPipelines)load(context, "vkCreateGraphicsPipelines"); + vkCreateImage = (PFN_vkCreateImage)load(context, "vkCreateImage"); + vkCreateImageView = (PFN_vkCreateImageView)load(context, "vkCreateImageView"); + vkCreatePipelineCache = (PFN_vkCreatePipelineCache)load(context, "vkCreatePipelineCache"); + vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout)load(context, "vkCreatePipelineLayout"); + vkCreateQueryPool = (PFN_vkCreateQueryPool)load(context, "vkCreateQueryPool"); + vkCreateRenderPass = (PFN_vkCreateRenderPass)load(context, "vkCreateRenderPass"); + vkCreateSampler = (PFN_vkCreateSampler)load(context, "vkCreateSampler"); + vkCreateSemaphore = (PFN_vkCreateSemaphore)load(context, "vkCreateSemaphore"); + vkCreateShaderModule = (PFN_vkCreateShaderModule)load(context, "vkCreateShaderModule"); + vkDestroyBuffer = (PFN_vkDestroyBuffer)load(context, "vkDestroyBuffer"); + vkDestroyBufferView = (PFN_vkDestroyBufferView)load(context, "vkDestroyBufferView"); + vkDestroyCommandPool = (PFN_vkDestroyCommandPool)load(context, "vkDestroyCommandPool"); + vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool)load(context, "vkDestroyDescriptorPool"); + vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout)load(context, "vkDestroyDescriptorSetLayout"); + vkDestroyDevice = (PFN_vkDestroyDevice)load(context, "vkDestroyDevice"); + vkDestroyEvent = (PFN_vkDestroyEvent)load(context, "vkDestroyEvent"); + vkDestroyFence = (PFN_vkDestroyFence)load(context, "vkDestroyFence"); + vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer)load(context, "vkDestroyFramebuffer"); + vkDestroyImage = (PFN_vkDestroyImage)load(context, "vkDestroyImage"); + vkDestroyImageView = (PFN_vkDestroyImageView)load(context, "vkDestroyImageView"); + vkDestroyPipeline = (PFN_vkDestroyPipeline)load(context, "vkDestroyPipeline"); + vkDestroyPipelineCache = (PFN_vkDestroyPipelineCache)load(context, "vkDestroyPipelineCache"); + vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout)load(context, "vkDestroyPipelineLayout"); + vkDestroyQueryPool = (PFN_vkDestroyQueryPool)load(context, "vkDestroyQueryPool"); + vkDestroyRenderPass = (PFN_vkDestroyRenderPass)load(context, "vkDestroyRenderPass"); + vkDestroySampler = (PFN_vkDestroySampler)load(context, "vkDestroySampler"); + vkDestroySemaphore = (PFN_vkDestroySemaphore)load(context, "vkDestroySemaphore"); + vkDestroyShaderModule = (PFN_vkDestroyShaderModule)load(context, "vkDestroyShaderModule"); + vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle)load(context, "vkDeviceWaitIdle"); + vkEndCommandBuffer = (PFN_vkEndCommandBuffer)load(context, "vkEndCommandBuffer"); + vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)load(context, "vkFlushMappedMemoryRanges"); + vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers)load(context, "vkFreeCommandBuffers"); + vkFreeDescriptorSets = (PFN_vkFreeDescriptorSets)load(context, "vkFreeDescriptorSets"); + vkFreeMemory = (PFN_vkFreeMemory)load(context, "vkFreeMemory"); + vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)load(context, "vkGetBufferMemoryRequirements"); + vkGetDeviceMemoryCommitment = (PFN_vkGetDeviceMemoryCommitment)load(context, "vkGetDeviceMemoryCommitment"); + vkGetDeviceQueue = (PFN_vkGetDeviceQueue)load(context, "vkGetDeviceQueue"); + vkGetEventStatus = (PFN_vkGetEventStatus)load(context, "vkGetEventStatus"); + vkGetFenceStatus = (PFN_vkGetFenceStatus)load(context, "vkGetFenceStatus"); + vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)load(context, "vkGetImageMemoryRequirements"); + vkGetImageSparseMemoryRequirements = (PFN_vkGetImageSparseMemoryRequirements)load(context, "vkGetImageSparseMemoryRequirements"); + vkGetImageSubresourceLayout = (PFN_vkGetImageSubresourceLayout)load(context, "vkGetImageSubresourceLayout"); + vkGetPipelineCacheData = (PFN_vkGetPipelineCacheData)load(context, "vkGetPipelineCacheData"); + vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults)load(context, "vkGetQueryPoolResults"); + vkGetRenderAreaGranularity = (PFN_vkGetRenderAreaGranularity)load(context, "vkGetRenderAreaGranularity"); + vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)load(context, "vkInvalidateMappedMemoryRanges"); + vkMapMemory = (PFN_vkMapMemory)load(context, "vkMapMemory"); + vkMergePipelineCaches = (PFN_vkMergePipelineCaches)load(context, "vkMergePipelineCaches"); + vkQueueBindSparse = (PFN_vkQueueBindSparse)load(context, "vkQueueBindSparse"); + vkQueueSubmit = (PFN_vkQueueSubmit)load(context, "vkQueueSubmit"); + vkQueueWaitIdle = (PFN_vkQueueWaitIdle)load(context, "vkQueueWaitIdle"); + vkResetCommandBuffer = (PFN_vkResetCommandBuffer)load(context, "vkResetCommandBuffer"); + vkResetCommandPool = (PFN_vkResetCommandPool)load(context, "vkResetCommandPool"); + vkResetDescriptorPool = (PFN_vkResetDescriptorPool)load(context, "vkResetDescriptorPool"); + vkResetEvent = (PFN_vkResetEvent)load(context, "vkResetEvent"); + vkResetFences = (PFN_vkResetFences)load(context, "vkResetFences"); + vkSetEvent = (PFN_vkSetEvent)load(context, "vkSetEvent"); + vkUnmapMemory = (PFN_vkUnmapMemory)load(context, "vkUnmapMemory"); + vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets)load(context, "vkUpdateDescriptorSets"); + vkWaitForFences = (PFN_vkWaitForFences)load(context, "vkWaitForFences"); +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) + vkBindBufferMemory2 = (PFN_vkBindBufferMemory2)load(context, "vkBindBufferMemory2"); + vkBindImageMemory2 = (PFN_vkBindImageMemory2)load(context, "vkBindImageMemory2"); + vkCmdDispatchBase = (PFN_vkCmdDispatchBase)load(context, "vkCmdDispatchBase"); + vkCmdSetDeviceMask = (PFN_vkCmdSetDeviceMask)load(context, "vkCmdSetDeviceMask"); + vkCreateDescriptorUpdateTemplate = (PFN_vkCreateDescriptorUpdateTemplate)load(context, "vkCreateDescriptorUpdateTemplate"); + vkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion)load(context, "vkCreateSamplerYcbcrConversion"); + vkDestroyDescriptorUpdateTemplate = (PFN_vkDestroyDescriptorUpdateTemplate)load(context, "vkDestroyDescriptorUpdateTemplate"); + vkDestroySamplerYcbcrConversion = (PFN_vkDestroySamplerYcbcrConversion)load(context, "vkDestroySamplerYcbcrConversion"); + vkGetBufferMemoryRequirements2 = (PFN_vkGetBufferMemoryRequirements2)load(context, "vkGetBufferMemoryRequirements2"); + vkGetDescriptorSetLayoutSupport = (PFN_vkGetDescriptorSetLayoutSupport)load(context, "vkGetDescriptorSetLayoutSupport"); + vkGetDeviceGroupPeerMemoryFeatures = (PFN_vkGetDeviceGroupPeerMemoryFeatures)load(context, "vkGetDeviceGroupPeerMemoryFeatures"); + vkGetDeviceQueue2 = (PFN_vkGetDeviceQueue2)load(context, "vkGetDeviceQueue2"); + vkGetImageMemoryRequirements2 = (PFN_vkGetImageMemoryRequirements2)load(context, "vkGetImageMemoryRequirements2"); + vkGetImageSparseMemoryRequirements2 = (PFN_vkGetImageSparseMemoryRequirements2)load(context, "vkGetImageSparseMemoryRequirements2"); + vkTrimCommandPool = (PFN_vkTrimCommandPool)load(context, "vkTrimCommandPool"); + vkUpdateDescriptorSetWithTemplate = (PFN_vkUpdateDescriptorSetWithTemplate)load(context, "vkUpdateDescriptorSetWithTemplate"); +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_2) + vkCmdBeginRenderPass2 = (PFN_vkCmdBeginRenderPass2)load(context, "vkCmdBeginRenderPass2"); + vkCmdDrawIndexedIndirectCount = (PFN_vkCmdDrawIndexedIndirectCount)load(context, "vkCmdDrawIndexedIndirectCount"); + vkCmdDrawIndirectCount = (PFN_vkCmdDrawIndirectCount)load(context, "vkCmdDrawIndirectCount"); + vkCmdEndRenderPass2 = (PFN_vkCmdEndRenderPass2)load(context, "vkCmdEndRenderPass2"); + vkCmdNextSubpass2 = (PFN_vkCmdNextSubpass2)load(context, "vkCmdNextSubpass2"); + vkCreateRenderPass2 = (PFN_vkCreateRenderPass2)load(context, "vkCreateRenderPass2"); + vkGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddress)load(context, "vkGetBufferDeviceAddress"); + vkGetBufferOpaqueCaptureAddress = (PFN_vkGetBufferOpaqueCaptureAddress)load(context, "vkGetBufferOpaqueCaptureAddress"); + vkGetDeviceMemoryOpaqueCaptureAddress = (PFN_vkGetDeviceMemoryOpaqueCaptureAddress)load(context, "vkGetDeviceMemoryOpaqueCaptureAddress"); + vkGetSemaphoreCounterValue = (PFN_vkGetSemaphoreCounterValue)load(context, "vkGetSemaphoreCounterValue"); + vkResetQueryPool = (PFN_vkResetQueryPool)load(context, "vkResetQueryPool"); + vkSignalSemaphore = (PFN_vkSignalSemaphore)load(context, "vkSignalSemaphore"); + vkWaitSemaphores = (PFN_vkWaitSemaphores)load(context, "vkWaitSemaphores"); +#endif /* defined(VK_VERSION_1_2) */ +#if defined(VK_VERSION_1_3) + vkCmdBeginRendering = (PFN_vkCmdBeginRendering)load(context, "vkCmdBeginRendering"); + vkCmdBindVertexBuffers2 = (PFN_vkCmdBindVertexBuffers2)load(context, "vkCmdBindVertexBuffers2"); + vkCmdBlitImage2 = (PFN_vkCmdBlitImage2)load(context, "vkCmdBlitImage2"); + vkCmdCopyBuffer2 = (PFN_vkCmdCopyBuffer2)load(context, "vkCmdCopyBuffer2"); + vkCmdCopyBufferToImage2 = (PFN_vkCmdCopyBufferToImage2)load(context, "vkCmdCopyBufferToImage2"); + vkCmdCopyImage2 = (PFN_vkCmdCopyImage2)load(context, "vkCmdCopyImage2"); + vkCmdCopyImageToBuffer2 = (PFN_vkCmdCopyImageToBuffer2)load(context, "vkCmdCopyImageToBuffer2"); + vkCmdEndRendering = (PFN_vkCmdEndRendering)load(context, "vkCmdEndRendering"); + vkCmdPipelineBarrier2 = (PFN_vkCmdPipelineBarrier2)load(context, "vkCmdPipelineBarrier2"); + vkCmdResetEvent2 = (PFN_vkCmdResetEvent2)load(context, "vkCmdResetEvent2"); + vkCmdResolveImage2 = (PFN_vkCmdResolveImage2)load(context, "vkCmdResolveImage2"); + vkCmdSetCullMode = (PFN_vkCmdSetCullMode)load(context, "vkCmdSetCullMode"); + vkCmdSetDepthBiasEnable = (PFN_vkCmdSetDepthBiasEnable)load(context, "vkCmdSetDepthBiasEnable"); + vkCmdSetDepthBoundsTestEnable = (PFN_vkCmdSetDepthBoundsTestEnable)load(context, "vkCmdSetDepthBoundsTestEnable"); + vkCmdSetDepthCompareOp = (PFN_vkCmdSetDepthCompareOp)load(context, "vkCmdSetDepthCompareOp"); + vkCmdSetDepthTestEnable = (PFN_vkCmdSetDepthTestEnable)load(context, "vkCmdSetDepthTestEnable"); + vkCmdSetDepthWriteEnable = (PFN_vkCmdSetDepthWriteEnable)load(context, "vkCmdSetDepthWriteEnable"); + vkCmdSetEvent2 = (PFN_vkCmdSetEvent2)load(context, "vkCmdSetEvent2"); + vkCmdSetFrontFace = (PFN_vkCmdSetFrontFace)load(context, "vkCmdSetFrontFace"); + vkCmdSetPrimitiveRestartEnable = (PFN_vkCmdSetPrimitiveRestartEnable)load(context, "vkCmdSetPrimitiveRestartEnable"); + vkCmdSetPrimitiveTopology = (PFN_vkCmdSetPrimitiveTopology)load(context, "vkCmdSetPrimitiveTopology"); + vkCmdSetRasterizerDiscardEnable = (PFN_vkCmdSetRasterizerDiscardEnable)load(context, "vkCmdSetRasterizerDiscardEnable"); + vkCmdSetScissorWithCount = (PFN_vkCmdSetScissorWithCount)load(context, "vkCmdSetScissorWithCount"); + vkCmdSetStencilOp = (PFN_vkCmdSetStencilOp)load(context, "vkCmdSetStencilOp"); + vkCmdSetStencilTestEnable = (PFN_vkCmdSetStencilTestEnable)load(context, "vkCmdSetStencilTestEnable"); + vkCmdSetViewportWithCount = (PFN_vkCmdSetViewportWithCount)load(context, "vkCmdSetViewportWithCount"); + vkCmdWaitEvents2 = (PFN_vkCmdWaitEvents2)load(context, "vkCmdWaitEvents2"); + vkCmdWriteTimestamp2 = (PFN_vkCmdWriteTimestamp2)load(context, "vkCmdWriteTimestamp2"); + vkCreatePrivateDataSlot = (PFN_vkCreatePrivateDataSlot)load(context, "vkCreatePrivateDataSlot"); + vkDestroyPrivateDataSlot = (PFN_vkDestroyPrivateDataSlot)load(context, "vkDestroyPrivateDataSlot"); + vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)load(context, "vkGetDeviceBufferMemoryRequirements"); + vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)load(context, "vkGetDeviceImageMemoryRequirements"); + vkGetDeviceImageSparseMemoryRequirements = (PFN_vkGetDeviceImageSparseMemoryRequirements)load(context, "vkGetDeviceImageSparseMemoryRequirements"); + vkGetPrivateData = (PFN_vkGetPrivateData)load(context, "vkGetPrivateData"); + vkQueueSubmit2 = (PFN_vkQueueSubmit2)load(context, "vkQueueSubmit2"); + vkSetPrivateData = (PFN_vkSetPrivateData)load(context, "vkSetPrivateData"); +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_AMDX_shader_enqueue) + vkCmdDispatchGraphAMDX = (PFN_vkCmdDispatchGraphAMDX)load(context, "vkCmdDispatchGraphAMDX"); + vkCmdDispatchGraphIndirectAMDX = (PFN_vkCmdDispatchGraphIndirectAMDX)load(context, "vkCmdDispatchGraphIndirectAMDX"); + vkCmdDispatchGraphIndirectCountAMDX = (PFN_vkCmdDispatchGraphIndirectCountAMDX)load(context, "vkCmdDispatchGraphIndirectCountAMDX"); + vkCmdInitializeGraphScratchMemoryAMDX = (PFN_vkCmdInitializeGraphScratchMemoryAMDX)load(context, "vkCmdInitializeGraphScratchMemoryAMDX"); + vkCreateExecutionGraphPipelinesAMDX = (PFN_vkCreateExecutionGraphPipelinesAMDX)load(context, "vkCreateExecutionGraphPipelinesAMDX"); + vkGetExecutionGraphPipelineNodeIndexAMDX = (PFN_vkGetExecutionGraphPipelineNodeIndexAMDX)load(context, "vkGetExecutionGraphPipelineNodeIndexAMDX"); + vkGetExecutionGraphPipelineScratchSizeAMDX = (PFN_vkGetExecutionGraphPipelineScratchSizeAMDX)load(context, "vkGetExecutionGraphPipelineScratchSizeAMDX"); +#endif /* defined(VK_AMDX_shader_enqueue) */ +#if defined(VK_AMD_anti_lag) + vkAntiLagUpdateAMD = (PFN_vkAntiLagUpdateAMD)load(context, "vkAntiLagUpdateAMD"); +#endif /* defined(VK_AMD_anti_lag) */ +#if defined(VK_AMD_buffer_marker) + vkCmdWriteBufferMarkerAMD = (PFN_vkCmdWriteBufferMarkerAMD)load(context, "vkCmdWriteBufferMarkerAMD"); +#endif /* defined(VK_AMD_buffer_marker) */ +#if defined(VK_AMD_display_native_hdr) + vkSetLocalDimmingAMD = (PFN_vkSetLocalDimmingAMD)load(context, "vkSetLocalDimmingAMD"); +#endif /* defined(VK_AMD_display_native_hdr) */ +#if defined(VK_AMD_draw_indirect_count) + vkCmdDrawIndexedIndirectCountAMD = (PFN_vkCmdDrawIndexedIndirectCountAMD)load(context, "vkCmdDrawIndexedIndirectCountAMD"); + vkCmdDrawIndirectCountAMD = (PFN_vkCmdDrawIndirectCountAMD)load(context, "vkCmdDrawIndirectCountAMD"); +#endif /* defined(VK_AMD_draw_indirect_count) */ +#if defined(VK_AMD_shader_info) + vkGetShaderInfoAMD = (PFN_vkGetShaderInfoAMD)load(context, "vkGetShaderInfoAMD"); +#endif /* defined(VK_AMD_shader_info) */ +#if defined(VK_ANDROID_external_memory_android_hardware_buffer) + vkGetAndroidHardwareBufferPropertiesANDROID = (PFN_vkGetAndroidHardwareBufferPropertiesANDROID)load(context, "vkGetAndroidHardwareBufferPropertiesANDROID"); + vkGetMemoryAndroidHardwareBufferANDROID = (PFN_vkGetMemoryAndroidHardwareBufferANDROID)load(context, "vkGetMemoryAndroidHardwareBufferANDROID"); +#endif /* defined(VK_ANDROID_external_memory_android_hardware_buffer) */ +#if defined(VK_EXT_attachment_feedback_loop_dynamic_state) + vkCmdSetAttachmentFeedbackLoopEnableEXT = (PFN_vkCmdSetAttachmentFeedbackLoopEnableEXT)load(context, "vkCmdSetAttachmentFeedbackLoopEnableEXT"); +#endif /* defined(VK_EXT_attachment_feedback_loop_dynamic_state) */ +#if defined(VK_EXT_buffer_device_address) + vkGetBufferDeviceAddressEXT = (PFN_vkGetBufferDeviceAddressEXT)load(context, "vkGetBufferDeviceAddressEXT"); +#endif /* defined(VK_EXT_buffer_device_address) */ +#if defined(VK_EXT_calibrated_timestamps) + vkGetCalibratedTimestampsEXT = (PFN_vkGetCalibratedTimestampsEXT)load(context, "vkGetCalibratedTimestampsEXT"); +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_color_write_enable) + vkCmdSetColorWriteEnableEXT = (PFN_vkCmdSetColorWriteEnableEXT)load(context, "vkCmdSetColorWriteEnableEXT"); +#endif /* defined(VK_EXT_color_write_enable) */ +#if defined(VK_EXT_conditional_rendering) + vkCmdBeginConditionalRenderingEXT = (PFN_vkCmdBeginConditionalRenderingEXT)load(context, "vkCmdBeginConditionalRenderingEXT"); + vkCmdEndConditionalRenderingEXT = (PFN_vkCmdEndConditionalRenderingEXT)load(context, "vkCmdEndConditionalRenderingEXT"); +#endif /* defined(VK_EXT_conditional_rendering) */ +#if defined(VK_EXT_debug_marker) + vkCmdDebugMarkerBeginEXT = (PFN_vkCmdDebugMarkerBeginEXT)load(context, "vkCmdDebugMarkerBeginEXT"); + vkCmdDebugMarkerEndEXT = (PFN_vkCmdDebugMarkerEndEXT)load(context, "vkCmdDebugMarkerEndEXT"); + vkCmdDebugMarkerInsertEXT = (PFN_vkCmdDebugMarkerInsertEXT)load(context, "vkCmdDebugMarkerInsertEXT"); + vkDebugMarkerSetObjectNameEXT = (PFN_vkDebugMarkerSetObjectNameEXT)load(context, "vkDebugMarkerSetObjectNameEXT"); + vkDebugMarkerSetObjectTagEXT = (PFN_vkDebugMarkerSetObjectTagEXT)load(context, "vkDebugMarkerSetObjectTagEXT"); +#endif /* defined(VK_EXT_debug_marker) */ +#if defined(VK_EXT_depth_bias_control) + vkCmdSetDepthBias2EXT = (PFN_vkCmdSetDepthBias2EXT)load(context, "vkCmdSetDepthBias2EXT"); +#endif /* defined(VK_EXT_depth_bias_control) */ +#if defined(VK_EXT_descriptor_buffer) + vkCmdBindDescriptorBufferEmbeddedSamplersEXT = (PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT)load(context, "vkCmdBindDescriptorBufferEmbeddedSamplersEXT"); + vkCmdBindDescriptorBuffersEXT = (PFN_vkCmdBindDescriptorBuffersEXT)load(context, "vkCmdBindDescriptorBuffersEXT"); + vkCmdSetDescriptorBufferOffsetsEXT = (PFN_vkCmdSetDescriptorBufferOffsetsEXT)load(context, "vkCmdSetDescriptorBufferOffsetsEXT"); + vkGetBufferOpaqueCaptureDescriptorDataEXT = (PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT)load(context, "vkGetBufferOpaqueCaptureDescriptorDataEXT"); + vkGetDescriptorEXT = (PFN_vkGetDescriptorEXT)load(context, "vkGetDescriptorEXT"); + vkGetDescriptorSetLayoutBindingOffsetEXT = (PFN_vkGetDescriptorSetLayoutBindingOffsetEXT)load(context, "vkGetDescriptorSetLayoutBindingOffsetEXT"); + vkGetDescriptorSetLayoutSizeEXT = (PFN_vkGetDescriptorSetLayoutSizeEXT)load(context, "vkGetDescriptorSetLayoutSizeEXT"); + vkGetImageOpaqueCaptureDescriptorDataEXT = (PFN_vkGetImageOpaqueCaptureDescriptorDataEXT)load(context, "vkGetImageOpaqueCaptureDescriptorDataEXT"); + vkGetImageViewOpaqueCaptureDescriptorDataEXT = (PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT)load(context, "vkGetImageViewOpaqueCaptureDescriptorDataEXT"); + vkGetSamplerOpaqueCaptureDescriptorDataEXT = (PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT)load(context, "vkGetSamplerOpaqueCaptureDescriptorDataEXT"); +#endif /* defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) + vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT = (PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT)load(context, "vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT"); +#endif /* defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) */ +#if defined(VK_EXT_device_fault) + vkGetDeviceFaultInfoEXT = (PFN_vkGetDeviceFaultInfoEXT)load(context, "vkGetDeviceFaultInfoEXT"); +#endif /* defined(VK_EXT_device_fault) */ +#if defined(VK_EXT_discard_rectangles) + vkCmdSetDiscardRectangleEXT = (PFN_vkCmdSetDiscardRectangleEXT)load(context, "vkCmdSetDiscardRectangleEXT"); +#endif /* defined(VK_EXT_discard_rectangles) */ +#if defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 + vkCmdSetDiscardRectangleEnableEXT = (PFN_vkCmdSetDiscardRectangleEnableEXT)load(context, "vkCmdSetDiscardRectangleEnableEXT"); + vkCmdSetDiscardRectangleModeEXT = (PFN_vkCmdSetDiscardRectangleModeEXT)load(context, "vkCmdSetDiscardRectangleModeEXT"); +#endif /* defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 */ +#if defined(VK_EXT_display_control) + vkDisplayPowerControlEXT = (PFN_vkDisplayPowerControlEXT)load(context, "vkDisplayPowerControlEXT"); + vkGetSwapchainCounterEXT = (PFN_vkGetSwapchainCounterEXT)load(context, "vkGetSwapchainCounterEXT"); + vkRegisterDeviceEventEXT = (PFN_vkRegisterDeviceEventEXT)load(context, "vkRegisterDeviceEventEXT"); + vkRegisterDisplayEventEXT = (PFN_vkRegisterDisplayEventEXT)load(context, "vkRegisterDisplayEventEXT"); +#endif /* defined(VK_EXT_display_control) */ +#if defined(VK_EXT_external_memory_host) + vkGetMemoryHostPointerPropertiesEXT = (PFN_vkGetMemoryHostPointerPropertiesEXT)load(context, "vkGetMemoryHostPointerPropertiesEXT"); +#endif /* defined(VK_EXT_external_memory_host) */ +#if defined(VK_EXT_full_screen_exclusive) + vkAcquireFullScreenExclusiveModeEXT = (PFN_vkAcquireFullScreenExclusiveModeEXT)load(context, "vkAcquireFullScreenExclusiveModeEXT"); + vkReleaseFullScreenExclusiveModeEXT = (PFN_vkReleaseFullScreenExclusiveModeEXT)load(context, "vkReleaseFullScreenExclusiveModeEXT"); +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) + vkGetDeviceGroupSurfacePresentModes2EXT = (PFN_vkGetDeviceGroupSurfacePresentModes2EXT)load(context, "vkGetDeviceGroupSurfacePresentModes2EXT"); +#endif /* defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) */ +#if defined(VK_EXT_hdr_metadata) + vkSetHdrMetadataEXT = (PFN_vkSetHdrMetadataEXT)load(context, "vkSetHdrMetadataEXT"); +#endif /* defined(VK_EXT_hdr_metadata) */ +#if defined(VK_EXT_host_image_copy) + vkCopyImageToImageEXT = (PFN_vkCopyImageToImageEXT)load(context, "vkCopyImageToImageEXT"); + vkCopyImageToMemoryEXT = (PFN_vkCopyImageToMemoryEXT)load(context, "vkCopyImageToMemoryEXT"); + vkCopyMemoryToImageEXT = (PFN_vkCopyMemoryToImageEXT)load(context, "vkCopyMemoryToImageEXT"); + vkTransitionImageLayoutEXT = (PFN_vkTransitionImageLayoutEXT)load(context, "vkTransitionImageLayoutEXT"); +#endif /* defined(VK_EXT_host_image_copy) */ +#if defined(VK_EXT_host_query_reset) + vkResetQueryPoolEXT = (PFN_vkResetQueryPoolEXT)load(context, "vkResetQueryPoolEXT"); +#endif /* defined(VK_EXT_host_query_reset) */ +#if defined(VK_EXT_image_drm_format_modifier) + vkGetImageDrmFormatModifierPropertiesEXT = (PFN_vkGetImageDrmFormatModifierPropertiesEXT)load(context, "vkGetImageDrmFormatModifierPropertiesEXT"); +#endif /* defined(VK_EXT_image_drm_format_modifier) */ +#if defined(VK_EXT_line_rasterization) + vkCmdSetLineStippleEXT = (PFN_vkCmdSetLineStippleEXT)load(context, "vkCmdSetLineStippleEXT"); +#endif /* defined(VK_EXT_line_rasterization) */ +#if defined(VK_EXT_mesh_shader) + vkCmdDrawMeshTasksEXT = (PFN_vkCmdDrawMeshTasksEXT)load(context, "vkCmdDrawMeshTasksEXT"); + vkCmdDrawMeshTasksIndirectCountEXT = (PFN_vkCmdDrawMeshTasksIndirectCountEXT)load(context, "vkCmdDrawMeshTasksIndirectCountEXT"); + vkCmdDrawMeshTasksIndirectEXT = (PFN_vkCmdDrawMeshTasksIndirectEXT)load(context, "vkCmdDrawMeshTasksIndirectEXT"); +#endif /* defined(VK_EXT_mesh_shader) */ +#if defined(VK_EXT_metal_objects) + vkExportMetalObjectsEXT = (PFN_vkExportMetalObjectsEXT)load(context, "vkExportMetalObjectsEXT"); +#endif /* defined(VK_EXT_metal_objects) */ +#if defined(VK_EXT_multi_draw) + vkCmdDrawMultiEXT = (PFN_vkCmdDrawMultiEXT)load(context, "vkCmdDrawMultiEXT"); + vkCmdDrawMultiIndexedEXT = (PFN_vkCmdDrawMultiIndexedEXT)load(context, "vkCmdDrawMultiIndexedEXT"); +#endif /* defined(VK_EXT_multi_draw) */ +#if defined(VK_EXT_opacity_micromap) + vkBuildMicromapsEXT = (PFN_vkBuildMicromapsEXT)load(context, "vkBuildMicromapsEXT"); + vkCmdBuildMicromapsEXT = (PFN_vkCmdBuildMicromapsEXT)load(context, "vkCmdBuildMicromapsEXT"); + vkCmdCopyMemoryToMicromapEXT = (PFN_vkCmdCopyMemoryToMicromapEXT)load(context, "vkCmdCopyMemoryToMicromapEXT"); + vkCmdCopyMicromapEXT = (PFN_vkCmdCopyMicromapEXT)load(context, "vkCmdCopyMicromapEXT"); + vkCmdCopyMicromapToMemoryEXT = (PFN_vkCmdCopyMicromapToMemoryEXT)load(context, "vkCmdCopyMicromapToMemoryEXT"); + vkCmdWriteMicromapsPropertiesEXT = (PFN_vkCmdWriteMicromapsPropertiesEXT)load(context, "vkCmdWriteMicromapsPropertiesEXT"); + vkCopyMemoryToMicromapEXT = (PFN_vkCopyMemoryToMicromapEXT)load(context, "vkCopyMemoryToMicromapEXT"); + vkCopyMicromapEXT = (PFN_vkCopyMicromapEXT)load(context, "vkCopyMicromapEXT"); + vkCopyMicromapToMemoryEXT = (PFN_vkCopyMicromapToMemoryEXT)load(context, "vkCopyMicromapToMemoryEXT"); + vkCreateMicromapEXT = (PFN_vkCreateMicromapEXT)load(context, "vkCreateMicromapEXT"); + vkDestroyMicromapEXT = (PFN_vkDestroyMicromapEXT)load(context, "vkDestroyMicromapEXT"); + vkGetDeviceMicromapCompatibilityEXT = (PFN_vkGetDeviceMicromapCompatibilityEXT)load(context, "vkGetDeviceMicromapCompatibilityEXT"); + vkGetMicromapBuildSizesEXT = (PFN_vkGetMicromapBuildSizesEXT)load(context, "vkGetMicromapBuildSizesEXT"); + vkWriteMicromapsPropertiesEXT = (PFN_vkWriteMicromapsPropertiesEXT)load(context, "vkWriteMicromapsPropertiesEXT"); +#endif /* defined(VK_EXT_opacity_micromap) */ +#if defined(VK_EXT_pageable_device_local_memory) + vkSetDeviceMemoryPriorityEXT = (PFN_vkSetDeviceMemoryPriorityEXT)load(context, "vkSetDeviceMemoryPriorityEXT"); +#endif /* defined(VK_EXT_pageable_device_local_memory) */ +#if defined(VK_EXT_pipeline_properties) + vkGetPipelinePropertiesEXT = (PFN_vkGetPipelinePropertiesEXT)load(context, "vkGetPipelinePropertiesEXT"); +#endif /* defined(VK_EXT_pipeline_properties) */ +#if defined(VK_EXT_private_data) + vkCreatePrivateDataSlotEXT = (PFN_vkCreatePrivateDataSlotEXT)load(context, "vkCreatePrivateDataSlotEXT"); + vkDestroyPrivateDataSlotEXT = (PFN_vkDestroyPrivateDataSlotEXT)load(context, "vkDestroyPrivateDataSlotEXT"); + vkGetPrivateDataEXT = (PFN_vkGetPrivateDataEXT)load(context, "vkGetPrivateDataEXT"); + vkSetPrivateDataEXT = (PFN_vkSetPrivateDataEXT)load(context, "vkSetPrivateDataEXT"); +#endif /* defined(VK_EXT_private_data) */ +#if defined(VK_EXT_sample_locations) + vkCmdSetSampleLocationsEXT = (PFN_vkCmdSetSampleLocationsEXT)load(context, "vkCmdSetSampleLocationsEXT"); +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_shader_module_identifier) + vkGetShaderModuleCreateInfoIdentifierEXT = (PFN_vkGetShaderModuleCreateInfoIdentifierEXT)load(context, "vkGetShaderModuleCreateInfoIdentifierEXT"); + vkGetShaderModuleIdentifierEXT = (PFN_vkGetShaderModuleIdentifierEXT)load(context, "vkGetShaderModuleIdentifierEXT"); +#endif /* defined(VK_EXT_shader_module_identifier) */ +#if defined(VK_EXT_shader_object) + vkCmdBindShadersEXT = (PFN_vkCmdBindShadersEXT)load(context, "vkCmdBindShadersEXT"); + vkCreateShadersEXT = (PFN_vkCreateShadersEXT)load(context, "vkCreateShadersEXT"); + vkDestroyShaderEXT = (PFN_vkDestroyShaderEXT)load(context, "vkDestroyShaderEXT"); + vkGetShaderBinaryDataEXT = (PFN_vkGetShaderBinaryDataEXT)load(context, "vkGetShaderBinaryDataEXT"); +#endif /* defined(VK_EXT_shader_object) */ +#if defined(VK_EXT_swapchain_maintenance1) + vkReleaseSwapchainImagesEXT = (PFN_vkReleaseSwapchainImagesEXT)load(context, "vkReleaseSwapchainImagesEXT"); +#endif /* defined(VK_EXT_swapchain_maintenance1) */ +#if defined(VK_EXT_transform_feedback) + vkCmdBeginQueryIndexedEXT = (PFN_vkCmdBeginQueryIndexedEXT)load(context, "vkCmdBeginQueryIndexedEXT"); + vkCmdBeginTransformFeedbackEXT = (PFN_vkCmdBeginTransformFeedbackEXT)load(context, "vkCmdBeginTransformFeedbackEXT"); + vkCmdBindTransformFeedbackBuffersEXT = (PFN_vkCmdBindTransformFeedbackBuffersEXT)load(context, "vkCmdBindTransformFeedbackBuffersEXT"); + vkCmdDrawIndirectByteCountEXT = (PFN_vkCmdDrawIndirectByteCountEXT)load(context, "vkCmdDrawIndirectByteCountEXT"); + vkCmdEndQueryIndexedEXT = (PFN_vkCmdEndQueryIndexedEXT)load(context, "vkCmdEndQueryIndexedEXT"); + vkCmdEndTransformFeedbackEXT = (PFN_vkCmdEndTransformFeedbackEXT)load(context, "vkCmdEndTransformFeedbackEXT"); +#endif /* defined(VK_EXT_transform_feedback) */ +#if defined(VK_EXT_validation_cache) + vkCreateValidationCacheEXT = (PFN_vkCreateValidationCacheEXT)load(context, "vkCreateValidationCacheEXT"); + vkDestroyValidationCacheEXT = (PFN_vkDestroyValidationCacheEXT)load(context, "vkDestroyValidationCacheEXT"); + vkGetValidationCacheDataEXT = (PFN_vkGetValidationCacheDataEXT)load(context, "vkGetValidationCacheDataEXT"); + vkMergeValidationCachesEXT = (PFN_vkMergeValidationCachesEXT)load(context, "vkMergeValidationCachesEXT"); +#endif /* defined(VK_EXT_validation_cache) */ +#if defined(VK_FUCHSIA_buffer_collection) + vkCreateBufferCollectionFUCHSIA = (PFN_vkCreateBufferCollectionFUCHSIA)load(context, "vkCreateBufferCollectionFUCHSIA"); + vkDestroyBufferCollectionFUCHSIA = (PFN_vkDestroyBufferCollectionFUCHSIA)load(context, "vkDestroyBufferCollectionFUCHSIA"); + vkGetBufferCollectionPropertiesFUCHSIA = (PFN_vkGetBufferCollectionPropertiesFUCHSIA)load(context, "vkGetBufferCollectionPropertiesFUCHSIA"); + vkSetBufferCollectionBufferConstraintsFUCHSIA = (PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA)load(context, "vkSetBufferCollectionBufferConstraintsFUCHSIA"); + vkSetBufferCollectionImageConstraintsFUCHSIA = (PFN_vkSetBufferCollectionImageConstraintsFUCHSIA)load(context, "vkSetBufferCollectionImageConstraintsFUCHSIA"); +#endif /* defined(VK_FUCHSIA_buffer_collection) */ +#if defined(VK_FUCHSIA_external_memory) + vkGetMemoryZirconHandleFUCHSIA = (PFN_vkGetMemoryZirconHandleFUCHSIA)load(context, "vkGetMemoryZirconHandleFUCHSIA"); + vkGetMemoryZirconHandlePropertiesFUCHSIA = (PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA)load(context, "vkGetMemoryZirconHandlePropertiesFUCHSIA"); +#endif /* defined(VK_FUCHSIA_external_memory) */ +#if defined(VK_FUCHSIA_external_semaphore) + vkGetSemaphoreZirconHandleFUCHSIA = (PFN_vkGetSemaphoreZirconHandleFUCHSIA)load(context, "vkGetSemaphoreZirconHandleFUCHSIA"); + vkImportSemaphoreZirconHandleFUCHSIA = (PFN_vkImportSemaphoreZirconHandleFUCHSIA)load(context, "vkImportSemaphoreZirconHandleFUCHSIA"); +#endif /* defined(VK_FUCHSIA_external_semaphore) */ +#if defined(VK_GOOGLE_display_timing) + vkGetPastPresentationTimingGOOGLE = (PFN_vkGetPastPresentationTimingGOOGLE)load(context, "vkGetPastPresentationTimingGOOGLE"); + vkGetRefreshCycleDurationGOOGLE = (PFN_vkGetRefreshCycleDurationGOOGLE)load(context, "vkGetRefreshCycleDurationGOOGLE"); +#endif /* defined(VK_GOOGLE_display_timing) */ +#if defined(VK_HUAWEI_cluster_culling_shader) + vkCmdDrawClusterHUAWEI = (PFN_vkCmdDrawClusterHUAWEI)load(context, "vkCmdDrawClusterHUAWEI"); + vkCmdDrawClusterIndirectHUAWEI = (PFN_vkCmdDrawClusterIndirectHUAWEI)load(context, "vkCmdDrawClusterIndirectHUAWEI"); +#endif /* defined(VK_HUAWEI_cluster_culling_shader) */ +#if defined(VK_HUAWEI_invocation_mask) + vkCmdBindInvocationMaskHUAWEI = (PFN_vkCmdBindInvocationMaskHUAWEI)load(context, "vkCmdBindInvocationMaskHUAWEI"); +#endif /* defined(VK_HUAWEI_invocation_mask) */ +#if defined(VK_HUAWEI_subpass_shading) + vkCmdSubpassShadingHUAWEI = (PFN_vkCmdSubpassShadingHUAWEI)load(context, "vkCmdSubpassShadingHUAWEI"); + vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI = (PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI)load(context, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI"); +#endif /* defined(VK_HUAWEI_subpass_shading) */ +#if defined(VK_INTEL_performance_query) + vkAcquirePerformanceConfigurationINTEL = (PFN_vkAcquirePerformanceConfigurationINTEL)load(context, "vkAcquirePerformanceConfigurationINTEL"); + vkCmdSetPerformanceMarkerINTEL = (PFN_vkCmdSetPerformanceMarkerINTEL)load(context, "vkCmdSetPerformanceMarkerINTEL"); + vkCmdSetPerformanceOverrideINTEL = (PFN_vkCmdSetPerformanceOverrideINTEL)load(context, "vkCmdSetPerformanceOverrideINTEL"); + vkCmdSetPerformanceStreamMarkerINTEL = (PFN_vkCmdSetPerformanceStreamMarkerINTEL)load(context, "vkCmdSetPerformanceStreamMarkerINTEL"); + vkGetPerformanceParameterINTEL = (PFN_vkGetPerformanceParameterINTEL)load(context, "vkGetPerformanceParameterINTEL"); + vkInitializePerformanceApiINTEL = (PFN_vkInitializePerformanceApiINTEL)load(context, "vkInitializePerformanceApiINTEL"); + vkQueueSetPerformanceConfigurationINTEL = (PFN_vkQueueSetPerformanceConfigurationINTEL)load(context, "vkQueueSetPerformanceConfigurationINTEL"); + vkReleasePerformanceConfigurationINTEL = (PFN_vkReleasePerformanceConfigurationINTEL)load(context, "vkReleasePerformanceConfigurationINTEL"); + vkUninitializePerformanceApiINTEL = (PFN_vkUninitializePerformanceApiINTEL)load(context, "vkUninitializePerformanceApiINTEL"); +#endif /* defined(VK_INTEL_performance_query) */ +#if defined(VK_KHR_acceleration_structure) + vkBuildAccelerationStructuresKHR = (PFN_vkBuildAccelerationStructuresKHR)load(context, "vkBuildAccelerationStructuresKHR"); + vkCmdBuildAccelerationStructuresIndirectKHR = (PFN_vkCmdBuildAccelerationStructuresIndirectKHR)load(context, "vkCmdBuildAccelerationStructuresIndirectKHR"); + vkCmdBuildAccelerationStructuresKHR = (PFN_vkCmdBuildAccelerationStructuresKHR)load(context, "vkCmdBuildAccelerationStructuresKHR"); + vkCmdCopyAccelerationStructureKHR = (PFN_vkCmdCopyAccelerationStructureKHR)load(context, "vkCmdCopyAccelerationStructureKHR"); + vkCmdCopyAccelerationStructureToMemoryKHR = (PFN_vkCmdCopyAccelerationStructureToMemoryKHR)load(context, "vkCmdCopyAccelerationStructureToMemoryKHR"); + vkCmdCopyMemoryToAccelerationStructureKHR = (PFN_vkCmdCopyMemoryToAccelerationStructureKHR)load(context, "vkCmdCopyMemoryToAccelerationStructureKHR"); + vkCmdWriteAccelerationStructuresPropertiesKHR = (PFN_vkCmdWriteAccelerationStructuresPropertiesKHR)load(context, "vkCmdWriteAccelerationStructuresPropertiesKHR"); + vkCopyAccelerationStructureKHR = (PFN_vkCopyAccelerationStructureKHR)load(context, "vkCopyAccelerationStructureKHR"); + vkCopyAccelerationStructureToMemoryKHR = (PFN_vkCopyAccelerationStructureToMemoryKHR)load(context, "vkCopyAccelerationStructureToMemoryKHR"); + vkCopyMemoryToAccelerationStructureKHR = (PFN_vkCopyMemoryToAccelerationStructureKHR)load(context, "vkCopyMemoryToAccelerationStructureKHR"); + vkCreateAccelerationStructureKHR = (PFN_vkCreateAccelerationStructureKHR)load(context, "vkCreateAccelerationStructureKHR"); + vkDestroyAccelerationStructureKHR = (PFN_vkDestroyAccelerationStructureKHR)load(context, "vkDestroyAccelerationStructureKHR"); + vkGetAccelerationStructureBuildSizesKHR = (PFN_vkGetAccelerationStructureBuildSizesKHR)load(context, "vkGetAccelerationStructureBuildSizesKHR"); + vkGetAccelerationStructureDeviceAddressKHR = (PFN_vkGetAccelerationStructureDeviceAddressKHR)load(context, "vkGetAccelerationStructureDeviceAddressKHR"); + vkGetDeviceAccelerationStructureCompatibilityKHR = (PFN_vkGetDeviceAccelerationStructureCompatibilityKHR)load(context, "vkGetDeviceAccelerationStructureCompatibilityKHR"); + vkWriteAccelerationStructuresPropertiesKHR = (PFN_vkWriteAccelerationStructuresPropertiesKHR)load(context, "vkWriteAccelerationStructuresPropertiesKHR"); +#endif /* defined(VK_KHR_acceleration_structure) */ +#if defined(VK_KHR_bind_memory2) + vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2KHR)load(context, "vkBindBufferMemory2KHR"); + vkBindImageMemory2KHR = (PFN_vkBindImageMemory2KHR)load(context, "vkBindImageMemory2KHR"); +#endif /* defined(VK_KHR_bind_memory2) */ +#if defined(VK_KHR_buffer_device_address) + vkGetBufferDeviceAddressKHR = (PFN_vkGetBufferDeviceAddressKHR)load(context, "vkGetBufferDeviceAddressKHR"); + vkGetBufferOpaqueCaptureAddressKHR = (PFN_vkGetBufferOpaqueCaptureAddressKHR)load(context, "vkGetBufferOpaqueCaptureAddressKHR"); + vkGetDeviceMemoryOpaqueCaptureAddressKHR = (PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR)load(context, "vkGetDeviceMemoryOpaqueCaptureAddressKHR"); +#endif /* defined(VK_KHR_buffer_device_address) */ +#if defined(VK_KHR_calibrated_timestamps) + vkGetCalibratedTimestampsKHR = (PFN_vkGetCalibratedTimestampsKHR)load(context, "vkGetCalibratedTimestampsKHR"); +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_copy_commands2) + vkCmdBlitImage2KHR = (PFN_vkCmdBlitImage2KHR)load(context, "vkCmdBlitImage2KHR"); + vkCmdCopyBuffer2KHR = (PFN_vkCmdCopyBuffer2KHR)load(context, "vkCmdCopyBuffer2KHR"); + vkCmdCopyBufferToImage2KHR = (PFN_vkCmdCopyBufferToImage2KHR)load(context, "vkCmdCopyBufferToImage2KHR"); + vkCmdCopyImage2KHR = (PFN_vkCmdCopyImage2KHR)load(context, "vkCmdCopyImage2KHR"); + vkCmdCopyImageToBuffer2KHR = (PFN_vkCmdCopyImageToBuffer2KHR)load(context, "vkCmdCopyImageToBuffer2KHR"); + vkCmdResolveImage2KHR = (PFN_vkCmdResolveImage2KHR)load(context, "vkCmdResolveImage2KHR"); +#endif /* defined(VK_KHR_copy_commands2) */ +#if defined(VK_KHR_create_renderpass2) + vkCmdBeginRenderPass2KHR = (PFN_vkCmdBeginRenderPass2KHR)load(context, "vkCmdBeginRenderPass2KHR"); + vkCmdEndRenderPass2KHR = (PFN_vkCmdEndRenderPass2KHR)load(context, "vkCmdEndRenderPass2KHR"); + vkCmdNextSubpass2KHR = (PFN_vkCmdNextSubpass2KHR)load(context, "vkCmdNextSubpass2KHR"); + vkCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)load(context, "vkCreateRenderPass2KHR"); +#endif /* defined(VK_KHR_create_renderpass2) */ +#if defined(VK_KHR_deferred_host_operations) + vkCreateDeferredOperationKHR = (PFN_vkCreateDeferredOperationKHR)load(context, "vkCreateDeferredOperationKHR"); + vkDeferredOperationJoinKHR = (PFN_vkDeferredOperationJoinKHR)load(context, "vkDeferredOperationJoinKHR"); + vkDestroyDeferredOperationKHR = (PFN_vkDestroyDeferredOperationKHR)load(context, "vkDestroyDeferredOperationKHR"); + vkGetDeferredOperationMaxConcurrencyKHR = (PFN_vkGetDeferredOperationMaxConcurrencyKHR)load(context, "vkGetDeferredOperationMaxConcurrencyKHR"); + vkGetDeferredOperationResultKHR = (PFN_vkGetDeferredOperationResultKHR)load(context, "vkGetDeferredOperationResultKHR"); +#endif /* defined(VK_KHR_deferred_host_operations) */ +#if defined(VK_KHR_descriptor_update_template) + vkCreateDescriptorUpdateTemplateKHR = (PFN_vkCreateDescriptorUpdateTemplateKHR)load(context, "vkCreateDescriptorUpdateTemplateKHR"); + vkDestroyDescriptorUpdateTemplateKHR = (PFN_vkDestroyDescriptorUpdateTemplateKHR)load(context, "vkDestroyDescriptorUpdateTemplateKHR"); + vkUpdateDescriptorSetWithTemplateKHR = (PFN_vkUpdateDescriptorSetWithTemplateKHR)load(context, "vkUpdateDescriptorSetWithTemplateKHR"); +#endif /* defined(VK_KHR_descriptor_update_template) */ +#if defined(VK_KHR_device_group) + vkCmdDispatchBaseKHR = (PFN_vkCmdDispatchBaseKHR)load(context, "vkCmdDispatchBaseKHR"); + vkCmdSetDeviceMaskKHR = (PFN_vkCmdSetDeviceMaskKHR)load(context, "vkCmdSetDeviceMaskKHR"); + vkGetDeviceGroupPeerMemoryFeaturesKHR = (PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR)load(context, "vkGetDeviceGroupPeerMemoryFeaturesKHR"); +#endif /* defined(VK_KHR_device_group) */ +#if defined(VK_KHR_display_swapchain) + vkCreateSharedSwapchainsKHR = (PFN_vkCreateSharedSwapchainsKHR)load(context, "vkCreateSharedSwapchainsKHR"); +#endif /* defined(VK_KHR_display_swapchain) */ +#if defined(VK_KHR_draw_indirect_count) + vkCmdDrawIndexedIndirectCountKHR = (PFN_vkCmdDrawIndexedIndirectCountKHR)load(context, "vkCmdDrawIndexedIndirectCountKHR"); + vkCmdDrawIndirectCountKHR = (PFN_vkCmdDrawIndirectCountKHR)load(context, "vkCmdDrawIndirectCountKHR"); +#endif /* defined(VK_KHR_draw_indirect_count) */ +#if defined(VK_KHR_dynamic_rendering) + vkCmdBeginRenderingKHR = (PFN_vkCmdBeginRenderingKHR)load(context, "vkCmdBeginRenderingKHR"); + vkCmdEndRenderingKHR = (PFN_vkCmdEndRenderingKHR)load(context, "vkCmdEndRenderingKHR"); +#endif /* defined(VK_KHR_dynamic_rendering) */ +#if defined(VK_KHR_dynamic_rendering_local_read) + vkCmdSetRenderingAttachmentLocationsKHR = (PFN_vkCmdSetRenderingAttachmentLocationsKHR)load(context, "vkCmdSetRenderingAttachmentLocationsKHR"); + vkCmdSetRenderingInputAttachmentIndicesKHR = (PFN_vkCmdSetRenderingInputAttachmentIndicesKHR)load(context, "vkCmdSetRenderingInputAttachmentIndicesKHR"); +#endif /* defined(VK_KHR_dynamic_rendering_local_read) */ +#if defined(VK_KHR_external_fence_fd) + vkGetFenceFdKHR = (PFN_vkGetFenceFdKHR)load(context, "vkGetFenceFdKHR"); + vkImportFenceFdKHR = (PFN_vkImportFenceFdKHR)load(context, "vkImportFenceFdKHR"); +#endif /* defined(VK_KHR_external_fence_fd) */ +#if defined(VK_KHR_external_fence_win32) + vkGetFenceWin32HandleKHR = (PFN_vkGetFenceWin32HandleKHR)load(context, "vkGetFenceWin32HandleKHR"); + vkImportFenceWin32HandleKHR = (PFN_vkImportFenceWin32HandleKHR)load(context, "vkImportFenceWin32HandleKHR"); +#endif /* defined(VK_KHR_external_fence_win32) */ +#if defined(VK_KHR_external_memory_fd) + vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)load(context, "vkGetMemoryFdKHR"); + vkGetMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR)load(context, "vkGetMemoryFdPropertiesKHR"); +#endif /* defined(VK_KHR_external_memory_fd) */ +#if defined(VK_KHR_external_memory_win32) + vkGetMemoryWin32HandleKHR = (PFN_vkGetMemoryWin32HandleKHR)load(context, "vkGetMemoryWin32HandleKHR"); + vkGetMemoryWin32HandlePropertiesKHR = (PFN_vkGetMemoryWin32HandlePropertiesKHR)load(context, "vkGetMemoryWin32HandlePropertiesKHR"); +#endif /* defined(VK_KHR_external_memory_win32) */ +#if defined(VK_KHR_external_semaphore_fd) + vkGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR)load(context, "vkGetSemaphoreFdKHR"); + vkImportSemaphoreFdKHR = (PFN_vkImportSemaphoreFdKHR)load(context, "vkImportSemaphoreFdKHR"); +#endif /* defined(VK_KHR_external_semaphore_fd) */ +#if defined(VK_KHR_external_semaphore_win32) + vkGetSemaphoreWin32HandleKHR = (PFN_vkGetSemaphoreWin32HandleKHR)load(context, "vkGetSemaphoreWin32HandleKHR"); + vkImportSemaphoreWin32HandleKHR = (PFN_vkImportSemaphoreWin32HandleKHR)load(context, "vkImportSemaphoreWin32HandleKHR"); +#endif /* defined(VK_KHR_external_semaphore_win32) */ +#if defined(VK_KHR_fragment_shading_rate) + vkCmdSetFragmentShadingRateKHR = (PFN_vkCmdSetFragmentShadingRateKHR)load(context, "vkCmdSetFragmentShadingRateKHR"); +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_memory_requirements2) + vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2KHR)load(context, "vkGetBufferMemoryRequirements2KHR"); + vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2KHR)load(context, "vkGetImageMemoryRequirements2KHR"); + vkGetImageSparseMemoryRequirements2KHR = (PFN_vkGetImageSparseMemoryRequirements2KHR)load(context, "vkGetImageSparseMemoryRequirements2KHR"); +#endif /* defined(VK_KHR_get_memory_requirements2) */ +#if defined(VK_KHR_line_rasterization) + vkCmdSetLineStippleKHR = (PFN_vkCmdSetLineStippleKHR)load(context, "vkCmdSetLineStippleKHR"); +#endif /* defined(VK_KHR_line_rasterization) */ +#if defined(VK_KHR_maintenance1) + vkTrimCommandPoolKHR = (PFN_vkTrimCommandPoolKHR)load(context, "vkTrimCommandPoolKHR"); +#endif /* defined(VK_KHR_maintenance1) */ +#if defined(VK_KHR_maintenance3) + vkGetDescriptorSetLayoutSupportKHR = (PFN_vkGetDescriptorSetLayoutSupportKHR)load(context, "vkGetDescriptorSetLayoutSupportKHR"); +#endif /* defined(VK_KHR_maintenance3) */ +#if defined(VK_KHR_maintenance4) + vkGetDeviceBufferMemoryRequirementsKHR = (PFN_vkGetDeviceBufferMemoryRequirementsKHR)load(context, "vkGetDeviceBufferMemoryRequirementsKHR"); + vkGetDeviceImageMemoryRequirementsKHR = (PFN_vkGetDeviceImageMemoryRequirementsKHR)load(context, "vkGetDeviceImageMemoryRequirementsKHR"); + vkGetDeviceImageSparseMemoryRequirementsKHR = (PFN_vkGetDeviceImageSparseMemoryRequirementsKHR)load(context, "vkGetDeviceImageSparseMemoryRequirementsKHR"); +#endif /* defined(VK_KHR_maintenance4) */ +#if defined(VK_KHR_maintenance5) + vkCmdBindIndexBuffer2KHR = (PFN_vkCmdBindIndexBuffer2KHR)load(context, "vkCmdBindIndexBuffer2KHR"); + vkGetDeviceImageSubresourceLayoutKHR = (PFN_vkGetDeviceImageSubresourceLayoutKHR)load(context, "vkGetDeviceImageSubresourceLayoutKHR"); + vkGetImageSubresourceLayout2KHR = (PFN_vkGetImageSubresourceLayout2KHR)load(context, "vkGetImageSubresourceLayout2KHR"); + vkGetRenderingAreaGranularityKHR = (PFN_vkGetRenderingAreaGranularityKHR)load(context, "vkGetRenderingAreaGranularityKHR"); +#endif /* defined(VK_KHR_maintenance5) */ +#if defined(VK_KHR_maintenance6) + vkCmdBindDescriptorSets2KHR = (PFN_vkCmdBindDescriptorSets2KHR)load(context, "vkCmdBindDescriptorSets2KHR"); + vkCmdPushConstants2KHR = (PFN_vkCmdPushConstants2KHR)load(context, "vkCmdPushConstants2KHR"); +#endif /* defined(VK_KHR_maintenance6) */ +#if defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) + vkCmdPushDescriptorSet2KHR = (PFN_vkCmdPushDescriptorSet2KHR)load(context, "vkCmdPushDescriptorSet2KHR"); + vkCmdPushDescriptorSetWithTemplate2KHR = (PFN_vkCmdPushDescriptorSetWithTemplate2KHR)load(context, "vkCmdPushDescriptorSetWithTemplate2KHR"); +#endif /* defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) + vkCmdBindDescriptorBufferEmbeddedSamplers2EXT = (PFN_vkCmdBindDescriptorBufferEmbeddedSamplers2EXT)load(context, "vkCmdBindDescriptorBufferEmbeddedSamplers2EXT"); + vkCmdSetDescriptorBufferOffsets2EXT = (PFN_vkCmdSetDescriptorBufferOffsets2EXT)load(context, "vkCmdSetDescriptorBufferOffsets2EXT"); +#endif /* defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_KHR_map_memory2) + vkMapMemory2KHR = (PFN_vkMapMemory2KHR)load(context, "vkMapMemory2KHR"); + vkUnmapMemory2KHR = (PFN_vkUnmapMemory2KHR)load(context, "vkUnmapMemory2KHR"); +#endif /* defined(VK_KHR_map_memory2) */ +#if defined(VK_KHR_performance_query) + vkAcquireProfilingLockKHR = (PFN_vkAcquireProfilingLockKHR)load(context, "vkAcquireProfilingLockKHR"); + vkReleaseProfilingLockKHR = (PFN_vkReleaseProfilingLockKHR)load(context, "vkReleaseProfilingLockKHR"); +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_pipeline_binary) + vkCreatePipelineBinariesKHR = (PFN_vkCreatePipelineBinariesKHR)load(context, "vkCreatePipelineBinariesKHR"); + vkDestroyPipelineBinaryKHR = (PFN_vkDestroyPipelineBinaryKHR)load(context, "vkDestroyPipelineBinaryKHR"); + vkGetPipelineBinaryDataKHR = (PFN_vkGetPipelineBinaryDataKHR)load(context, "vkGetPipelineBinaryDataKHR"); + vkGetPipelineKeyKHR = (PFN_vkGetPipelineKeyKHR)load(context, "vkGetPipelineKeyKHR"); + vkReleaseCapturedPipelineDataKHR = (PFN_vkReleaseCapturedPipelineDataKHR)load(context, "vkReleaseCapturedPipelineDataKHR"); +#endif /* defined(VK_KHR_pipeline_binary) */ +#if defined(VK_KHR_pipeline_executable_properties) + vkGetPipelineExecutableInternalRepresentationsKHR = (PFN_vkGetPipelineExecutableInternalRepresentationsKHR)load(context, "vkGetPipelineExecutableInternalRepresentationsKHR"); + vkGetPipelineExecutablePropertiesKHR = (PFN_vkGetPipelineExecutablePropertiesKHR)load(context, "vkGetPipelineExecutablePropertiesKHR"); + vkGetPipelineExecutableStatisticsKHR = (PFN_vkGetPipelineExecutableStatisticsKHR)load(context, "vkGetPipelineExecutableStatisticsKHR"); +#endif /* defined(VK_KHR_pipeline_executable_properties) */ +#if defined(VK_KHR_present_wait) + vkWaitForPresentKHR = (PFN_vkWaitForPresentKHR)load(context, "vkWaitForPresentKHR"); +#endif /* defined(VK_KHR_present_wait) */ +#if defined(VK_KHR_push_descriptor) + vkCmdPushDescriptorSetKHR = (PFN_vkCmdPushDescriptorSetKHR)load(context, "vkCmdPushDescriptorSetKHR"); +#endif /* defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) + vkCmdTraceRaysIndirect2KHR = (PFN_vkCmdTraceRaysIndirect2KHR)load(context, "vkCmdTraceRaysIndirect2KHR"); +#endif /* defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_ray_tracing_pipeline) + vkCmdSetRayTracingPipelineStackSizeKHR = (PFN_vkCmdSetRayTracingPipelineStackSizeKHR)load(context, "vkCmdSetRayTracingPipelineStackSizeKHR"); + vkCmdTraceRaysIndirectKHR = (PFN_vkCmdTraceRaysIndirectKHR)load(context, "vkCmdTraceRaysIndirectKHR"); + vkCmdTraceRaysKHR = (PFN_vkCmdTraceRaysKHR)load(context, "vkCmdTraceRaysKHR"); + vkCreateRayTracingPipelinesKHR = (PFN_vkCreateRayTracingPipelinesKHR)load(context, "vkCreateRayTracingPipelinesKHR"); + vkGetRayTracingCaptureReplayShaderGroupHandlesKHR = (PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR)load(context, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR"); + vkGetRayTracingShaderGroupHandlesKHR = (PFN_vkGetRayTracingShaderGroupHandlesKHR)load(context, "vkGetRayTracingShaderGroupHandlesKHR"); + vkGetRayTracingShaderGroupStackSizeKHR = (PFN_vkGetRayTracingShaderGroupStackSizeKHR)load(context, "vkGetRayTracingShaderGroupStackSizeKHR"); +#endif /* defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_sampler_ycbcr_conversion) + vkCreateSamplerYcbcrConversionKHR = (PFN_vkCreateSamplerYcbcrConversionKHR)load(context, "vkCreateSamplerYcbcrConversionKHR"); + vkDestroySamplerYcbcrConversionKHR = (PFN_vkDestroySamplerYcbcrConversionKHR)load(context, "vkDestroySamplerYcbcrConversionKHR"); +#endif /* defined(VK_KHR_sampler_ycbcr_conversion) */ +#if defined(VK_KHR_shared_presentable_image) + vkGetSwapchainStatusKHR = (PFN_vkGetSwapchainStatusKHR)load(context, "vkGetSwapchainStatusKHR"); +#endif /* defined(VK_KHR_shared_presentable_image) */ +#if defined(VK_KHR_swapchain) + vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR)load(context, "vkAcquireNextImageKHR"); + vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)load(context, "vkCreateSwapchainKHR"); + vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)load(context, "vkDestroySwapchainKHR"); + vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)load(context, "vkGetSwapchainImagesKHR"); + vkQueuePresentKHR = (PFN_vkQueuePresentKHR)load(context, "vkQueuePresentKHR"); +#endif /* defined(VK_KHR_swapchain) */ +#if defined(VK_KHR_synchronization2) + vkCmdPipelineBarrier2KHR = (PFN_vkCmdPipelineBarrier2KHR)load(context, "vkCmdPipelineBarrier2KHR"); + vkCmdResetEvent2KHR = (PFN_vkCmdResetEvent2KHR)load(context, "vkCmdResetEvent2KHR"); + vkCmdSetEvent2KHR = (PFN_vkCmdSetEvent2KHR)load(context, "vkCmdSetEvent2KHR"); + vkCmdWaitEvents2KHR = (PFN_vkCmdWaitEvents2KHR)load(context, "vkCmdWaitEvents2KHR"); + vkCmdWriteTimestamp2KHR = (PFN_vkCmdWriteTimestamp2KHR)load(context, "vkCmdWriteTimestamp2KHR"); + vkQueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)load(context, "vkQueueSubmit2KHR"); +#endif /* defined(VK_KHR_synchronization2) */ +#if defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) + vkCmdWriteBufferMarker2AMD = (PFN_vkCmdWriteBufferMarker2AMD)load(context, "vkCmdWriteBufferMarker2AMD"); +#endif /* defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) */ +#if defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) + vkGetQueueCheckpointData2NV = (PFN_vkGetQueueCheckpointData2NV)load(context, "vkGetQueueCheckpointData2NV"); +#endif /* defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_KHR_timeline_semaphore) + vkGetSemaphoreCounterValueKHR = (PFN_vkGetSemaphoreCounterValueKHR)load(context, "vkGetSemaphoreCounterValueKHR"); + vkSignalSemaphoreKHR = (PFN_vkSignalSemaphoreKHR)load(context, "vkSignalSemaphoreKHR"); + vkWaitSemaphoresKHR = (PFN_vkWaitSemaphoresKHR)load(context, "vkWaitSemaphoresKHR"); +#endif /* defined(VK_KHR_timeline_semaphore) */ +#if defined(VK_KHR_video_decode_queue) + vkCmdDecodeVideoKHR = (PFN_vkCmdDecodeVideoKHR)load(context, "vkCmdDecodeVideoKHR"); +#endif /* defined(VK_KHR_video_decode_queue) */ +#if defined(VK_KHR_video_encode_queue) + vkCmdEncodeVideoKHR = (PFN_vkCmdEncodeVideoKHR)load(context, "vkCmdEncodeVideoKHR"); + vkGetEncodedVideoSessionParametersKHR = (PFN_vkGetEncodedVideoSessionParametersKHR)load(context, "vkGetEncodedVideoSessionParametersKHR"); +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) + vkBindVideoSessionMemoryKHR = (PFN_vkBindVideoSessionMemoryKHR)load(context, "vkBindVideoSessionMemoryKHR"); + vkCmdBeginVideoCodingKHR = (PFN_vkCmdBeginVideoCodingKHR)load(context, "vkCmdBeginVideoCodingKHR"); + vkCmdControlVideoCodingKHR = (PFN_vkCmdControlVideoCodingKHR)load(context, "vkCmdControlVideoCodingKHR"); + vkCmdEndVideoCodingKHR = (PFN_vkCmdEndVideoCodingKHR)load(context, "vkCmdEndVideoCodingKHR"); + vkCreateVideoSessionKHR = (PFN_vkCreateVideoSessionKHR)load(context, "vkCreateVideoSessionKHR"); + vkCreateVideoSessionParametersKHR = (PFN_vkCreateVideoSessionParametersKHR)load(context, "vkCreateVideoSessionParametersKHR"); + vkDestroyVideoSessionKHR = (PFN_vkDestroyVideoSessionKHR)load(context, "vkDestroyVideoSessionKHR"); + vkDestroyVideoSessionParametersKHR = (PFN_vkDestroyVideoSessionParametersKHR)load(context, "vkDestroyVideoSessionParametersKHR"); + vkGetVideoSessionMemoryRequirementsKHR = (PFN_vkGetVideoSessionMemoryRequirementsKHR)load(context, "vkGetVideoSessionMemoryRequirementsKHR"); + vkUpdateVideoSessionParametersKHR = (PFN_vkUpdateVideoSessionParametersKHR)load(context, "vkUpdateVideoSessionParametersKHR"); +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_NVX_binary_import) + vkCmdCuLaunchKernelNVX = (PFN_vkCmdCuLaunchKernelNVX)load(context, "vkCmdCuLaunchKernelNVX"); + vkCreateCuFunctionNVX = (PFN_vkCreateCuFunctionNVX)load(context, "vkCreateCuFunctionNVX"); + vkCreateCuModuleNVX = (PFN_vkCreateCuModuleNVX)load(context, "vkCreateCuModuleNVX"); + vkDestroyCuFunctionNVX = (PFN_vkDestroyCuFunctionNVX)load(context, "vkDestroyCuFunctionNVX"); + vkDestroyCuModuleNVX = (PFN_vkDestroyCuModuleNVX)load(context, "vkDestroyCuModuleNVX"); +#endif /* defined(VK_NVX_binary_import) */ +#if defined(VK_NVX_image_view_handle) + vkGetImageViewAddressNVX = (PFN_vkGetImageViewAddressNVX)load(context, "vkGetImageViewAddressNVX"); + vkGetImageViewHandleNVX = (PFN_vkGetImageViewHandleNVX)load(context, "vkGetImageViewHandleNVX"); +#endif /* defined(VK_NVX_image_view_handle) */ +#if defined(VK_NV_clip_space_w_scaling) + vkCmdSetViewportWScalingNV = (PFN_vkCmdSetViewportWScalingNV)load(context, "vkCmdSetViewportWScalingNV"); +#endif /* defined(VK_NV_clip_space_w_scaling) */ +#if defined(VK_NV_copy_memory_indirect) + vkCmdCopyMemoryIndirectNV = (PFN_vkCmdCopyMemoryIndirectNV)load(context, "vkCmdCopyMemoryIndirectNV"); + vkCmdCopyMemoryToImageIndirectNV = (PFN_vkCmdCopyMemoryToImageIndirectNV)load(context, "vkCmdCopyMemoryToImageIndirectNV"); +#endif /* defined(VK_NV_copy_memory_indirect) */ +#if defined(VK_NV_cuda_kernel_launch) + vkCmdCudaLaunchKernelNV = (PFN_vkCmdCudaLaunchKernelNV)load(context, "vkCmdCudaLaunchKernelNV"); + vkCreateCudaFunctionNV = (PFN_vkCreateCudaFunctionNV)load(context, "vkCreateCudaFunctionNV"); + vkCreateCudaModuleNV = (PFN_vkCreateCudaModuleNV)load(context, "vkCreateCudaModuleNV"); + vkDestroyCudaFunctionNV = (PFN_vkDestroyCudaFunctionNV)load(context, "vkDestroyCudaFunctionNV"); + vkDestroyCudaModuleNV = (PFN_vkDestroyCudaModuleNV)load(context, "vkDestroyCudaModuleNV"); + vkGetCudaModuleCacheNV = (PFN_vkGetCudaModuleCacheNV)load(context, "vkGetCudaModuleCacheNV"); +#endif /* defined(VK_NV_cuda_kernel_launch) */ +#if defined(VK_NV_device_diagnostic_checkpoints) + vkCmdSetCheckpointNV = (PFN_vkCmdSetCheckpointNV)load(context, "vkCmdSetCheckpointNV"); + vkGetQueueCheckpointDataNV = (PFN_vkGetQueueCheckpointDataNV)load(context, "vkGetQueueCheckpointDataNV"); +#endif /* defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_NV_device_generated_commands) + vkCmdBindPipelineShaderGroupNV = (PFN_vkCmdBindPipelineShaderGroupNV)load(context, "vkCmdBindPipelineShaderGroupNV"); + vkCmdExecuteGeneratedCommandsNV = (PFN_vkCmdExecuteGeneratedCommandsNV)load(context, "vkCmdExecuteGeneratedCommandsNV"); + vkCmdPreprocessGeneratedCommandsNV = (PFN_vkCmdPreprocessGeneratedCommandsNV)load(context, "vkCmdPreprocessGeneratedCommandsNV"); + vkCreateIndirectCommandsLayoutNV = (PFN_vkCreateIndirectCommandsLayoutNV)load(context, "vkCreateIndirectCommandsLayoutNV"); + vkDestroyIndirectCommandsLayoutNV = (PFN_vkDestroyIndirectCommandsLayoutNV)load(context, "vkDestroyIndirectCommandsLayoutNV"); + vkGetGeneratedCommandsMemoryRequirementsNV = (PFN_vkGetGeneratedCommandsMemoryRequirementsNV)load(context, "vkGetGeneratedCommandsMemoryRequirementsNV"); +#endif /* defined(VK_NV_device_generated_commands) */ +#if defined(VK_NV_device_generated_commands_compute) + vkCmdUpdatePipelineIndirectBufferNV = (PFN_vkCmdUpdatePipelineIndirectBufferNV)load(context, "vkCmdUpdatePipelineIndirectBufferNV"); + vkGetPipelineIndirectDeviceAddressNV = (PFN_vkGetPipelineIndirectDeviceAddressNV)load(context, "vkGetPipelineIndirectDeviceAddressNV"); + vkGetPipelineIndirectMemoryRequirementsNV = (PFN_vkGetPipelineIndirectMemoryRequirementsNV)load(context, "vkGetPipelineIndirectMemoryRequirementsNV"); +#endif /* defined(VK_NV_device_generated_commands_compute) */ +#if defined(VK_NV_external_memory_rdma) + vkGetMemoryRemoteAddressNV = (PFN_vkGetMemoryRemoteAddressNV)load(context, "vkGetMemoryRemoteAddressNV"); +#endif /* defined(VK_NV_external_memory_rdma) */ +#if defined(VK_NV_external_memory_win32) + vkGetMemoryWin32HandleNV = (PFN_vkGetMemoryWin32HandleNV)load(context, "vkGetMemoryWin32HandleNV"); +#endif /* defined(VK_NV_external_memory_win32) */ +#if defined(VK_NV_fragment_shading_rate_enums) + vkCmdSetFragmentShadingRateEnumNV = (PFN_vkCmdSetFragmentShadingRateEnumNV)load(context, "vkCmdSetFragmentShadingRateEnumNV"); +#endif /* defined(VK_NV_fragment_shading_rate_enums) */ +#if defined(VK_NV_low_latency2) + vkGetLatencyTimingsNV = (PFN_vkGetLatencyTimingsNV)load(context, "vkGetLatencyTimingsNV"); + vkLatencySleepNV = (PFN_vkLatencySleepNV)load(context, "vkLatencySleepNV"); + vkQueueNotifyOutOfBandNV = (PFN_vkQueueNotifyOutOfBandNV)load(context, "vkQueueNotifyOutOfBandNV"); + vkSetLatencyMarkerNV = (PFN_vkSetLatencyMarkerNV)load(context, "vkSetLatencyMarkerNV"); + vkSetLatencySleepModeNV = (PFN_vkSetLatencySleepModeNV)load(context, "vkSetLatencySleepModeNV"); +#endif /* defined(VK_NV_low_latency2) */ +#if defined(VK_NV_memory_decompression) + vkCmdDecompressMemoryIndirectCountNV = (PFN_vkCmdDecompressMemoryIndirectCountNV)load(context, "vkCmdDecompressMemoryIndirectCountNV"); + vkCmdDecompressMemoryNV = (PFN_vkCmdDecompressMemoryNV)load(context, "vkCmdDecompressMemoryNV"); +#endif /* defined(VK_NV_memory_decompression) */ +#if defined(VK_NV_mesh_shader) + vkCmdDrawMeshTasksIndirectCountNV = (PFN_vkCmdDrawMeshTasksIndirectCountNV)load(context, "vkCmdDrawMeshTasksIndirectCountNV"); + vkCmdDrawMeshTasksIndirectNV = (PFN_vkCmdDrawMeshTasksIndirectNV)load(context, "vkCmdDrawMeshTasksIndirectNV"); + vkCmdDrawMeshTasksNV = (PFN_vkCmdDrawMeshTasksNV)load(context, "vkCmdDrawMeshTasksNV"); +#endif /* defined(VK_NV_mesh_shader) */ +#if defined(VK_NV_optical_flow) + vkBindOpticalFlowSessionImageNV = (PFN_vkBindOpticalFlowSessionImageNV)load(context, "vkBindOpticalFlowSessionImageNV"); + vkCmdOpticalFlowExecuteNV = (PFN_vkCmdOpticalFlowExecuteNV)load(context, "vkCmdOpticalFlowExecuteNV"); + vkCreateOpticalFlowSessionNV = (PFN_vkCreateOpticalFlowSessionNV)load(context, "vkCreateOpticalFlowSessionNV"); + vkDestroyOpticalFlowSessionNV = (PFN_vkDestroyOpticalFlowSessionNV)load(context, "vkDestroyOpticalFlowSessionNV"); +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_NV_ray_tracing) + vkBindAccelerationStructureMemoryNV = (PFN_vkBindAccelerationStructureMemoryNV)load(context, "vkBindAccelerationStructureMemoryNV"); + vkCmdBuildAccelerationStructureNV = (PFN_vkCmdBuildAccelerationStructureNV)load(context, "vkCmdBuildAccelerationStructureNV"); + vkCmdCopyAccelerationStructureNV = (PFN_vkCmdCopyAccelerationStructureNV)load(context, "vkCmdCopyAccelerationStructureNV"); + vkCmdTraceRaysNV = (PFN_vkCmdTraceRaysNV)load(context, "vkCmdTraceRaysNV"); + vkCmdWriteAccelerationStructuresPropertiesNV = (PFN_vkCmdWriteAccelerationStructuresPropertiesNV)load(context, "vkCmdWriteAccelerationStructuresPropertiesNV"); + vkCompileDeferredNV = (PFN_vkCompileDeferredNV)load(context, "vkCompileDeferredNV"); + vkCreateAccelerationStructureNV = (PFN_vkCreateAccelerationStructureNV)load(context, "vkCreateAccelerationStructureNV"); + vkCreateRayTracingPipelinesNV = (PFN_vkCreateRayTracingPipelinesNV)load(context, "vkCreateRayTracingPipelinesNV"); + vkDestroyAccelerationStructureNV = (PFN_vkDestroyAccelerationStructureNV)load(context, "vkDestroyAccelerationStructureNV"); + vkGetAccelerationStructureHandleNV = (PFN_vkGetAccelerationStructureHandleNV)load(context, "vkGetAccelerationStructureHandleNV"); + vkGetAccelerationStructureMemoryRequirementsNV = (PFN_vkGetAccelerationStructureMemoryRequirementsNV)load(context, "vkGetAccelerationStructureMemoryRequirementsNV"); + vkGetRayTracingShaderGroupHandlesNV = (PFN_vkGetRayTracingShaderGroupHandlesNV)load(context, "vkGetRayTracingShaderGroupHandlesNV"); +#endif /* defined(VK_NV_ray_tracing) */ +#if defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 + vkCmdSetExclusiveScissorEnableNV = (PFN_vkCmdSetExclusiveScissorEnableNV)load(context, "vkCmdSetExclusiveScissorEnableNV"); +#endif /* defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 */ +#if defined(VK_NV_scissor_exclusive) + vkCmdSetExclusiveScissorNV = (PFN_vkCmdSetExclusiveScissorNV)load(context, "vkCmdSetExclusiveScissorNV"); +#endif /* defined(VK_NV_scissor_exclusive) */ +#if defined(VK_NV_shading_rate_image) + vkCmdBindShadingRateImageNV = (PFN_vkCmdBindShadingRateImageNV)load(context, "vkCmdBindShadingRateImageNV"); + vkCmdSetCoarseSampleOrderNV = (PFN_vkCmdSetCoarseSampleOrderNV)load(context, "vkCmdSetCoarseSampleOrderNV"); + vkCmdSetViewportShadingRatePaletteNV = (PFN_vkCmdSetViewportShadingRatePaletteNV)load(context, "vkCmdSetViewportShadingRatePaletteNV"); +#endif /* defined(VK_NV_shading_rate_image) */ +#if defined(VK_QCOM_tile_properties) + vkGetDynamicRenderingTilePropertiesQCOM = (PFN_vkGetDynamicRenderingTilePropertiesQCOM)load(context, "vkGetDynamicRenderingTilePropertiesQCOM"); + vkGetFramebufferTilePropertiesQCOM = (PFN_vkGetFramebufferTilePropertiesQCOM)load(context, "vkGetFramebufferTilePropertiesQCOM"); +#endif /* defined(VK_QCOM_tile_properties) */ +#if defined(VK_QNX_external_memory_screen_buffer) + vkGetScreenBufferPropertiesQNX = (PFN_vkGetScreenBufferPropertiesQNX)load(context, "vkGetScreenBufferPropertiesQNX"); +#endif /* defined(VK_QNX_external_memory_screen_buffer) */ +#if defined(VK_VALVE_descriptor_set_host_mapping) + vkGetDescriptorSetHostMappingVALVE = (PFN_vkGetDescriptorSetHostMappingVALVE)load(context, "vkGetDescriptorSetHostMappingVALVE"); + vkGetDescriptorSetLayoutHostMappingInfoVALVE = (PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE)load(context, "vkGetDescriptorSetLayoutHostMappingInfoVALVE"); +#endif /* defined(VK_VALVE_descriptor_set_host_mapping) */ +#if (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) + vkCmdBindVertexBuffers2EXT = (PFN_vkCmdBindVertexBuffers2EXT)load(context, "vkCmdBindVertexBuffers2EXT"); + vkCmdSetCullModeEXT = (PFN_vkCmdSetCullModeEXT)load(context, "vkCmdSetCullModeEXT"); + vkCmdSetDepthBoundsTestEnableEXT = (PFN_vkCmdSetDepthBoundsTestEnableEXT)load(context, "vkCmdSetDepthBoundsTestEnableEXT"); + vkCmdSetDepthCompareOpEXT = (PFN_vkCmdSetDepthCompareOpEXT)load(context, "vkCmdSetDepthCompareOpEXT"); + vkCmdSetDepthTestEnableEXT = (PFN_vkCmdSetDepthTestEnableEXT)load(context, "vkCmdSetDepthTestEnableEXT"); + vkCmdSetDepthWriteEnableEXT = (PFN_vkCmdSetDepthWriteEnableEXT)load(context, "vkCmdSetDepthWriteEnableEXT"); + vkCmdSetFrontFaceEXT = (PFN_vkCmdSetFrontFaceEXT)load(context, "vkCmdSetFrontFaceEXT"); + vkCmdSetPrimitiveTopologyEXT = (PFN_vkCmdSetPrimitiveTopologyEXT)load(context, "vkCmdSetPrimitiveTopologyEXT"); + vkCmdSetScissorWithCountEXT = (PFN_vkCmdSetScissorWithCountEXT)load(context, "vkCmdSetScissorWithCountEXT"); + vkCmdSetStencilOpEXT = (PFN_vkCmdSetStencilOpEXT)load(context, "vkCmdSetStencilOpEXT"); + vkCmdSetStencilTestEnableEXT = (PFN_vkCmdSetStencilTestEnableEXT)load(context, "vkCmdSetStencilTestEnableEXT"); + vkCmdSetViewportWithCountEXT = (PFN_vkCmdSetViewportWithCountEXT)load(context, "vkCmdSetViewportWithCountEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) + vkCmdSetDepthBiasEnableEXT = (PFN_vkCmdSetDepthBiasEnableEXT)load(context, "vkCmdSetDepthBiasEnableEXT"); + vkCmdSetLogicOpEXT = (PFN_vkCmdSetLogicOpEXT)load(context, "vkCmdSetLogicOpEXT"); + vkCmdSetPatchControlPointsEXT = (PFN_vkCmdSetPatchControlPointsEXT)load(context, "vkCmdSetPatchControlPointsEXT"); + vkCmdSetPrimitiveRestartEnableEXT = (PFN_vkCmdSetPrimitiveRestartEnableEXT)load(context, "vkCmdSetPrimitiveRestartEnableEXT"); + vkCmdSetRasterizerDiscardEnableEXT = (PFN_vkCmdSetRasterizerDiscardEnableEXT)load(context, "vkCmdSetRasterizerDiscardEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) + vkCmdSetAlphaToCoverageEnableEXT = (PFN_vkCmdSetAlphaToCoverageEnableEXT)load(context, "vkCmdSetAlphaToCoverageEnableEXT"); + vkCmdSetAlphaToOneEnableEXT = (PFN_vkCmdSetAlphaToOneEnableEXT)load(context, "vkCmdSetAlphaToOneEnableEXT"); + vkCmdSetColorBlendEnableEXT = (PFN_vkCmdSetColorBlendEnableEXT)load(context, "vkCmdSetColorBlendEnableEXT"); + vkCmdSetColorBlendEquationEXT = (PFN_vkCmdSetColorBlendEquationEXT)load(context, "vkCmdSetColorBlendEquationEXT"); + vkCmdSetColorWriteMaskEXT = (PFN_vkCmdSetColorWriteMaskEXT)load(context, "vkCmdSetColorWriteMaskEXT"); + vkCmdSetDepthClampEnableEXT = (PFN_vkCmdSetDepthClampEnableEXT)load(context, "vkCmdSetDepthClampEnableEXT"); + vkCmdSetLogicOpEnableEXT = (PFN_vkCmdSetLogicOpEnableEXT)load(context, "vkCmdSetLogicOpEnableEXT"); + vkCmdSetPolygonModeEXT = (PFN_vkCmdSetPolygonModeEXT)load(context, "vkCmdSetPolygonModeEXT"); + vkCmdSetRasterizationSamplesEXT = (PFN_vkCmdSetRasterizationSamplesEXT)load(context, "vkCmdSetRasterizationSamplesEXT"); + vkCmdSetSampleMaskEXT = (PFN_vkCmdSetSampleMaskEXT)load(context, "vkCmdSetSampleMaskEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) + vkCmdSetTessellationDomainOriginEXT = (PFN_vkCmdSetTessellationDomainOriginEXT)load(context, "vkCmdSetTessellationDomainOriginEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) + vkCmdSetRasterizationStreamEXT = (PFN_vkCmdSetRasterizationStreamEXT)load(context, "vkCmdSetRasterizationStreamEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) + vkCmdSetConservativeRasterizationModeEXT = (PFN_vkCmdSetConservativeRasterizationModeEXT)load(context, "vkCmdSetConservativeRasterizationModeEXT"); + vkCmdSetExtraPrimitiveOverestimationSizeEXT = (PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT)load(context, "vkCmdSetExtraPrimitiveOverestimationSizeEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) + vkCmdSetDepthClipEnableEXT = (PFN_vkCmdSetDepthClipEnableEXT)load(context, "vkCmdSetDepthClipEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) + vkCmdSetSampleLocationsEnableEXT = (PFN_vkCmdSetSampleLocationsEnableEXT)load(context, "vkCmdSetSampleLocationsEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) + vkCmdSetColorBlendAdvancedEXT = (PFN_vkCmdSetColorBlendAdvancedEXT)load(context, "vkCmdSetColorBlendAdvancedEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) + vkCmdSetProvokingVertexModeEXT = (PFN_vkCmdSetProvokingVertexModeEXT)load(context, "vkCmdSetProvokingVertexModeEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) + vkCmdSetLineRasterizationModeEXT = (PFN_vkCmdSetLineRasterizationModeEXT)load(context, "vkCmdSetLineRasterizationModeEXT"); + vkCmdSetLineStippleEnableEXT = (PFN_vkCmdSetLineStippleEnableEXT)load(context, "vkCmdSetLineStippleEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) + vkCmdSetDepthClipNegativeOneToOneEXT = (PFN_vkCmdSetDepthClipNegativeOneToOneEXT)load(context, "vkCmdSetDepthClipNegativeOneToOneEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) + vkCmdSetViewportWScalingEnableNV = (PFN_vkCmdSetViewportWScalingEnableNV)load(context, "vkCmdSetViewportWScalingEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) + vkCmdSetViewportSwizzleNV = (PFN_vkCmdSetViewportSwizzleNV)load(context, "vkCmdSetViewportSwizzleNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) + vkCmdSetCoverageToColorEnableNV = (PFN_vkCmdSetCoverageToColorEnableNV)load(context, "vkCmdSetCoverageToColorEnableNV"); + vkCmdSetCoverageToColorLocationNV = (PFN_vkCmdSetCoverageToColorLocationNV)load(context, "vkCmdSetCoverageToColorLocationNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) + vkCmdSetCoverageModulationModeNV = (PFN_vkCmdSetCoverageModulationModeNV)load(context, "vkCmdSetCoverageModulationModeNV"); + vkCmdSetCoverageModulationTableEnableNV = (PFN_vkCmdSetCoverageModulationTableEnableNV)load(context, "vkCmdSetCoverageModulationTableEnableNV"); + vkCmdSetCoverageModulationTableNV = (PFN_vkCmdSetCoverageModulationTableNV)load(context, "vkCmdSetCoverageModulationTableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) + vkCmdSetShadingRateImageEnableNV = (PFN_vkCmdSetShadingRateImageEnableNV)load(context, "vkCmdSetShadingRateImageEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) + vkCmdSetRepresentativeFragmentTestEnableNV = (PFN_vkCmdSetRepresentativeFragmentTestEnableNV)load(context, "vkCmdSetRepresentativeFragmentTestEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) + vkCmdSetCoverageReductionModeNV = (PFN_vkCmdSetCoverageReductionModeNV)load(context, "vkCmdSetCoverageReductionModeNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) */ +#if (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) + vkGetImageSubresourceLayout2EXT = (PFN_vkGetImageSubresourceLayout2EXT)load(context, "vkGetImageSubresourceLayout2EXT"); +#endif /* (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) */ +#if (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) + vkCmdSetVertexInputEXT = (PFN_vkCmdSetVertexInputEXT)load(context, "vkCmdSetVertexInputEXT"); +#endif /* (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) */ +#if (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) + vkCmdPushDescriptorSetWithTemplateKHR = (PFN_vkCmdPushDescriptorSetWithTemplateKHR)load(context, "vkCmdPushDescriptorSetWithTemplateKHR"); +#endif /* (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR)load(context, "vkGetDeviceGroupPresentCapabilitiesKHR"); + vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR)load(context, "vkGetDeviceGroupSurfacePresentModesKHR"); +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR)load(context, "vkAcquireNextImage2KHR"); +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ + /* VOLK_GENERATE_LOAD_DEVICE */ +} + +static void volkGenLoadDeviceTable(struct VolkDeviceTable* table, void* context, PFN_vkVoidFunction (*load)(void*, const char*)) +{ + /* VOLK_GENERATE_LOAD_DEVICE_TABLE */ +#if defined(VK_VERSION_1_0) + table->vkAllocateCommandBuffers = (PFN_vkAllocateCommandBuffers)load(context, "vkAllocateCommandBuffers"); + table->vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets)load(context, "vkAllocateDescriptorSets"); + table->vkAllocateMemory = (PFN_vkAllocateMemory)load(context, "vkAllocateMemory"); + table->vkBeginCommandBuffer = (PFN_vkBeginCommandBuffer)load(context, "vkBeginCommandBuffer"); + table->vkBindBufferMemory = (PFN_vkBindBufferMemory)load(context, "vkBindBufferMemory"); + table->vkBindImageMemory = (PFN_vkBindImageMemory)load(context, "vkBindImageMemory"); + table->vkCmdBeginQuery = (PFN_vkCmdBeginQuery)load(context, "vkCmdBeginQuery"); + table->vkCmdBeginRenderPass = (PFN_vkCmdBeginRenderPass)load(context, "vkCmdBeginRenderPass"); + table->vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets)load(context, "vkCmdBindDescriptorSets"); + table->vkCmdBindIndexBuffer = (PFN_vkCmdBindIndexBuffer)load(context, "vkCmdBindIndexBuffer"); + table->vkCmdBindPipeline = (PFN_vkCmdBindPipeline)load(context, "vkCmdBindPipeline"); + table->vkCmdBindVertexBuffers = (PFN_vkCmdBindVertexBuffers)load(context, "vkCmdBindVertexBuffers"); + table->vkCmdBlitImage = (PFN_vkCmdBlitImage)load(context, "vkCmdBlitImage"); + table->vkCmdClearAttachments = (PFN_vkCmdClearAttachments)load(context, "vkCmdClearAttachments"); + table->vkCmdClearColorImage = (PFN_vkCmdClearColorImage)load(context, "vkCmdClearColorImage"); + table->vkCmdClearDepthStencilImage = (PFN_vkCmdClearDepthStencilImage)load(context, "vkCmdClearDepthStencilImage"); + table->vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)load(context, "vkCmdCopyBuffer"); + table->vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage)load(context, "vkCmdCopyBufferToImage"); + table->vkCmdCopyImage = (PFN_vkCmdCopyImage)load(context, "vkCmdCopyImage"); + table->vkCmdCopyImageToBuffer = (PFN_vkCmdCopyImageToBuffer)load(context, "vkCmdCopyImageToBuffer"); + table->vkCmdCopyQueryPoolResults = (PFN_vkCmdCopyQueryPoolResults)load(context, "vkCmdCopyQueryPoolResults"); + table->vkCmdDispatch = (PFN_vkCmdDispatch)load(context, "vkCmdDispatch"); + table->vkCmdDispatchIndirect = (PFN_vkCmdDispatchIndirect)load(context, "vkCmdDispatchIndirect"); + table->vkCmdDraw = (PFN_vkCmdDraw)load(context, "vkCmdDraw"); + table->vkCmdDrawIndexed = (PFN_vkCmdDrawIndexed)load(context, "vkCmdDrawIndexed"); + table->vkCmdDrawIndexedIndirect = (PFN_vkCmdDrawIndexedIndirect)load(context, "vkCmdDrawIndexedIndirect"); + table->vkCmdDrawIndirect = (PFN_vkCmdDrawIndirect)load(context, "vkCmdDrawIndirect"); + table->vkCmdEndQuery = (PFN_vkCmdEndQuery)load(context, "vkCmdEndQuery"); + table->vkCmdEndRenderPass = (PFN_vkCmdEndRenderPass)load(context, "vkCmdEndRenderPass"); + table->vkCmdExecuteCommands = (PFN_vkCmdExecuteCommands)load(context, "vkCmdExecuteCommands"); + table->vkCmdFillBuffer = (PFN_vkCmdFillBuffer)load(context, "vkCmdFillBuffer"); + table->vkCmdNextSubpass = (PFN_vkCmdNextSubpass)load(context, "vkCmdNextSubpass"); + table->vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier)load(context, "vkCmdPipelineBarrier"); + table->vkCmdPushConstants = (PFN_vkCmdPushConstants)load(context, "vkCmdPushConstants"); + table->vkCmdResetEvent = (PFN_vkCmdResetEvent)load(context, "vkCmdResetEvent"); + table->vkCmdResetQueryPool = (PFN_vkCmdResetQueryPool)load(context, "vkCmdResetQueryPool"); + table->vkCmdResolveImage = (PFN_vkCmdResolveImage)load(context, "vkCmdResolveImage"); + table->vkCmdSetBlendConstants = (PFN_vkCmdSetBlendConstants)load(context, "vkCmdSetBlendConstants"); + table->vkCmdSetDepthBias = (PFN_vkCmdSetDepthBias)load(context, "vkCmdSetDepthBias"); + table->vkCmdSetDepthBounds = (PFN_vkCmdSetDepthBounds)load(context, "vkCmdSetDepthBounds"); + table->vkCmdSetEvent = (PFN_vkCmdSetEvent)load(context, "vkCmdSetEvent"); + table->vkCmdSetLineWidth = (PFN_vkCmdSetLineWidth)load(context, "vkCmdSetLineWidth"); + table->vkCmdSetScissor = (PFN_vkCmdSetScissor)load(context, "vkCmdSetScissor"); + table->vkCmdSetStencilCompareMask = (PFN_vkCmdSetStencilCompareMask)load(context, "vkCmdSetStencilCompareMask"); + table->vkCmdSetStencilReference = (PFN_vkCmdSetStencilReference)load(context, "vkCmdSetStencilReference"); + table->vkCmdSetStencilWriteMask = (PFN_vkCmdSetStencilWriteMask)load(context, "vkCmdSetStencilWriteMask"); + table->vkCmdSetViewport = (PFN_vkCmdSetViewport)load(context, "vkCmdSetViewport"); + table->vkCmdUpdateBuffer = (PFN_vkCmdUpdateBuffer)load(context, "vkCmdUpdateBuffer"); + table->vkCmdWaitEvents = (PFN_vkCmdWaitEvents)load(context, "vkCmdWaitEvents"); + table->vkCmdWriteTimestamp = (PFN_vkCmdWriteTimestamp)load(context, "vkCmdWriteTimestamp"); + table->vkCreateBuffer = (PFN_vkCreateBuffer)load(context, "vkCreateBuffer"); + table->vkCreateBufferView = (PFN_vkCreateBufferView)load(context, "vkCreateBufferView"); + table->vkCreateCommandPool = (PFN_vkCreateCommandPool)load(context, "vkCreateCommandPool"); + table->vkCreateComputePipelines = (PFN_vkCreateComputePipelines)load(context, "vkCreateComputePipelines"); + table->vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool)load(context, "vkCreateDescriptorPool"); + table->vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout)load(context, "vkCreateDescriptorSetLayout"); + table->vkCreateEvent = (PFN_vkCreateEvent)load(context, "vkCreateEvent"); + table->vkCreateFence = (PFN_vkCreateFence)load(context, "vkCreateFence"); + table->vkCreateFramebuffer = (PFN_vkCreateFramebuffer)load(context, "vkCreateFramebuffer"); + table->vkCreateGraphicsPipelines = (PFN_vkCreateGraphicsPipelines)load(context, "vkCreateGraphicsPipelines"); + table->vkCreateImage = (PFN_vkCreateImage)load(context, "vkCreateImage"); + table->vkCreateImageView = (PFN_vkCreateImageView)load(context, "vkCreateImageView"); + table->vkCreatePipelineCache = (PFN_vkCreatePipelineCache)load(context, "vkCreatePipelineCache"); + table->vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout)load(context, "vkCreatePipelineLayout"); + table->vkCreateQueryPool = (PFN_vkCreateQueryPool)load(context, "vkCreateQueryPool"); + table->vkCreateRenderPass = (PFN_vkCreateRenderPass)load(context, "vkCreateRenderPass"); + table->vkCreateSampler = (PFN_vkCreateSampler)load(context, "vkCreateSampler"); + table->vkCreateSemaphore = (PFN_vkCreateSemaphore)load(context, "vkCreateSemaphore"); + table->vkCreateShaderModule = (PFN_vkCreateShaderModule)load(context, "vkCreateShaderModule"); + table->vkDestroyBuffer = (PFN_vkDestroyBuffer)load(context, "vkDestroyBuffer"); + table->vkDestroyBufferView = (PFN_vkDestroyBufferView)load(context, "vkDestroyBufferView"); + table->vkDestroyCommandPool = (PFN_vkDestroyCommandPool)load(context, "vkDestroyCommandPool"); + table->vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool)load(context, "vkDestroyDescriptorPool"); + table->vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout)load(context, "vkDestroyDescriptorSetLayout"); + table->vkDestroyDevice = (PFN_vkDestroyDevice)load(context, "vkDestroyDevice"); + table->vkDestroyEvent = (PFN_vkDestroyEvent)load(context, "vkDestroyEvent"); + table->vkDestroyFence = (PFN_vkDestroyFence)load(context, "vkDestroyFence"); + table->vkDestroyFramebuffer = (PFN_vkDestroyFramebuffer)load(context, "vkDestroyFramebuffer"); + table->vkDestroyImage = (PFN_vkDestroyImage)load(context, "vkDestroyImage"); + table->vkDestroyImageView = (PFN_vkDestroyImageView)load(context, "vkDestroyImageView"); + table->vkDestroyPipeline = (PFN_vkDestroyPipeline)load(context, "vkDestroyPipeline"); + table->vkDestroyPipelineCache = (PFN_vkDestroyPipelineCache)load(context, "vkDestroyPipelineCache"); + table->vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout)load(context, "vkDestroyPipelineLayout"); + table->vkDestroyQueryPool = (PFN_vkDestroyQueryPool)load(context, "vkDestroyQueryPool"); + table->vkDestroyRenderPass = (PFN_vkDestroyRenderPass)load(context, "vkDestroyRenderPass"); + table->vkDestroySampler = (PFN_vkDestroySampler)load(context, "vkDestroySampler"); + table->vkDestroySemaphore = (PFN_vkDestroySemaphore)load(context, "vkDestroySemaphore"); + table->vkDestroyShaderModule = (PFN_vkDestroyShaderModule)load(context, "vkDestroyShaderModule"); + table->vkDeviceWaitIdle = (PFN_vkDeviceWaitIdle)load(context, "vkDeviceWaitIdle"); + table->vkEndCommandBuffer = (PFN_vkEndCommandBuffer)load(context, "vkEndCommandBuffer"); + table->vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)load(context, "vkFlushMappedMemoryRanges"); + table->vkFreeCommandBuffers = (PFN_vkFreeCommandBuffers)load(context, "vkFreeCommandBuffers"); + table->vkFreeDescriptorSets = (PFN_vkFreeDescriptorSets)load(context, "vkFreeDescriptorSets"); + table->vkFreeMemory = (PFN_vkFreeMemory)load(context, "vkFreeMemory"); + table->vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)load(context, "vkGetBufferMemoryRequirements"); + table->vkGetDeviceMemoryCommitment = (PFN_vkGetDeviceMemoryCommitment)load(context, "vkGetDeviceMemoryCommitment"); + table->vkGetDeviceQueue = (PFN_vkGetDeviceQueue)load(context, "vkGetDeviceQueue"); + table->vkGetEventStatus = (PFN_vkGetEventStatus)load(context, "vkGetEventStatus"); + table->vkGetFenceStatus = (PFN_vkGetFenceStatus)load(context, "vkGetFenceStatus"); + table->vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)load(context, "vkGetImageMemoryRequirements"); + table->vkGetImageSparseMemoryRequirements = (PFN_vkGetImageSparseMemoryRequirements)load(context, "vkGetImageSparseMemoryRequirements"); + table->vkGetImageSubresourceLayout = (PFN_vkGetImageSubresourceLayout)load(context, "vkGetImageSubresourceLayout"); + table->vkGetPipelineCacheData = (PFN_vkGetPipelineCacheData)load(context, "vkGetPipelineCacheData"); + table->vkGetQueryPoolResults = (PFN_vkGetQueryPoolResults)load(context, "vkGetQueryPoolResults"); + table->vkGetRenderAreaGranularity = (PFN_vkGetRenderAreaGranularity)load(context, "vkGetRenderAreaGranularity"); + table->vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)load(context, "vkInvalidateMappedMemoryRanges"); + table->vkMapMemory = (PFN_vkMapMemory)load(context, "vkMapMemory"); + table->vkMergePipelineCaches = (PFN_vkMergePipelineCaches)load(context, "vkMergePipelineCaches"); + table->vkQueueBindSparse = (PFN_vkQueueBindSparse)load(context, "vkQueueBindSparse"); + table->vkQueueSubmit = (PFN_vkQueueSubmit)load(context, "vkQueueSubmit"); + table->vkQueueWaitIdle = (PFN_vkQueueWaitIdle)load(context, "vkQueueWaitIdle"); + table->vkResetCommandBuffer = (PFN_vkResetCommandBuffer)load(context, "vkResetCommandBuffer"); + table->vkResetCommandPool = (PFN_vkResetCommandPool)load(context, "vkResetCommandPool"); + table->vkResetDescriptorPool = (PFN_vkResetDescriptorPool)load(context, "vkResetDescriptorPool"); + table->vkResetEvent = (PFN_vkResetEvent)load(context, "vkResetEvent"); + table->vkResetFences = (PFN_vkResetFences)load(context, "vkResetFences"); + table->vkSetEvent = (PFN_vkSetEvent)load(context, "vkSetEvent"); + table->vkUnmapMemory = (PFN_vkUnmapMemory)load(context, "vkUnmapMemory"); + table->vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets)load(context, "vkUpdateDescriptorSets"); + table->vkWaitForFences = (PFN_vkWaitForFences)load(context, "vkWaitForFences"); +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) + table->vkBindBufferMemory2 = (PFN_vkBindBufferMemory2)load(context, "vkBindBufferMemory2"); + table->vkBindImageMemory2 = (PFN_vkBindImageMemory2)load(context, "vkBindImageMemory2"); + table->vkCmdDispatchBase = (PFN_vkCmdDispatchBase)load(context, "vkCmdDispatchBase"); + table->vkCmdSetDeviceMask = (PFN_vkCmdSetDeviceMask)load(context, "vkCmdSetDeviceMask"); + table->vkCreateDescriptorUpdateTemplate = (PFN_vkCreateDescriptorUpdateTemplate)load(context, "vkCreateDescriptorUpdateTemplate"); + table->vkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion)load(context, "vkCreateSamplerYcbcrConversion"); + table->vkDestroyDescriptorUpdateTemplate = (PFN_vkDestroyDescriptorUpdateTemplate)load(context, "vkDestroyDescriptorUpdateTemplate"); + table->vkDestroySamplerYcbcrConversion = (PFN_vkDestroySamplerYcbcrConversion)load(context, "vkDestroySamplerYcbcrConversion"); + table->vkGetBufferMemoryRequirements2 = (PFN_vkGetBufferMemoryRequirements2)load(context, "vkGetBufferMemoryRequirements2"); + table->vkGetDescriptorSetLayoutSupport = (PFN_vkGetDescriptorSetLayoutSupport)load(context, "vkGetDescriptorSetLayoutSupport"); + table->vkGetDeviceGroupPeerMemoryFeatures = (PFN_vkGetDeviceGroupPeerMemoryFeatures)load(context, "vkGetDeviceGroupPeerMemoryFeatures"); + table->vkGetDeviceQueue2 = (PFN_vkGetDeviceQueue2)load(context, "vkGetDeviceQueue2"); + table->vkGetImageMemoryRequirements2 = (PFN_vkGetImageMemoryRequirements2)load(context, "vkGetImageMemoryRequirements2"); + table->vkGetImageSparseMemoryRequirements2 = (PFN_vkGetImageSparseMemoryRequirements2)load(context, "vkGetImageSparseMemoryRequirements2"); + table->vkTrimCommandPool = (PFN_vkTrimCommandPool)load(context, "vkTrimCommandPool"); + table->vkUpdateDescriptorSetWithTemplate = (PFN_vkUpdateDescriptorSetWithTemplate)load(context, "vkUpdateDescriptorSetWithTemplate"); +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_2) + table->vkCmdBeginRenderPass2 = (PFN_vkCmdBeginRenderPass2)load(context, "vkCmdBeginRenderPass2"); + table->vkCmdDrawIndexedIndirectCount = (PFN_vkCmdDrawIndexedIndirectCount)load(context, "vkCmdDrawIndexedIndirectCount"); + table->vkCmdDrawIndirectCount = (PFN_vkCmdDrawIndirectCount)load(context, "vkCmdDrawIndirectCount"); + table->vkCmdEndRenderPass2 = (PFN_vkCmdEndRenderPass2)load(context, "vkCmdEndRenderPass2"); + table->vkCmdNextSubpass2 = (PFN_vkCmdNextSubpass2)load(context, "vkCmdNextSubpass2"); + table->vkCreateRenderPass2 = (PFN_vkCreateRenderPass2)load(context, "vkCreateRenderPass2"); + table->vkGetBufferDeviceAddress = (PFN_vkGetBufferDeviceAddress)load(context, "vkGetBufferDeviceAddress"); + table->vkGetBufferOpaqueCaptureAddress = (PFN_vkGetBufferOpaqueCaptureAddress)load(context, "vkGetBufferOpaqueCaptureAddress"); + table->vkGetDeviceMemoryOpaqueCaptureAddress = (PFN_vkGetDeviceMemoryOpaqueCaptureAddress)load(context, "vkGetDeviceMemoryOpaqueCaptureAddress"); + table->vkGetSemaphoreCounterValue = (PFN_vkGetSemaphoreCounterValue)load(context, "vkGetSemaphoreCounterValue"); + table->vkResetQueryPool = (PFN_vkResetQueryPool)load(context, "vkResetQueryPool"); + table->vkSignalSemaphore = (PFN_vkSignalSemaphore)load(context, "vkSignalSemaphore"); + table->vkWaitSemaphores = (PFN_vkWaitSemaphores)load(context, "vkWaitSemaphores"); +#endif /* defined(VK_VERSION_1_2) */ +#if defined(VK_VERSION_1_3) + table->vkCmdBeginRendering = (PFN_vkCmdBeginRendering)load(context, "vkCmdBeginRendering"); + table->vkCmdBindVertexBuffers2 = (PFN_vkCmdBindVertexBuffers2)load(context, "vkCmdBindVertexBuffers2"); + table->vkCmdBlitImage2 = (PFN_vkCmdBlitImage2)load(context, "vkCmdBlitImage2"); + table->vkCmdCopyBuffer2 = (PFN_vkCmdCopyBuffer2)load(context, "vkCmdCopyBuffer2"); + table->vkCmdCopyBufferToImage2 = (PFN_vkCmdCopyBufferToImage2)load(context, "vkCmdCopyBufferToImage2"); + table->vkCmdCopyImage2 = (PFN_vkCmdCopyImage2)load(context, "vkCmdCopyImage2"); + table->vkCmdCopyImageToBuffer2 = (PFN_vkCmdCopyImageToBuffer2)load(context, "vkCmdCopyImageToBuffer2"); + table->vkCmdEndRendering = (PFN_vkCmdEndRendering)load(context, "vkCmdEndRendering"); + table->vkCmdPipelineBarrier2 = (PFN_vkCmdPipelineBarrier2)load(context, "vkCmdPipelineBarrier2"); + table->vkCmdResetEvent2 = (PFN_vkCmdResetEvent2)load(context, "vkCmdResetEvent2"); + table->vkCmdResolveImage2 = (PFN_vkCmdResolveImage2)load(context, "vkCmdResolveImage2"); + table->vkCmdSetCullMode = (PFN_vkCmdSetCullMode)load(context, "vkCmdSetCullMode"); + table->vkCmdSetDepthBiasEnable = (PFN_vkCmdSetDepthBiasEnable)load(context, "vkCmdSetDepthBiasEnable"); + table->vkCmdSetDepthBoundsTestEnable = (PFN_vkCmdSetDepthBoundsTestEnable)load(context, "vkCmdSetDepthBoundsTestEnable"); + table->vkCmdSetDepthCompareOp = (PFN_vkCmdSetDepthCompareOp)load(context, "vkCmdSetDepthCompareOp"); + table->vkCmdSetDepthTestEnable = (PFN_vkCmdSetDepthTestEnable)load(context, "vkCmdSetDepthTestEnable"); + table->vkCmdSetDepthWriteEnable = (PFN_vkCmdSetDepthWriteEnable)load(context, "vkCmdSetDepthWriteEnable"); + table->vkCmdSetEvent2 = (PFN_vkCmdSetEvent2)load(context, "vkCmdSetEvent2"); + table->vkCmdSetFrontFace = (PFN_vkCmdSetFrontFace)load(context, "vkCmdSetFrontFace"); + table->vkCmdSetPrimitiveRestartEnable = (PFN_vkCmdSetPrimitiveRestartEnable)load(context, "vkCmdSetPrimitiveRestartEnable"); + table->vkCmdSetPrimitiveTopology = (PFN_vkCmdSetPrimitiveTopology)load(context, "vkCmdSetPrimitiveTopology"); + table->vkCmdSetRasterizerDiscardEnable = (PFN_vkCmdSetRasterizerDiscardEnable)load(context, "vkCmdSetRasterizerDiscardEnable"); + table->vkCmdSetScissorWithCount = (PFN_vkCmdSetScissorWithCount)load(context, "vkCmdSetScissorWithCount"); + table->vkCmdSetStencilOp = (PFN_vkCmdSetStencilOp)load(context, "vkCmdSetStencilOp"); + table->vkCmdSetStencilTestEnable = (PFN_vkCmdSetStencilTestEnable)load(context, "vkCmdSetStencilTestEnable"); + table->vkCmdSetViewportWithCount = (PFN_vkCmdSetViewportWithCount)load(context, "vkCmdSetViewportWithCount"); + table->vkCmdWaitEvents2 = (PFN_vkCmdWaitEvents2)load(context, "vkCmdWaitEvents2"); + table->vkCmdWriteTimestamp2 = (PFN_vkCmdWriteTimestamp2)load(context, "vkCmdWriteTimestamp2"); + table->vkCreatePrivateDataSlot = (PFN_vkCreatePrivateDataSlot)load(context, "vkCreatePrivateDataSlot"); + table->vkDestroyPrivateDataSlot = (PFN_vkDestroyPrivateDataSlot)load(context, "vkDestroyPrivateDataSlot"); + table->vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)load(context, "vkGetDeviceBufferMemoryRequirements"); + table->vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)load(context, "vkGetDeviceImageMemoryRequirements"); + table->vkGetDeviceImageSparseMemoryRequirements = (PFN_vkGetDeviceImageSparseMemoryRequirements)load(context, "vkGetDeviceImageSparseMemoryRequirements"); + table->vkGetPrivateData = (PFN_vkGetPrivateData)load(context, "vkGetPrivateData"); + table->vkQueueSubmit2 = (PFN_vkQueueSubmit2)load(context, "vkQueueSubmit2"); + table->vkSetPrivateData = (PFN_vkSetPrivateData)load(context, "vkSetPrivateData"); +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_AMDX_shader_enqueue) + table->vkCmdDispatchGraphAMDX = (PFN_vkCmdDispatchGraphAMDX)load(context, "vkCmdDispatchGraphAMDX"); + table->vkCmdDispatchGraphIndirectAMDX = (PFN_vkCmdDispatchGraphIndirectAMDX)load(context, "vkCmdDispatchGraphIndirectAMDX"); + table->vkCmdDispatchGraphIndirectCountAMDX = (PFN_vkCmdDispatchGraphIndirectCountAMDX)load(context, "vkCmdDispatchGraphIndirectCountAMDX"); + table->vkCmdInitializeGraphScratchMemoryAMDX = (PFN_vkCmdInitializeGraphScratchMemoryAMDX)load(context, "vkCmdInitializeGraphScratchMemoryAMDX"); + table->vkCreateExecutionGraphPipelinesAMDX = (PFN_vkCreateExecutionGraphPipelinesAMDX)load(context, "vkCreateExecutionGraphPipelinesAMDX"); + table->vkGetExecutionGraphPipelineNodeIndexAMDX = (PFN_vkGetExecutionGraphPipelineNodeIndexAMDX)load(context, "vkGetExecutionGraphPipelineNodeIndexAMDX"); + table->vkGetExecutionGraphPipelineScratchSizeAMDX = (PFN_vkGetExecutionGraphPipelineScratchSizeAMDX)load(context, "vkGetExecutionGraphPipelineScratchSizeAMDX"); +#endif /* defined(VK_AMDX_shader_enqueue) */ +#if defined(VK_AMD_anti_lag) + table->vkAntiLagUpdateAMD = (PFN_vkAntiLagUpdateAMD)load(context, "vkAntiLagUpdateAMD"); +#endif /* defined(VK_AMD_anti_lag) */ +#if defined(VK_AMD_buffer_marker) + table->vkCmdWriteBufferMarkerAMD = (PFN_vkCmdWriteBufferMarkerAMD)load(context, "vkCmdWriteBufferMarkerAMD"); +#endif /* defined(VK_AMD_buffer_marker) */ +#if defined(VK_AMD_display_native_hdr) + table->vkSetLocalDimmingAMD = (PFN_vkSetLocalDimmingAMD)load(context, "vkSetLocalDimmingAMD"); +#endif /* defined(VK_AMD_display_native_hdr) */ +#if defined(VK_AMD_draw_indirect_count) + table->vkCmdDrawIndexedIndirectCountAMD = (PFN_vkCmdDrawIndexedIndirectCountAMD)load(context, "vkCmdDrawIndexedIndirectCountAMD"); + table->vkCmdDrawIndirectCountAMD = (PFN_vkCmdDrawIndirectCountAMD)load(context, "vkCmdDrawIndirectCountAMD"); +#endif /* defined(VK_AMD_draw_indirect_count) */ +#if defined(VK_AMD_shader_info) + table->vkGetShaderInfoAMD = (PFN_vkGetShaderInfoAMD)load(context, "vkGetShaderInfoAMD"); +#endif /* defined(VK_AMD_shader_info) */ +#if defined(VK_ANDROID_external_memory_android_hardware_buffer) + table->vkGetAndroidHardwareBufferPropertiesANDROID = (PFN_vkGetAndroidHardwareBufferPropertiesANDROID)load(context, "vkGetAndroidHardwareBufferPropertiesANDROID"); + table->vkGetMemoryAndroidHardwareBufferANDROID = (PFN_vkGetMemoryAndroidHardwareBufferANDROID)load(context, "vkGetMemoryAndroidHardwareBufferANDROID"); +#endif /* defined(VK_ANDROID_external_memory_android_hardware_buffer) */ +#if defined(VK_EXT_attachment_feedback_loop_dynamic_state) + table->vkCmdSetAttachmentFeedbackLoopEnableEXT = (PFN_vkCmdSetAttachmentFeedbackLoopEnableEXT)load(context, "vkCmdSetAttachmentFeedbackLoopEnableEXT"); +#endif /* defined(VK_EXT_attachment_feedback_loop_dynamic_state) */ +#if defined(VK_EXT_buffer_device_address) + table->vkGetBufferDeviceAddressEXT = (PFN_vkGetBufferDeviceAddressEXT)load(context, "vkGetBufferDeviceAddressEXT"); +#endif /* defined(VK_EXT_buffer_device_address) */ +#if defined(VK_EXT_calibrated_timestamps) + table->vkGetCalibratedTimestampsEXT = (PFN_vkGetCalibratedTimestampsEXT)load(context, "vkGetCalibratedTimestampsEXT"); +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_color_write_enable) + table->vkCmdSetColorWriteEnableEXT = (PFN_vkCmdSetColorWriteEnableEXT)load(context, "vkCmdSetColorWriteEnableEXT"); +#endif /* defined(VK_EXT_color_write_enable) */ +#if defined(VK_EXT_conditional_rendering) + table->vkCmdBeginConditionalRenderingEXT = (PFN_vkCmdBeginConditionalRenderingEXT)load(context, "vkCmdBeginConditionalRenderingEXT"); + table->vkCmdEndConditionalRenderingEXT = (PFN_vkCmdEndConditionalRenderingEXT)load(context, "vkCmdEndConditionalRenderingEXT"); +#endif /* defined(VK_EXT_conditional_rendering) */ +#if defined(VK_EXT_debug_marker) + table->vkCmdDebugMarkerBeginEXT = (PFN_vkCmdDebugMarkerBeginEXT)load(context, "vkCmdDebugMarkerBeginEXT"); + table->vkCmdDebugMarkerEndEXT = (PFN_vkCmdDebugMarkerEndEXT)load(context, "vkCmdDebugMarkerEndEXT"); + table->vkCmdDebugMarkerInsertEXT = (PFN_vkCmdDebugMarkerInsertEXT)load(context, "vkCmdDebugMarkerInsertEXT"); + table->vkDebugMarkerSetObjectNameEXT = (PFN_vkDebugMarkerSetObjectNameEXT)load(context, "vkDebugMarkerSetObjectNameEXT"); + table->vkDebugMarkerSetObjectTagEXT = (PFN_vkDebugMarkerSetObjectTagEXT)load(context, "vkDebugMarkerSetObjectTagEXT"); +#endif /* defined(VK_EXT_debug_marker) */ +#if defined(VK_EXT_depth_bias_control) + table->vkCmdSetDepthBias2EXT = (PFN_vkCmdSetDepthBias2EXT)load(context, "vkCmdSetDepthBias2EXT"); +#endif /* defined(VK_EXT_depth_bias_control) */ +#if defined(VK_EXT_descriptor_buffer) + table->vkCmdBindDescriptorBufferEmbeddedSamplersEXT = (PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT)load(context, "vkCmdBindDescriptorBufferEmbeddedSamplersEXT"); + table->vkCmdBindDescriptorBuffersEXT = (PFN_vkCmdBindDescriptorBuffersEXT)load(context, "vkCmdBindDescriptorBuffersEXT"); + table->vkCmdSetDescriptorBufferOffsetsEXT = (PFN_vkCmdSetDescriptorBufferOffsetsEXT)load(context, "vkCmdSetDescriptorBufferOffsetsEXT"); + table->vkGetBufferOpaqueCaptureDescriptorDataEXT = (PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT)load(context, "vkGetBufferOpaqueCaptureDescriptorDataEXT"); + table->vkGetDescriptorEXT = (PFN_vkGetDescriptorEXT)load(context, "vkGetDescriptorEXT"); + table->vkGetDescriptorSetLayoutBindingOffsetEXT = (PFN_vkGetDescriptorSetLayoutBindingOffsetEXT)load(context, "vkGetDescriptorSetLayoutBindingOffsetEXT"); + table->vkGetDescriptorSetLayoutSizeEXT = (PFN_vkGetDescriptorSetLayoutSizeEXT)load(context, "vkGetDescriptorSetLayoutSizeEXT"); + table->vkGetImageOpaqueCaptureDescriptorDataEXT = (PFN_vkGetImageOpaqueCaptureDescriptorDataEXT)load(context, "vkGetImageOpaqueCaptureDescriptorDataEXT"); + table->vkGetImageViewOpaqueCaptureDescriptorDataEXT = (PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT)load(context, "vkGetImageViewOpaqueCaptureDescriptorDataEXT"); + table->vkGetSamplerOpaqueCaptureDescriptorDataEXT = (PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT)load(context, "vkGetSamplerOpaqueCaptureDescriptorDataEXT"); +#endif /* defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) + table->vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT = (PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT)load(context, "vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT"); +#endif /* defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) */ +#if defined(VK_EXT_device_fault) + table->vkGetDeviceFaultInfoEXT = (PFN_vkGetDeviceFaultInfoEXT)load(context, "vkGetDeviceFaultInfoEXT"); +#endif /* defined(VK_EXT_device_fault) */ +#if defined(VK_EXT_discard_rectangles) + table->vkCmdSetDiscardRectangleEXT = (PFN_vkCmdSetDiscardRectangleEXT)load(context, "vkCmdSetDiscardRectangleEXT"); +#endif /* defined(VK_EXT_discard_rectangles) */ +#if defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 + table->vkCmdSetDiscardRectangleEnableEXT = (PFN_vkCmdSetDiscardRectangleEnableEXT)load(context, "vkCmdSetDiscardRectangleEnableEXT"); + table->vkCmdSetDiscardRectangleModeEXT = (PFN_vkCmdSetDiscardRectangleModeEXT)load(context, "vkCmdSetDiscardRectangleModeEXT"); +#endif /* defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 */ +#if defined(VK_EXT_display_control) + table->vkDisplayPowerControlEXT = (PFN_vkDisplayPowerControlEXT)load(context, "vkDisplayPowerControlEXT"); + table->vkGetSwapchainCounterEXT = (PFN_vkGetSwapchainCounterEXT)load(context, "vkGetSwapchainCounterEXT"); + table->vkRegisterDeviceEventEXT = (PFN_vkRegisterDeviceEventEXT)load(context, "vkRegisterDeviceEventEXT"); + table->vkRegisterDisplayEventEXT = (PFN_vkRegisterDisplayEventEXT)load(context, "vkRegisterDisplayEventEXT"); +#endif /* defined(VK_EXT_display_control) */ +#if defined(VK_EXT_external_memory_host) + table->vkGetMemoryHostPointerPropertiesEXT = (PFN_vkGetMemoryHostPointerPropertiesEXT)load(context, "vkGetMemoryHostPointerPropertiesEXT"); +#endif /* defined(VK_EXT_external_memory_host) */ +#if defined(VK_EXT_full_screen_exclusive) + table->vkAcquireFullScreenExclusiveModeEXT = (PFN_vkAcquireFullScreenExclusiveModeEXT)load(context, "vkAcquireFullScreenExclusiveModeEXT"); + table->vkReleaseFullScreenExclusiveModeEXT = (PFN_vkReleaseFullScreenExclusiveModeEXT)load(context, "vkReleaseFullScreenExclusiveModeEXT"); +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) + table->vkGetDeviceGroupSurfacePresentModes2EXT = (PFN_vkGetDeviceGroupSurfacePresentModes2EXT)load(context, "vkGetDeviceGroupSurfacePresentModes2EXT"); +#endif /* defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) */ +#if defined(VK_EXT_hdr_metadata) + table->vkSetHdrMetadataEXT = (PFN_vkSetHdrMetadataEXT)load(context, "vkSetHdrMetadataEXT"); +#endif /* defined(VK_EXT_hdr_metadata) */ +#if defined(VK_EXT_host_image_copy) + table->vkCopyImageToImageEXT = (PFN_vkCopyImageToImageEXT)load(context, "vkCopyImageToImageEXT"); + table->vkCopyImageToMemoryEXT = (PFN_vkCopyImageToMemoryEXT)load(context, "vkCopyImageToMemoryEXT"); + table->vkCopyMemoryToImageEXT = (PFN_vkCopyMemoryToImageEXT)load(context, "vkCopyMemoryToImageEXT"); + table->vkTransitionImageLayoutEXT = (PFN_vkTransitionImageLayoutEXT)load(context, "vkTransitionImageLayoutEXT"); +#endif /* defined(VK_EXT_host_image_copy) */ +#if defined(VK_EXT_host_query_reset) + table->vkResetQueryPoolEXT = (PFN_vkResetQueryPoolEXT)load(context, "vkResetQueryPoolEXT"); +#endif /* defined(VK_EXT_host_query_reset) */ +#if defined(VK_EXT_image_drm_format_modifier) + table->vkGetImageDrmFormatModifierPropertiesEXT = (PFN_vkGetImageDrmFormatModifierPropertiesEXT)load(context, "vkGetImageDrmFormatModifierPropertiesEXT"); +#endif /* defined(VK_EXT_image_drm_format_modifier) */ +#if defined(VK_EXT_line_rasterization) + table->vkCmdSetLineStippleEXT = (PFN_vkCmdSetLineStippleEXT)load(context, "vkCmdSetLineStippleEXT"); +#endif /* defined(VK_EXT_line_rasterization) */ +#if defined(VK_EXT_mesh_shader) + table->vkCmdDrawMeshTasksEXT = (PFN_vkCmdDrawMeshTasksEXT)load(context, "vkCmdDrawMeshTasksEXT"); + table->vkCmdDrawMeshTasksIndirectCountEXT = (PFN_vkCmdDrawMeshTasksIndirectCountEXT)load(context, "vkCmdDrawMeshTasksIndirectCountEXT"); + table->vkCmdDrawMeshTasksIndirectEXT = (PFN_vkCmdDrawMeshTasksIndirectEXT)load(context, "vkCmdDrawMeshTasksIndirectEXT"); +#endif /* defined(VK_EXT_mesh_shader) */ +#if defined(VK_EXT_metal_objects) + table->vkExportMetalObjectsEXT = (PFN_vkExportMetalObjectsEXT)load(context, "vkExportMetalObjectsEXT"); +#endif /* defined(VK_EXT_metal_objects) */ +#if defined(VK_EXT_multi_draw) + table->vkCmdDrawMultiEXT = (PFN_vkCmdDrawMultiEXT)load(context, "vkCmdDrawMultiEXT"); + table->vkCmdDrawMultiIndexedEXT = (PFN_vkCmdDrawMultiIndexedEXT)load(context, "vkCmdDrawMultiIndexedEXT"); +#endif /* defined(VK_EXT_multi_draw) */ +#if defined(VK_EXT_opacity_micromap) + table->vkBuildMicromapsEXT = (PFN_vkBuildMicromapsEXT)load(context, "vkBuildMicromapsEXT"); + table->vkCmdBuildMicromapsEXT = (PFN_vkCmdBuildMicromapsEXT)load(context, "vkCmdBuildMicromapsEXT"); + table->vkCmdCopyMemoryToMicromapEXT = (PFN_vkCmdCopyMemoryToMicromapEXT)load(context, "vkCmdCopyMemoryToMicromapEXT"); + table->vkCmdCopyMicromapEXT = (PFN_vkCmdCopyMicromapEXT)load(context, "vkCmdCopyMicromapEXT"); + table->vkCmdCopyMicromapToMemoryEXT = (PFN_vkCmdCopyMicromapToMemoryEXT)load(context, "vkCmdCopyMicromapToMemoryEXT"); + table->vkCmdWriteMicromapsPropertiesEXT = (PFN_vkCmdWriteMicromapsPropertiesEXT)load(context, "vkCmdWriteMicromapsPropertiesEXT"); + table->vkCopyMemoryToMicromapEXT = (PFN_vkCopyMemoryToMicromapEXT)load(context, "vkCopyMemoryToMicromapEXT"); + table->vkCopyMicromapEXT = (PFN_vkCopyMicromapEXT)load(context, "vkCopyMicromapEXT"); + table->vkCopyMicromapToMemoryEXT = (PFN_vkCopyMicromapToMemoryEXT)load(context, "vkCopyMicromapToMemoryEXT"); + table->vkCreateMicromapEXT = (PFN_vkCreateMicromapEXT)load(context, "vkCreateMicromapEXT"); + table->vkDestroyMicromapEXT = (PFN_vkDestroyMicromapEXT)load(context, "vkDestroyMicromapEXT"); + table->vkGetDeviceMicromapCompatibilityEXT = (PFN_vkGetDeviceMicromapCompatibilityEXT)load(context, "vkGetDeviceMicromapCompatibilityEXT"); + table->vkGetMicromapBuildSizesEXT = (PFN_vkGetMicromapBuildSizesEXT)load(context, "vkGetMicromapBuildSizesEXT"); + table->vkWriteMicromapsPropertiesEXT = (PFN_vkWriteMicromapsPropertiesEXT)load(context, "vkWriteMicromapsPropertiesEXT"); +#endif /* defined(VK_EXT_opacity_micromap) */ +#if defined(VK_EXT_pageable_device_local_memory) + table->vkSetDeviceMemoryPriorityEXT = (PFN_vkSetDeviceMemoryPriorityEXT)load(context, "vkSetDeviceMemoryPriorityEXT"); +#endif /* defined(VK_EXT_pageable_device_local_memory) */ +#if defined(VK_EXT_pipeline_properties) + table->vkGetPipelinePropertiesEXT = (PFN_vkGetPipelinePropertiesEXT)load(context, "vkGetPipelinePropertiesEXT"); +#endif /* defined(VK_EXT_pipeline_properties) */ +#if defined(VK_EXT_private_data) + table->vkCreatePrivateDataSlotEXT = (PFN_vkCreatePrivateDataSlotEXT)load(context, "vkCreatePrivateDataSlotEXT"); + table->vkDestroyPrivateDataSlotEXT = (PFN_vkDestroyPrivateDataSlotEXT)load(context, "vkDestroyPrivateDataSlotEXT"); + table->vkGetPrivateDataEXT = (PFN_vkGetPrivateDataEXT)load(context, "vkGetPrivateDataEXT"); + table->vkSetPrivateDataEXT = (PFN_vkSetPrivateDataEXT)load(context, "vkSetPrivateDataEXT"); +#endif /* defined(VK_EXT_private_data) */ +#if defined(VK_EXT_sample_locations) + table->vkCmdSetSampleLocationsEXT = (PFN_vkCmdSetSampleLocationsEXT)load(context, "vkCmdSetSampleLocationsEXT"); +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_shader_module_identifier) + table->vkGetShaderModuleCreateInfoIdentifierEXT = (PFN_vkGetShaderModuleCreateInfoIdentifierEXT)load(context, "vkGetShaderModuleCreateInfoIdentifierEXT"); + table->vkGetShaderModuleIdentifierEXT = (PFN_vkGetShaderModuleIdentifierEXT)load(context, "vkGetShaderModuleIdentifierEXT"); +#endif /* defined(VK_EXT_shader_module_identifier) */ +#if defined(VK_EXT_shader_object) + table->vkCmdBindShadersEXT = (PFN_vkCmdBindShadersEXT)load(context, "vkCmdBindShadersEXT"); + table->vkCreateShadersEXT = (PFN_vkCreateShadersEXT)load(context, "vkCreateShadersEXT"); + table->vkDestroyShaderEXT = (PFN_vkDestroyShaderEXT)load(context, "vkDestroyShaderEXT"); + table->vkGetShaderBinaryDataEXT = (PFN_vkGetShaderBinaryDataEXT)load(context, "vkGetShaderBinaryDataEXT"); +#endif /* defined(VK_EXT_shader_object) */ +#if defined(VK_EXT_swapchain_maintenance1) + table->vkReleaseSwapchainImagesEXT = (PFN_vkReleaseSwapchainImagesEXT)load(context, "vkReleaseSwapchainImagesEXT"); +#endif /* defined(VK_EXT_swapchain_maintenance1) */ +#if defined(VK_EXT_transform_feedback) + table->vkCmdBeginQueryIndexedEXT = (PFN_vkCmdBeginQueryIndexedEXT)load(context, "vkCmdBeginQueryIndexedEXT"); + table->vkCmdBeginTransformFeedbackEXT = (PFN_vkCmdBeginTransformFeedbackEXT)load(context, "vkCmdBeginTransformFeedbackEXT"); + table->vkCmdBindTransformFeedbackBuffersEXT = (PFN_vkCmdBindTransformFeedbackBuffersEXT)load(context, "vkCmdBindTransformFeedbackBuffersEXT"); + table->vkCmdDrawIndirectByteCountEXT = (PFN_vkCmdDrawIndirectByteCountEXT)load(context, "vkCmdDrawIndirectByteCountEXT"); + table->vkCmdEndQueryIndexedEXT = (PFN_vkCmdEndQueryIndexedEXT)load(context, "vkCmdEndQueryIndexedEXT"); + table->vkCmdEndTransformFeedbackEXT = (PFN_vkCmdEndTransformFeedbackEXT)load(context, "vkCmdEndTransformFeedbackEXT"); +#endif /* defined(VK_EXT_transform_feedback) */ +#if defined(VK_EXT_validation_cache) + table->vkCreateValidationCacheEXT = (PFN_vkCreateValidationCacheEXT)load(context, "vkCreateValidationCacheEXT"); + table->vkDestroyValidationCacheEXT = (PFN_vkDestroyValidationCacheEXT)load(context, "vkDestroyValidationCacheEXT"); + table->vkGetValidationCacheDataEXT = (PFN_vkGetValidationCacheDataEXT)load(context, "vkGetValidationCacheDataEXT"); + table->vkMergeValidationCachesEXT = (PFN_vkMergeValidationCachesEXT)load(context, "vkMergeValidationCachesEXT"); +#endif /* defined(VK_EXT_validation_cache) */ +#if defined(VK_FUCHSIA_buffer_collection) + table->vkCreateBufferCollectionFUCHSIA = (PFN_vkCreateBufferCollectionFUCHSIA)load(context, "vkCreateBufferCollectionFUCHSIA"); + table->vkDestroyBufferCollectionFUCHSIA = (PFN_vkDestroyBufferCollectionFUCHSIA)load(context, "vkDestroyBufferCollectionFUCHSIA"); + table->vkGetBufferCollectionPropertiesFUCHSIA = (PFN_vkGetBufferCollectionPropertiesFUCHSIA)load(context, "vkGetBufferCollectionPropertiesFUCHSIA"); + table->vkSetBufferCollectionBufferConstraintsFUCHSIA = (PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA)load(context, "vkSetBufferCollectionBufferConstraintsFUCHSIA"); + table->vkSetBufferCollectionImageConstraintsFUCHSIA = (PFN_vkSetBufferCollectionImageConstraintsFUCHSIA)load(context, "vkSetBufferCollectionImageConstraintsFUCHSIA"); +#endif /* defined(VK_FUCHSIA_buffer_collection) */ +#if defined(VK_FUCHSIA_external_memory) + table->vkGetMemoryZirconHandleFUCHSIA = (PFN_vkGetMemoryZirconHandleFUCHSIA)load(context, "vkGetMemoryZirconHandleFUCHSIA"); + table->vkGetMemoryZirconHandlePropertiesFUCHSIA = (PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA)load(context, "vkGetMemoryZirconHandlePropertiesFUCHSIA"); +#endif /* defined(VK_FUCHSIA_external_memory) */ +#if defined(VK_FUCHSIA_external_semaphore) + table->vkGetSemaphoreZirconHandleFUCHSIA = (PFN_vkGetSemaphoreZirconHandleFUCHSIA)load(context, "vkGetSemaphoreZirconHandleFUCHSIA"); + table->vkImportSemaphoreZirconHandleFUCHSIA = (PFN_vkImportSemaphoreZirconHandleFUCHSIA)load(context, "vkImportSemaphoreZirconHandleFUCHSIA"); +#endif /* defined(VK_FUCHSIA_external_semaphore) */ +#if defined(VK_GOOGLE_display_timing) + table->vkGetPastPresentationTimingGOOGLE = (PFN_vkGetPastPresentationTimingGOOGLE)load(context, "vkGetPastPresentationTimingGOOGLE"); + table->vkGetRefreshCycleDurationGOOGLE = (PFN_vkGetRefreshCycleDurationGOOGLE)load(context, "vkGetRefreshCycleDurationGOOGLE"); +#endif /* defined(VK_GOOGLE_display_timing) */ +#if defined(VK_HUAWEI_cluster_culling_shader) + table->vkCmdDrawClusterHUAWEI = (PFN_vkCmdDrawClusterHUAWEI)load(context, "vkCmdDrawClusterHUAWEI"); + table->vkCmdDrawClusterIndirectHUAWEI = (PFN_vkCmdDrawClusterIndirectHUAWEI)load(context, "vkCmdDrawClusterIndirectHUAWEI"); +#endif /* defined(VK_HUAWEI_cluster_culling_shader) */ +#if defined(VK_HUAWEI_invocation_mask) + table->vkCmdBindInvocationMaskHUAWEI = (PFN_vkCmdBindInvocationMaskHUAWEI)load(context, "vkCmdBindInvocationMaskHUAWEI"); +#endif /* defined(VK_HUAWEI_invocation_mask) */ +#if defined(VK_HUAWEI_subpass_shading) + table->vkCmdSubpassShadingHUAWEI = (PFN_vkCmdSubpassShadingHUAWEI)load(context, "vkCmdSubpassShadingHUAWEI"); + table->vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI = (PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI)load(context, "vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI"); +#endif /* defined(VK_HUAWEI_subpass_shading) */ +#if defined(VK_INTEL_performance_query) + table->vkAcquirePerformanceConfigurationINTEL = (PFN_vkAcquirePerformanceConfigurationINTEL)load(context, "vkAcquirePerformanceConfigurationINTEL"); + table->vkCmdSetPerformanceMarkerINTEL = (PFN_vkCmdSetPerformanceMarkerINTEL)load(context, "vkCmdSetPerformanceMarkerINTEL"); + table->vkCmdSetPerformanceOverrideINTEL = (PFN_vkCmdSetPerformanceOverrideINTEL)load(context, "vkCmdSetPerformanceOverrideINTEL"); + table->vkCmdSetPerformanceStreamMarkerINTEL = (PFN_vkCmdSetPerformanceStreamMarkerINTEL)load(context, "vkCmdSetPerformanceStreamMarkerINTEL"); + table->vkGetPerformanceParameterINTEL = (PFN_vkGetPerformanceParameterINTEL)load(context, "vkGetPerformanceParameterINTEL"); + table->vkInitializePerformanceApiINTEL = (PFN_vkInitializePerformanceApiINTEL)load(context, "vkInitializePerformanceApiINTEL"); + table->vkQueueSetPerformanceConfigurationINTEL = (PFN_vkQueueSetPerformanceConfigurationINTEL)load(context, "vkQueueSetPerformanceConfigurationINTEL"); + table->vkReleasePerformanceConfigurationINTEL = (PFN_vkReleasePerformanceConfigurationINTEL)load(context, "vkReleasePerformanceConfigurationINTEL"); + table->vkUninitializePerformanceApiINTEL = (PFN_vkUninitializePerformanceApiINTEL)load(context, "vkUninitializePerformanceApiINTEL"); +#endif /* defined(VK_INTEL_performance_query) */ +#if defined(VK_KHR_acceleration_structure) + table->vkBuildAccelerationStructuresKHR = (PFN_vkBuildAccelerationStructuresKHR)load(context, "vkBuildAccelerationStructuresKHR"); + table->vkCmdBuildAccelerationStructuresIndirectKHR = (PFN_vkCmdBuildAccelerationStructuresIndirectKHR)load(context, "vkCmdBuildAccelerationStructuresIndirectKHR"); + table->vkCmdBuildAccelerationStructuresKHR = (PFN_vkCmdBuildAccelerationStructuresKHR)load(context, "vkCmdBuildAccelerationStructuresKHR"); + table->vkCmdCopyAccelerationStructureKHR = (PFN_vkCmdCopyAccelerationStructureKHR)load(context, "vkCmdCopyAccelerationStructureKHR"); + table->vkCmdCopyAccelerationStructureToMemoryKHR = (PFN_vkCmdCopyAccelerationStructureToMemoryKHR)load(context, "vkCmdCopyAccelerationStructureToMemoryKHR"); + table->vkCmdCopyMemoryToAccelerationStructureKHR = (PFN_vkCmdCopyMemoryToAccelerationStructureKHR)load(context, "vkCmdCopyMemoryToAccelerationStructureKHR"); + table->vkCmdWriteAccelerationStructuresPropertiesKHR = (PFN_vkCmdWriteAccelerationStructuresPropertiesKHR)load(context, "vkCmdWriteAccelerationStructuresPropertiesKHR"); + table->vkCopyAccelerationStructureKHR = (PFN_vkCopyAccelerationStructureKHR)load(context, "vkCopyAccelerationStructureKHR"); + table->vkCopyAccelerationStructureToMemoryKHR = (PFN_vkCopyAccelerationStructureToMemoryKHR)load(context, "vkCopyAccelerationStructureToMemoryKHR"); + table->vkCopyMemoryToAccelerationStructureKHR = (PFN_vkCopyMemoryToAccelerationStructureKHR)load(context, "vkCopyMemoryToAccelerationStructureKHR"); + table->vkCreateAccelerationStructureKHR = (PFN_vkCreateAccelerationStructureKHR)load(context, "vkCreateAccelerationStructureKHR"); + table->vkDestroyAccelerationStructureKHR = (PFN_vkDestroyAccelerationStructureKHR)load(context, "vkDestroyAccelerationStructureKHR"); + table->vkGetAccelerationStructureBuildSizesKHR = (PFN_vkGetAccelerationStructureBuildSizesKHR)load(context, "vkGetAccelerationStructureBuildSizesKHR"); + table->vkGetAccelerationStructureDeviceAddressKHR = (PFN_vkGetAccelerationStructureDeviceAddressKHR)load(context, "vkGetAccelerationStructureDeviceAddressKHR"); + table->vkGetDeviceAccelerationStructureCompatibilityKHR = (PFN_vkGetDeviceAccelerationStructureCompatibilityKHR)load(context, "vkGetDeviceAccelerationStructureCompatibilityKHR"); + table->vkWriteAccelerationStructuresPropertiesKHR = (PFN_vkWriteAccelerationStructuresPropertiesKHR)load(context, "vkWriteAccelerationStructuresPropertiesKHR"); +#endif /* defined(VK_KHR_acceleration_structure) */ +#if defined(VK_KHR_bind_memory2) + table->vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2KHR)load(context, "vkBindBufferMemory2KHR"); + table->vkBindImageMemory2KHR = (PFN_vkBindImageMemory2KHR)load(context, "vkBindImageMemory2KHR"); +#endif /* defined(VK_KHR_bind_memory2) */ +#if defined(VK_KHR_buffer_device_address) + table->vkGetBufferDeviceAddressKHR = (PFN_vkGetBufferDeviceAddressKHR)load(context, "vkGetBufferDeviceAddressKHR"); + table->vkGetBufferOpaqueCaptureAddressKHR = (PFN_vkGetBufferOpaqueCaptureAddressKHR)load(context, "vkGetBufferOpaqueCaptureAddressKHR"); + table->vkGetDeviceMemoryOpaqueCaptureAddressKHR = (PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR)load(context, "vkGetDeviceMemoryOpaqueCaptureAddressKHR"); +#endif /* defined(VK_KHR_buffer_device_address) */ +#if defined(VK_KHR_calibrated_timestamps) + table->vkGetCalibratedTimestampsKHR = (PFN_vkGetCalibratedTimestampsKHR)load(context, "vkGetCalibratedTimestampsKHR"); +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_copy_commands2) + table->vkCmdBlitImage2KHR = (PFN_vkCmdBlitImage2KHR)load(context, "vkCmdBlitImage2KHR"); + table->vkCmdCopyBuffer2KHR = (PFN_vkCmdCopyBuffer2KHR)load(context, "vkCmdCopyBuffer2KHR"); + table->vkCmdCopyBufferToImage2KHR = (PFN_vkCmdCopyBufferToImage2KHR)load(context, "vkCmdCopyBufferToImage2KHR"); + table->vkCmdCopyImage2KHR = (PFN_vkCmdCopyImage2KHR)load(context, "vkCmdCopyImage2KHR"); + table->vkCmdCopyImageToBuffer2KHR = (PFN_vkCmdCopyImageToBuffer2KHR)load(context, "vkCmdCopyImageToBuffer2KHR"); + table->vkCmdResolveImage2KHR = (PFN_vkCmdResolveImage2KHR)load(context, "vkCmdResolveImage2KHR"); +#endif /* defined(VK_KHR_copy_commands2) */ +#if defined(VK_KHR_create_renderpass2) + table->vkCmdBeginRenderPass2KHR = (PFN_vkCmdBeginRenderPass2KHR)load(context, "vkCmdBeginRenderPass2KHR"); + table->vkCmdEndRenderPass2KHR = (PFN_vkCmdEndRenderPass2KHR)load(context, "vkCmdEndRenderPass2KHR"); + table->vkCmdNextSubpass2KHR = (PFN_vkCmdNextSubpass2KHR)load(context, "vkCmdNextSubpass2KHR"); + table->vkCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)load(context, "vkCreateRenderPass2KHR"); +#endif /* defined(VK_KHR_create_renderpass2) */ +#if defined(VK_KHR_deferred_host_operations) + table->vkCreateDeferredOperationKHR = (PFN_vkCreateDeferredOperationKHR)load(context, "vkCreateDeferredOperationKHR"); + table->vkDeferredOperationJoinKHR = (PFN_vkDeferredOperationJoinKHR)load(context, "vkDeferredOperationJoinKHR"); + table->vkDestroyDeferredOperationKHR = (PFN_vkDestroyDeferredOperationKHR)load(context, "vkDestroyDeferredOperationKHR"); + table->vkGetDeferredOperationMaxConcurrencyKHR = (PFN_vkGetDeferredOperationMaxConcurrencyKHR)load(context, "vkGetDeferredOperationMaxConcurrencyKHR"); + table->vkGetDeferredOperationResultKHR = (PFN_vkGetDeferredOperationResultKHR)load(context, "vkGetDeferredOperationResultKHR"); +#endif /* defined(VK_KHR_deferred_host_operations) */ +#if defined(VK_KHR_descriptor_update_template) + table->vkCreateDescriptorUpdateTemplateKHR = (PFN_vkCreateDescriptorUpdateTemplateKHR)load(context, "vkCreateDescriptorUpdateTemplateKHR"); + table->vkDestroyDescriptorUpdateTemplateKHR = (PFN_vkDestroyDescriptorUpdateTemplateKHR)load(context, "vkDestroyDescriptorUpdateTemplateKHR"); + table->vkUpdateDescriptorSetWithTemplateKHR = (PFN_vkUpdateDescriptorSetWithTemplateKHR)load(context, "vkUpdateDescriptorSetWithTemplateKHR"); +#endif /* defined(VK_KHR_descriptor_update_template) */ +#if defined(VK_KHR_device_group) + table->vkCmdDispatchBaseKHR = (PFN_vkCmdDispatchBaseKHR)load(context, "vkCmdDispatchBaseKHR"); + table->vkCmdSetDeviceMaskKHR = (PFN_vkCmdSetDeviceMaskKHR)load(context, "vkCmdSetDeviceMaskKHR"); + table->vkGetDeviceGroupPeerMemoryFeaturesKHR = (PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR)load(context, "vkGetDeviceGroupPeerMemoryFeaturesKHR"); +#endif /* defined(VK_KHR_device_group) */ +#if defined(VK_KHR_display_swapchain) + table->vkCreateSharedSwapchainsKHR = (PFN_vkCreateSharedSwapchainsKHR)load(context, "vkCreateSharedSwapchainsKHR"); +#endif /* defined(VK_KHR_display_swapchain) */ +#if defined(VK_KHR_draw_indirect_count) + table->vkCmdDrawIndexedIndirectCountKHR = (PFN_vkCmdDrawIndexedIndirectCountKHR)load(context, "vkCmdDrawIndexedIndirectCountKHR"); + table->vkCmdDrawIndirectCountKHR = (PFN_vkCmdDrawIndirectCountKHR)load(context, "vkCmdDrawIndirectCountKHR"); +#endif /* defined(VK_KHR_draw_indirect_count) */ +#if defined(VK_KHR_dynamic_rendering) + table->vkCmdBeginRenderingKHR = (PFN_vkCmdBeginRenderingKHR)load(context, "vkCmdBeginRenderingKHR"); + table->vkCmdEndRenderingKHR = (PFN_vkCmdEndRenderingKHR)load(context, "vkCmdEndRenderingKHR"); +#endif /* defined(VK_KHR_dynamic_rendering) */ +#if defined(VK_KHR_dynamic_rendering_local_read) + table->vkCmdSetRenderingAttachmentLocationsKHR = (PFN_vkCmdSetRenderingAttachmentLocationsKHR)load(context, "vkCmdSetRenderingAttachmentLocationsKHR"); + table->vkCmdSetRenderingInputAttachmentIndicesKHR = (PFN_vkCmdSetRenderingInputAttachmentIndicesKHR)load(context, "vkCmdSetRenderingInputAttachmentIndicesKHR"); +#endif /* defined(VK_KHR_dynamic_rendering_local_read) */ +#if defined(VK_KHR_external_fence_fd) + table->vkGetFenceFdKHR = (PFN_vkGetFenceFdKHR)load(context, "vkGetFenceFdKHR"); + table->vkImportFenceFdKHR = (PFN_vkImportFenceFdKHR)load(context, "vkImportFenceFdKHR"); +#endif /* defined(VK_KHR_external_fence_fd) */ +#if defined(VK_KHR_external_fence_win32) + table->vkGetFenceWin32HandleKHR = (PFN_vkGetFenceWin32HandleKHR)load(context, "vkGetFenceWin32HandleKHR"); + table->vkImportFenceWin32HandleKHR = (PFN_vkImportFenceWin32HandleKHR)load(context, "vkImportFenceWin32HandleKHR"); +#endif /* defined(VK_KHR_external_fence_win32) */ +#if defined(VK_KHR_external_memory_fd) + table->vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)load(context, "vkGetMemoryFdKHR"); + table->vkGetMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR)load(context, "vkGetMemoryFdPropertiesKHR"); +#endif /* defined(VK_KHR_external_memory_fd) */ +#if defined(VK_KHR_external_memory_win32) + table->vkGetMemoryWin32HandleKHR = (PFN_vkGetMemoryWin32HandleKHR)load(context, "vkGetMemoryWin32HandleKHR"); + table->vkGetMemoryWin32HandlePropertiesKHR = (PFN_vkGetMemoryWin32HandlePropertiesKHR)load(context, "vkGetMemoryWin32HandlePropertiesKHR"); +#endif /* defined(VK_KHR_external_memory_win32) */ +#if defined(VK_KHR_external_semaphore_fd) + table->vkGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR)load(context, "vkGetSemaphoreFdKHR"); + table->vkImportSemaphoreFdKHR = (PFN_vkImportSemaphoreFdKHR)load(context, "vkImportSemaphoreFdKHR"); +#endif /* defined(VK_KHR_external_semaphore_fd) */ +#if defined(VK_KHR_external_semaphore_win32) + table->vkGetSemaphoreWin32HandleKHR = (PFN_vkGetSemaphoreWin32HandleKHR)load(context, "vkGetSemaphoreWin32HandleKHR"); + table->vkImportSemaphoreWin32HandleKHR = (PFN_vkImportSemaphoreWin32HandleKHR)load(context, "vkImportSemaphoreWin32HandleKHR"); +#endif /* defined(VK_KHR_external_semaphore_win32) */ +#if defined(VK_KHR_fragment_shading_rate) + table->vkCmdSetFragmentShadingRateKHR = (PFN_vkCmdSetFragmentShadingRateKHR)load(context, "vkCmdSetFragmentShadingRateKHR"); +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_memory_requirements2) + table->vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2KHR)load(context, "vkGetBufferMemoryRequirements2KHR"); + table->vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2KHR)load(context, "vkGetImageMemoryRequirements2KHR"); + table->vkGetImageSparseMemoryRequirements2KHR = (PFN_vkGetImageSparseMemoryRequirements2KHR)load(context, "vkGetImageSparseMemoryRequirements2KHR"); +#endif /* defined(VK_KHR_get_memory_requirements2) */ +#if defined(VK_KHR_line_rasterization) + table->vkCmdSetLineStippleKHR = (PFN_vkCmdSetLineStippleKHR)load(context, "vkCmdSetLineStippleKHR"); +#endif /* defined(VK_KHR_line_rasterization) */ +#if defined(VK_KHR_maintenance1) + table->vkTrimCommandPoolKHR = (PFN_vkTrimCommandPoolKHR)load(context, "vkTrimCommandPoolKHR"); +#endif /* defined(VK_KHR_maintenance1) */ +#if defined(VK_KHR_maintenance3) + table->vkGetDescriptorSetLayoutSupportKHR = (PFN_vkGetDescriptorSetLayoutSupportKHR)load(context, "vkGetDescriptorSetLayoutSupportKHR"); +#endif /* defined(VK_KHR_maintenance3) */ +#if defined(VK_KHR_maintenance4) + table->vkGetDeviceBufferMemoryRequirementsKHR = (PFN_vkGetDeviceBufferMemoryRequirementsKHR)load(context, "vkGetDeviceBufferMemoryRequirementsKHR"); + table->vkGetDeviceImageMemoryRequirementsKHR = (PFN_vkGetDeviceImageMemoryRequirementsKHR)load(context, "vkGetDeviceImageMemoryRequirementsKHR"); + table->vkGetDeviceImageSparseMemoryRequirementsKHR = (PFN_vkGetDeviceImageSparseMemoryRequirementsKHR)load(context, "vkGetDeviceImageSparseMemoryRequirementsKHR"); +#endif /* defined(VK_KHR_maintenance4) */ +#if defined(VK_KHR_maintenance5) + table->vkCmdBindIndexBuffer2KHR = (PFN_vkCmdBindIndexBuffer2KHR)load(context, "vkCmdBindIndexBuffer2KHR"); + table->vkGetDeviceImageSubresourceLayoutKHR = (PFN_vkGetDeviceImageSubresourceLayoutKHR)load(context, "vkGetDeviceImageSubresourceLayoutKHR"); + table->vkGetImageSubresourceLayout2KHR = (PFN_vkGetImageSubresourceLayout2KHR)load(context, "vkGetImageSubresourceLayout2KHR"); + table->vkGetRenderingAreaGranularityKHR = (PFN_vkGetRenderingAreaGranularityKHR)load(context, "vkGetRenderingAreaGranularityKHR"); +#endif /* defined(VK_KHR_maintenance5) */ +#if defined(VK_KHR_maintenance6) + table->vkCmdBindDescriptorSets2KHR = (PFN_vkCmdBindDescriptorSets2KHR)load(context, "vkCmdBindDescriptorSets2KHR"); + table->vkCmdPushConstants2KHR = (PFN_vkCmdPushConstants2KHR)load(context, "vkCmdPushConstants2KHR"); +#endif /* defined(VK_KHR_maintenance6) */ +#if defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) + table->vkCmdPushDescriptorSet2KHR = (PFN_vkCmdPushDescriptorSet2KHR)load(context, "vkCmdPushDescriptorSet2KHR"); + table->vkCmdPushDescriptorSetWithTemplate2KHR = (PFN_vkCmdPushDescriptorSetWithTemplate2KHR)load(context, "vkCmdPushDescriptorSetWithTemplate2KHR"); +#endif /* defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) + table->vkCmdBindDescriptorBufferEmbeddedSamplers2EXT = (PFN_vkCmdBindDescriptorBufferEmbeddedSamplers2EXT)load(context, "vkCmdBindDescriptorBufferEmbeddedSamplers2EXT"); + table->vkCmdSetDescriptorBufferOffsets2EXT = (PFN_vkCmdSetDescriptorBufferOffsets2EXT)load(context, "vkCmdSetDescriptorBufferOffsets2EXT"); +#endif /* defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_KHR_map_memory2) + table->vkMapMemory2KHR = (PFN_vkMapMemory2KHR)load(context, "vkMapMemory2KHR"); + table->vkUnmapMemory2KHR = (PFN_vkUnmapMemory2KHR)load(context, "vkUnmapMemory2KHR"); +#endif /* defined(VK_KHR_map_memory2) */ +#if defined(VK_KHR_performance_query) + table->vkAcquireProfilingLockKHR = (PFN_vkAcquireProfilingLockKHR)load(context, "vkAcquireProfilingLockKHR"); + table->vkReleaseProfilingLockKHR = (PFN_vkReleaseProfilingLockKHR)load(context, "vkReleaseProfilingLockKHR"); +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_pipeline_binary) + table->vkCreatePipelineBinariesKHR = (PFN_vkCreatePipelineBinariesKHR)load(context, "vkCreatePipelineBinariesKHR"); + table->vkDestroyPipelineBinaryKHR = (PFN_vkDestroyPipelineBinaryKHR)load(context, "vkDestroyPipelineBinaryKHR"); + table->vkGetPipelineBinaryDataKHR = (PFN_vkGetPipelineBinaryDataKHR)load(context, "vkGetPipelineBinaryDataKHR"); + table->vkGetPipelineKeyKHR = (PFN_vkGetPipelineKeyKHR)load(context, "vkGetPipelineKeyKHR"); + table->vkReleaseCapturedPipelineDataKHR = (PFN_vkReleaseCapturedPipelineDataKHR)load(context, "vkReleaseCapturedPipelineDataKHR"); +#endif /* defined(VK_KHR_pipeline_binary) */ +#if defined(VK_KHR_pipeline_executable_properties) + table->vkGetPipelineExecutableInternalRepresentationsKHR = (PFN_vkGetPipelineExecutableInternalRepresentationsKHR)load(context, "vkGetPipelineExecutableInternalRepresentationsKHR"); + table->vkGetPipelineExecutablePropertiesKHR = (PFN_vkGetPipelineExecutablePropertiesKHR)load(context, "vkGetPipelineExecutablePropertiesKHR"); + table->vkGetPipelineExecutableStatisticsKHR = (PFN_vkGetPipelineExecutableStatisticsKHR)load(context, "vkGetPipelineExecutableStatisticsKHR"); +#endif /* defined(VK_KHR_pipeline_executable_properties) */ +#if defined(VK_KHR_present_wait) + table->vkWaitForPresentKHR = (PFN_vkWaitForPresentKHR)load(context, "vkWaitForPresentKHR"); +#endif /* defined(VK_KHR_present_wait) */ +#if defined(VK_KHR_push_descriptor) + table->vkCmdPushDescriptorSetKHR = (PFN_vkCmdPushDescriptorSetKHR)load(context, "vkCmdPushDescriptorSetKHR"); +#endif /* defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) + table->vkCmdTraceRaysIndirect2KHR = (PFN_vkCmdTraceRaysIndirect2KHR)load(context, "vkCmdTraceRaysIndirect2KHR"); +#endif /* defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_ray_tracing_pipeline) + table->vkCmdSetRayTracingPipelineStackSizeKHR = (PFN_vkCmdSetRayTracingPipelineStackSizeKHR)load(context, "vkCmdSetRayTracingPipelineStackSizeKHR"); + table->vkCmdTraceRaysIndirectKHR = (PFN_vkCmdTraceRaysIndirectKHR)load(context, "vkCmdTraceRaysIndirectKHR"); + table->vkCmdTraceRaysKHR = (PFN_vkCmdTraceRaysKHR)load(context, "vkCmdTraceRaysKHR"); + table->vkCreateRayTracingPipelinesKHR = (PFN_vkCreateRayTracingPipelinesKHR)load(context, "vkCreateRayTracingPipelinesKHR"); + table->vkGetRayTracingCaptureReplayShaderGroupHandlesKHR = (PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR)load(context, "vkGetRayTracingCaptureReplayShaderGroupHandlesKHR"); + table->vkGetRayTracingShaderGroupHandlesKHR = (PFN_vkGetRayTracingShaderGroupHandlesKHR)load(context, "vkGetRayTracingShaderGroupHandlesKHR"); + table->vkGetRayTracingShaderGroupStackSizeKHR = (PFN_vkGetRayTracingShaderGroupStackSizeKHR)load(context, "vkGetRayTracingShaderGroupStackSizeKHR"); +#endif /* defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_sampler_ycbcr_conversion) + table->vkCreateSamplerYcbcrConversionKHR = (PFN_vkCreateSamplerYcbcrConversionKHR)load(context, "vkCreateSamplerYcbcrConversionKHR"); + table->vkDestroySamplerYcbcrConversionKHR = (PFN_vkDestroySamplerYcbcrConversionKHR)load(context, "vkDestroySamplerYcbcrConversionKHR"); +#endif /* defined(VK_KHR_sampler_ycbcr_conversion) */ +#if defined(VK_KHR_shared_presentable_image) + table->vkGetSwapchainStatusKHR = (PFN_vkGetSwapchainStatusKHR)load(context, "vkGetSwapchainStatusKHR"); +#endif /* defined(VK_KHR_shared_presentable_image) */ +#if defined(VK_KHR_swapchain) + table->vkAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR)load(context, "vkAcquireNextImageKHR"); + table->vkCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)load(context, "vkCreateSwapchainKHR"); + table->vkDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)load(context, "vkDestroySwapchainKHR"); + table->vkGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)load(context, "vkGetSwapchainImagesKHR"); + table->vkQueuePresentKHR = (PFN_vkQueuePresentKHR)load(context, "vkQueuePresentKHR"); +#endif /* defined(VK_KHR_swapchain) */ +#if defined(VK_KHR_synchronization2) + table->vkCmdPipelineBarrier2KHR = (PFN_vkCmdPipelineBarrier2KHR)load(context, "vkCmdPipelineBarrier2KHR"); + table->vkCmdResetEvent2KHR = (PFN_vkCmdResetEvent2KHR)load(context, "vkCmdResetEvent2KHR"); + table->vkCmdSetEvent2KHR = (PFN_vkCmdSetEvent2KHR)load(context, "vkCmdSetEvent2KHR"); + table->vkCmdWaitEvents2KHR = (PFN_vkCmdWaitEvents2KHR)load(context, "vkCmdWaitEvents2KHR"); + table->vkCmdWriteTimestamp2KHR = (PFN_vkCmdWriteTimestamp2KHR)load(context, "vkCmdWriteTimestamp2KHR"); + table->vkQueueSubmit2KHR = (PFN_vkQueueSubmit2KHR)load(context, "vkQueueSubmit2KHR"); +#endif /* defined(VK_KHR_synchronization2) */ +#if defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) + table->vkCmdWriteBufferMarker2AMD = (PFN_vkCmdWriteBufferMarker2AMD)load(context, "vkCmdWriteBufferMarker2AMD"); +#endif /* defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) */ +#if defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) + table->vkGetQueueCheckpointData2NV = (PFN_vkGetQueueCheckpointData2NV)load(context, "vkGetQueueCheckpointData2NV"); +#endif /* defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_KHR_timeline_semaphore) + table->vkGetSemaphoreCounterValueKHR = (PFN_vkGetSemaphoreCounterValueKHR)load(context, "vkGetSemaphoreCounterValueKHR"); + table->vkSignalSemaphoreKHR = (PFN_vkSignalSemaphoreKHR)load(context, "vkSignalSemaphoreKHR"); + table->vkWaitSemaphoresKHR = (PFN_vkWaitSemaphoresKHR)load(context, "vkWaitSemaphoresKHR"); +#endif /* defined(VK_KHR_timeline_semaphore) */ +#if defined(VK_KHR_video_decode_queue) + table->vkCmdDecodeVideoKHR = (PFN_vkCmdDecodeVideoKHR)load(context, "vkCmdDecodeVideoKHR"); +#endif /* defined(VK_KHR_video_decode_queue) */ +#if defined(VK_KHR_video_encode_queue) + table->vkCmdEncodeVideoKHR = (PFN_vkCmdEncodeVideoKHR)load(context, "vkCmdEncodeVideoKHR"); + table->vkGetEncodedVideoSessionParametersKHR = (PFN_vkGetEncodedVideoSessionParametersKHR)load(context, "vkGetEncodedVideoSessionParametersKHR"); +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) + table->vkBindVideoSessionMemoryKHR = (PFN_vkBindVideoSessionMemoryKHR)load(context, "vkBindVideoSessionMemoryKHR"); + table->vkCmdBeginVideoCodingKHR = (PFN_vkCmdBeginVideoCodingKHR)load(context, "vkCmdBeginVideoCodingKHR"); + table->vkCmdControlVideoCodingKHR = (PFN_vkCmdControlVideoCodingKHR)load(context, "vkCmdControlVideoCodingKHR"); + table->vkCmdEndVideoCodingKHR = (PFN_vkCmdEndVideoCodingKHR)load(context, "vkCmdEndVideoCodingKHR"); + table->vkCreateVideoSessionKHR = (PFN_vkCreateVideoSessionKHR)load(context, "vkCreateVideoSessionKHR"); + table->vkCreateVideoSessionParametersKHR = (PFN_vkCreateVideoSessionParametersKHR)load(context, "vkCreateVideoSessionParametersKHR"); + table->vkDestroyVideoSessionKHR = (PFN_vkDestroyVideoSessionKHR)load(context, "vkDestroyVideoSessionKHR"); + table->vkDestroyVideoSessionParametersKHR = (PFN_vkDestroyVideoSessionParametersKHR)load(context, "vkDestroyVideoSessionParametersKHR"); + table->vkGetVideoSessionMemoryRequirementsKHR = (PFN_vkGetVideoSessionMemoryRequirementsKHR)load(context, "vkGetVideoSessionMemoryRequirementsKHR"); + table->vkUpdateVideoSessionParametersKHR = (PFN_vkUpdateVideoSessionParametersKHR)load(context, "vkUpdateVideoSessionParametersKHR"); +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_NVX_binary_import) + table->vkCmdCuLaunchKernelNVX = (PFN_vkCmdCuLaunchKernelNVX)load(context, "vkCmdCuLaunchKernelNVX"); + table->vkCreateCuFunctionNVX = (PFN_vkCreateCuFunctionNVX)load(context, "vkCreateCuFunctionNVX"); + table->vkCreateCuModuleNVX = (PFN_vkCreateCuModuleNVX)load(context, "vkCreateCuModuleNVX"); + table->vkDestroyCuFunctionNVX = (PFN_vkDestroyCuFunctionNVX)load(context, "vkDestroyCuFunctionNVX"); + table->vkDestroyCuModuleNVX = (PFN_vkDestroyCuModuleNVX)load(context, "vkDestroyCuModuleNVX"); +#endif /* defined(VK_NVX_binary_import) */ +#if defined(VK_NVX_image_view_handle) + table->vkGetImageViewAddressNVX = (PFN_vkGetImageViewAddressNVX)load(context, "vkGetImageViewAddressNVX"); + table->vkGetImageViewHandleNVX = (PFN_vkGetImageViewHandleNVX)load(context, "vkGetImageViewHandleNVX"); +#endif /* defined(VK_NVX_image_view_handle) */ +#if defined(VK_NV_clip_space_w_scaling) + table->vkCmdSetViewportWScalingNV = (PFN_vkCmdSetViewportWScalingNV)load(context, "vkCmdSetViewportWScalingNV"); +#endif /* defined(VK_NV_clip_space_w_scaling) */ +#if defined(VK_NV_copy_memory_indirect) + table->vkCmdCopyMemoryIndirectNV = (PFN_vkCmdCopyMemoryIndirectNV)load(context, "vkCmdCopyMemoryIndirectNV"); + table->vkCmdCopyMemoryToImageIndirectNV = (PFN_vkCmdCopyMemoryToImageIndirectNV)load(context, "vkCmdCopyMemoryToImageIndirectNV"); +#endif /* defined(VK_NV_copy_memory_indirect) */ +#if defined(VK_NV_cuda_kernel_launch) + table->vkCmdCudaLaunchKernelNV = (PFN_vkCmdCudaLaunchKernelNV)load(context, "vkCmdCudaLaunchKernelNV"); + table->vkCreateCudaFunctionNV = (PFN_vkCreateCudaFunctionNV)load(context, "vkCreateCudaFunctionNV"); + table->vkCreateCudaModuleNV = (PFN_vkCreateCudaModuleNV)load(context, "vkCreateCudaModuleNV"); + table->vkDestroyCudaFunctionNV = (PFN_vkDestroyCudaFunctionNV)load(context, "vkDestroyCudaFunctionNV"); + table->vkDestroyCudaModuleNV = (PFN_vkDestroyCudaModuleNV)load(context, "vkDestroyCudaModuleNV"); + table->vkGetCudaModuleCacheNV = (PFN_vkGetCudaModuleCacheNV)load(context, "vkGetCudaModuleCacheNV"); +#endif /* defined(VK_NV_cuda_kernel_launch) */ +#if defined(VK_NV_device_diagnostic_checkpoints) + table->vkCmdSetCheckpointNV = (PFN_vkCmdSetCheckpointNV)load(context, "vkCmdSetCheckpointNV"); + table->vkGetQueueCheckpointDataNV = (PFN_vkGetQueueCheckpointDataNV)load(context, "vkGetQueueCheckpointDataNV"); +#endif /* defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_NV_device_generated_commands) + table->vkCmdBindPipelineShaderGroupNV = (PFN_vkCmdBindPipelineShaderGroupNV)load(context, "vkCmdBindPipelineShaderGroupNV"); + table->vkCmdExecuteGeneratedCommandsNV = (PFN_vkCmdExecuteGeneratedCommandsNV)load(context, "vkCmdExecuteGeneratedCommandsNV"); + table->vkCmdPreprocessGeneratedCommandsNV = (PFN_vkCmdPreprocessGeneratedCommandsNV)load(context, "vkCmdPreprocessGeneratedCommandsNV"); + table->vkCreateIndirectCommandsLayoutNV = (PFN_vkCreateIndirectCommandsLayoutNV)load(context, "vkCreateIndirectCommandsLayoutNV"); + table->vkDestroyIndirectCommandsLayoutNV = (PFN_vkDestroyIndirectCommandsLayoutNV)load(context, "vkDestroyIndirectCommandsLayoutNV"); + table->vkGetGeneratedCommandsMemoryRequirementsNV = (PFN_vkGetGeneratedCommandsMemoryRequirementsNV)load(context, "vkGetGeneratedCommandsMemoryRequirementsNV"); +#endif /* defined(VK_NV_device_generated_commands) */ +#if defined(VK_NV_device_generated_commands_compute) + table->vkCmdUpdatePipelineIndirectBufferNV = (PFN_vkCmdUpdatePipelineIndirectBufferNV)load(context, "vkCmdUpdatePipelineIndirectBufferNV"); + table->vkGetPipelineIndirectDeviceAddressNV = (PFN_vkGetPipelineIndirectDeviceAddressNV)load(context, "vkGetPipelineIndirectDeviceAddressNV"); + table->vkGetPipelineIndirectMemoryRequirementsNV = (PFN_vkGetPipelineIndirectMemoryRequirementsNV)load(context, "vkGetPipelineIndirectMemoryRequirementsNV"); +#endif /* defined(VK_NV_device_generated_commands_compute) */ +#if defined(VK_NV_external_memory_rdma) + table->vkGetMemoryRemoteAddressNV = (PFN_vkGetMemoryRemoteAddressNV)load(context, "vkGetMemoryRemoteAddressNV"); +#endif /* defined(VK_NV_external_memory_rdma) */ +#if defined(VK_NV_external_memory_win32) + table->vkGetMemoryWin32HandleNV = (PFN_vkGetMemoryWin32HandleNV)load(context, "vkGetMemoryWin32HandleNV"); +#endif /* defined(VK_NV_external_memory_win32) */ +#if defined(VK_NV_fragment_shading_rate_enums) + table->vkCmdSetFragmentShadingRateEnumNV = (PFN_vkCmdSetFragmentShadingRateEnumNV)load(context, "vkCmdSetFragmentShadingRateEnumNV"); +#endif /* defined(VK_NV_fragment_shading_rate_enums) */ +#if defined(VK_NV_low_latency2) + table->vkGetLatencyTimingsNV = (PFN_vkGetLatencyTimingsNV)load(context, "vkGetLatencyTimingsNV"); + table->vkLatencySleepNV = (PFN_vkLatencySleepNV)load(context, "vkLatencySleepNV"); + table->vkQueueNotifyOutOfBandNV = (PFN_vkQueueNotifyOutOfBandNV)load(context, "vkQueueNotifyOutOfBandNV"); + table->vkSetLatencyMarkerNV = (PFN_vkSetLatencyMarkerNV)load(context, "vkSetLatencyMarkerNV"); + table->vkSetLatencySleepModeNV = (PFN_vkSetLatencySleepModeNV)load(context, "vkSetLatencySleepModeNV"); +#endif /* defined(VK_NV_low_latency2) */ +#if defined(VK_NV_memory_decompression) + table->vkCmdDecompressMemoryIndirectCountNV = (PFN_vkCmdDecompressMemoryIndirectCountNV)load(context, "vkCmdDecompressMemoryIndirectCountNV"); + table->vkCmdDecompressMemoryNV = (PFN_vkCmdDecompressMemoryNV)load(context, "vkCmdDecompressMemoryNV"); +#endif /* defined(VK_NV_memory_decompression) */ +#if defined(VK_NV_mesh_shader) + table->vkCmdDrawMeshTasksIndirectCountNV = (PFN_vkCmdDrawMeshTasksIndirectCountNV)load(context, "vkCmdDrawMeshTasksIndirectCountNV"); + table->vkCmdDrawMeshTasksIndirectNV = (PFN_vkCmdDrawMeshTasksIndirectNV)load(context, "vkCmdDrawMeshTasksIndirectNV"); + table->vkCmdDrawMeshTasksNV = (PFN_vkCmdDrawMeshTasksNV)load(context, "vkCmdDrawMeshTasksNV"); +#endif /* defined(VK_NV_mesh_shader) */ +#if defined(VK_NV_optical_flow) + table->vkBindOpticalFlowSessionImageNV = (PFN_vkBindOpticalFlowSessionImageNV)load(context, "vkBindOpticalFlowSessionImageNV"); + table->vkCmdOpticalFlowExecuteNV = (PFN_vkCmdOpticalFlowExecuteNV)load(context, "vkCmdOpticalFlowExecuteNV"); + table->vkCreateOpticalFlowSessionNV = (PFN_vkCreateOpticalFlowSessionNV)load(context, "vkCreateOpticalFlowSessionNV"); + table->vkDestroyOpticalFlowSessionNV = (PFN_vkDestroyOpticalFlowSessionNV)load(context, "vkDestroyOpticalFlowSessionNV"); +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_NV_ray_tracing) + table->vkBindAccelerationStructureMemoryNV = (PFN_vkBindAccelerationStructureMemoryNV)load(context, "vkBindAccelerationStructureMemoryNV"); + table->vkCmdBuildAccelerationStructureNV = (PFN_vkCmdBuildAccelerationStructureNV)load(context, "vkCmdBuildAccelerationStructureNV"); + table->vkCmdCopyAccelerationStructureNV = (PFN_vkCmdCopyAccelerationStructureNV)load(context, "vkCmdCopyAccelerationStructureNV"); + table->vkCmdTraceRaysNV = (PFN_vkCmdTraceRaysNV)load(context, "vkCmdTraceRaysNV"); + table->vkCmdWriteAccelerationStructuresPropertiesNV = (PFN_vkCmdWriteAccelerationStructuresPropertiesNV)load(context, "vkCmdWriteAccelerationStructuresPropertiesNV"); + table->vkCompileDeferredNV = (PFN_vkCompileDeferredNV)load(context, "vkCompileDeferredNV"); + table->vkCreateAccelerationStructureNV = (PFN_vkCreateAccelerationStructureNV)load(context, "vkCreateAccelerationStructureNV"); + table->vkCreateRayTracingPipelinesNV = (PFN_vkCreateRayTracingPipelinesNV)load(context, "vkCreateRayTracingPipelinesNV"); + table->vkDestroyAccelerationStructureNV = (PFN_vkDestroyAccelerationStructureNV)load(context, "vkDestroyAccelerationStructureNV"); + table->vkGetAccelerationStructureHandleNV = (PFN_vkGetAccelerationStructureHandleNV)load(context, "vkGetAccelerationStructureHandleNV"); + table->vkGetAccelerationStructureMemoryRequirementsNV = (PFN_vkGetAccelerationStructureMemoryRequirementsNV)load(context, "vkGetAccelerationStructureMemoryRequirementsNV"); + table->vkGetRayTracingShaderGroupHandlesNV = (PFN_vkGetRayTracingShaderGroupHandlesNV)load(context, "vkGetRayTracingShaderGroupHandlesNV"); +#endif /* defined(VK_NV_ray_tracing) */ +#if defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 + table->vkCmdSetExclusiveScissorEnableNV = (PFN_vkCmdSetExclusiveScissorEnableNV)load(context, "vkCmdSetExclusiveScissorEnableNV"); +#endif /* defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 */ +#if defined(VK_NV_scissor_exclusive) + table->vkCmdSetExclusiveScissorNV = (PFN_vkCmdSetExclusiveScissorNV)load(context, "vkCmdSetExclusiveScissorNV"); +#endif /* defined(VK_NV_scissor_exclusive) */ +#if defined(VK_NV_shading_rate_image) + table->vkCmdBindShadingRateImageNV = (PFN_vkCmdBindShadingRateImageNV)load(context, "vkCmdBindShadingRateImageNV"); + table->vkCmdSetCoarseSampleOrderNV = (PFN_vkCmdSetCoarseSampleOrderNV)load(context, "vkCmdSetCoarseSampleOrderNV"); + table->vkCmdSetViewportShadingRatePaletteNV = (PFN_vkCmdSetViewportShadingRatePaletteNV)load(context, "vkCmdSetViewportShadingRatePaletteNV"); +#endif /* defined(VK_NV_shading_rate_image) */ +#if defined(VK_QCOM_tile_properties) + table->vkGetDynamicRenderingTilePropertiesQCOM = (PFN_vkGetDynamicRenderingTilePropertiesQCOM)load(context, "vkGetDynamicRenderingTilePropertiesQCOM"); + table->vkGetFramebufferTilePropertiesQCOM = (PFN_vkGetFramebufferTilePropertiesQCOM)load(context, "vkGetFramebufferTilePropertiesQCOM"); +#endif /* defined(VK_QCOM_tile_properties) */ +#if defined(VK_QNX_external_memory_screen_buffer) + table->vkGetScreenBufferPropertiesQNX = (PFN_vkGetScreenBufferPropertiesQNX)load(context, "vkGetScreenBufferPropertiesQNX"); +#endif /* defined(VK_QNX_external_memory_screen_buffer) */ +#if defined(VK_VALVE_descriptor_set_host_mapping) + table->vkGetDescriptorSetHostMappingVALVE = (PFN_vkGetDescriptorSetHostMappingVALVE)load(context, "vkGetDescriptorSetHostMappingVALVE"); + table->vkGetDescriptorSetLayoutHostMappingInfoVALVE = (PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE)load(context, "vkGetDescriptorSetLayoutHostMappingInfoVALVE"); +#endif /* defined(VK_VALVE_descriptor_set_host_mapping) */ +#if (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) + table->vkCmdBindVertexBuffers2EXT = (PFN_vkCmdBindVertexBuffers2EXT)load(context, "vkCmdBindVertexBuffers2EXT"); + table->vkCmdSetCullModeEXT = (PFN_vkCmdSetCullModeEXT)load(context, "vkCmdSetCullModeEXT"); + table->vkCmdSetDepthBoundsTestEnableEXT = (PFN_vkCmdSetDepthBoundsTestEnableEXT)load(context, "vkCmdSetDepthBoundsTestEnableEXT"); + table->vkCmdSetDepthCompareOpEXT = (PFN_vkCmdSetDepthCompareOpEXT)load(context, "vkCmdSetDepthCompareOpEXT"); + table->vkCmdSetDepthTestEnableEXT = (PFN_vkCmdSetDepthTestEnableEXT)load(context, "vkCmdSetDepthTestEnableEXT"); + table->vkCmdSetDepthWriteEnableEXT = (PFN_vkCmdSetDepthWriteEnableEXT)load(context, "vkCmdSetDepthWriteEnableEXT"); + table->vkCmdSetFrontFaceEXT = (PFN_vkCmdSetFrontFaceEXT)load(context, "vkCmdSetFrontFaceEXT"); + table->vkCmdSetPrimitiveTopologyEXT = (PFN_vkCmdSetPrimitiveTopologyEXT)load(context, "vkCmdSetPrimitiveTopologyEXT"); + table->vkCmdSetScissorWithCountEXT = (PFN_vkCmdSetScissorWithCountEXT)load(context, "vkCmdSetScissorWithCountEXT"); + table->vkCmdSetStencilOpEXT = (PFN_vkCmdSetStencilOpEXT)load(context, "vkCmdSetStencilOpEXT"); + table->vkCmdSetStencilTestEnableEXT = (PFN_vkCmdSetStencilTestEnableEXT)load(context, "vkCmdSetStencilTestEnableEXT"); + table->vkCmdSetViewportWithCountEXT = (PFN_vkCmdSetViewportWithCountEXT)load(context, "vkCmdSetViewportWithCountEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) + table->vkCmdSetDepthBiasEnableEXT = (PFN_vkCmdSetDepthBiasEnableEXT)load(context, "vkCmdSetDepthBiasEnableEXT"); + table->vkCmdSetLogicOpEXT = (PFN_vkCmdSetLogicOpEXT)load(context, "vkCmdSetLogicOpEXT"); + table->vkCmdSetPatchControlPointsEXT = (PFN_vkCmdSetPatchControlPointsEXT)load(context, "vkCmdSetPatchControlPointsEXT"); + table->vkCmdSetPrimitiveRestartEnableEXT = (PFN_vkCmdSetPrimitiveRestartEnableEXT)load(context, "vkCmdSetPrimitiveRestartEnableEXT"); + table->vkCmdSetRasterizerDiscardEnableEXT = (PFN_vkCmdSetRasterizerDiscardEnableEXT)load(context, "vkCmdSetRasterizerDiscardEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) + table->vkCmdSetAlphaToCoverageEnableEXT = (PFN_vkCmdSetAlphaToCoverageEnableEXT)load(context, "vkCmdSetAlphaToCoverageEnableEXT"); + table->vkCmdSetAlphaToOneEnableEXT = (PFN_vkCmdSetAlphaToOneEnableEXT)load(context, "vkCmdSetAlphaToOneEnableEXT"); + table->vkCmdSetColorBlendEnableEXT = (PFN_vkCmdSetColorBlendEnableEXT)load(context, "vkCmdSetColorBlendEnableEXT"); + table->vkCmdSetColorBlendEquationEXT = (PFN_vkCmdSetColorBlendEquationEXT)load(context, "vkCmdSetColorBlendEquationEXT"); + table->vkCmdSetColorWriteMaskEXT = (PFN_vkCmdSetColorWriteMaskEXT)load(context, "vkCmdSetColorWriteMaskEXT"); + table->vkCmdSetDepthClampEnableEXT = (PFN_vkCmdSetDepthClampEnableEXT)load(context, "vkCmdSetDepthClampEnableEXT"); + table->vkCmdSetLogicOpEnableEXT = (PFN_vkCmdSetLogicOpEnableEXT)load(context, "vkCmdSetLogicOpEnableEXT"); + table->vkCmdSetPolygonModeEXT = (PFN_vkCmdSetPolygonModeEXT)load(context, "vkCmdSetPolygonModeEXT"); + table->vkCmdSetRasterizationSamplesEXT = (PFN_vkCmdSetRasterizationSamplesEXT)load(context, "vkCmdSetRasterizationSamplesEXT"); + table->vkCmdSetSampleMaskEXT = (PFN_vkCmdSetSampleMaskEXT)load(context, "vkCmdSetSampleMaskEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) + table->vkCmdSetTessellationDomainOriginEXT = (PFN_vkCmdSetTessellationDomainOriginEXT)load(context, "vkCmdSetTessellationDomainOriginEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) + table->vkCmdSetRasterizationStreamEXT = (PFN_vkCmdSetRasterizationStreamEXT)load(context, "vkCmdSetRasterizationStreamEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) + table->vkCmdSetConservativeRasterizationModeEXT = (PFN_vkCmdSetConservativeRasterizationModeEXT)load(context, "vkCmdSetConservativeRasterizationModeEXT"); + table->vkCmdSetExtraPrimitiveOverestimationSizeEXT = (PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT)load(context, "vkCmdSetExtraPrimitiveOverestimationSizeEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) + table->vkCmdSetDepthClipEnableEXT = (PFN_vkCmdSetDepthClipEnableEXT)load(context, "vkCmdSetDepthClipEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) + table->vkCmdSetSampleLocationsEnableEXT = (PFN_vkCmdSetSampleLocationsEnableEXT)load(context, "vkCmdSetSampleLocationsEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) + table->vkCmdSetColorBlendAdvancedEXT = (PFN_vkCmdSetColorBlendAdvancedEXT)load(context, "vkCmdSetColorBlendAdvancedEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) + table->vkCmdSetProvokingVertexModeEXT = (PFN_vkCmdSetProvokingVertexModeEXT)load(context, "vkCmdSetProvokingVertexModeEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) + table->vkCmdSetLineRasterizationModeEXT = (PFN_vkCmdSetLineRasterizationModeEXT)load(context, "vkCmdSetLineRasterizationModeEXT"); + table->vkCmdSetLineStippleEnableEXT = (PFN_vkCmdSetLineStippleEnableEXT)load(context, "vkCmdSetLineStippleEnableEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) + table->vkCmdSetDepthClipNegativeOneToOneEXT = (PFN_vkCmdSetDepthClipNegativeOneToOneEXT)load(context, "vkCmdSetDepthClipNegativeOneToOneEXT"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) + table->vkCmdSetViewportWScalingEnableNV = (PFN_vkCmdSetViewportWScalingEnableNV)load(context, "vkCmdSetViewportWScalingEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) + table->vkCmdSetViewportSwizzleNV = (PFN_vkCmdSetViewportSwizzleNV)load(context, "vkCmdSetViewportSwizzleNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) + table->vkCmdSetCoverageToColorEnableNV = (PFN_vkCmdSetCoverageToColorEnableNV)load(context, "vkCmdSetCoverageToColorEnableNV"); + table->vkCmdSetCoverageToColorLocationNV = (PFN_vkCmdSetCoverageToColorLocationNV)load(context, "vkCmdSetCoverageToColorLocationNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) + table->vkCmdSetCoverageModulationModeNV = (PFN_vkCmdSetCoverageModulationModeNV)load(context, "vkCmdSetCoverageModulationModeNV"); + table->vkCmdSetCoverageModulationTableEnableNV = (PFN_vkCmdSetCoverageModulationTableEnableNV)load(context, "vkCmdSetCoverageModulationTableEnableNV"); + table->vkCmdSetCoverageModulationTableNV = (PFN_vkCmdSetCoverageModulationTableNV)load(context, "vkCmdSetCoverageModulationTableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) + table->vkCmdSetShadingRateImageEnableNV = (PFN_vkCmdSetShadingRateImageEnableNV)load(context, "vkCmdSetShadingRateImageEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) + table->vkCmdSetRepresentativeFragmentTestEnableNV = (PFN_vkCmdSetRepresentativeFragmentTestEnableNV)load(context, "vkCmdSetRepresentativeFragmentTestEnableNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) + table->vkCmdSetCoverageReductionModeNV = (PFN_vkCmdSetCoverageReductionModeNV)load(context, "vkCmdSetCoverageReductionModeNV"); +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) */ +#if (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) + table->vkGetImageSubresourceLayout2EXT = (PFN_vkGetImageSubresourceLayout2EXT)load(context, "vkGetImageSubresourceLayout2EXT"); +#endif /* (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) */ +#if (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) + table->vkCmdSetVertexInputEXT = (PFN_vkCmdSetVertexInputEXT)load(context, "vkCmdSetVertexInputEXT"); +#endif /* (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) */ +#if (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) + table->vkCmdPushDescriptorSetWithTemplateKHR = (PFN_vkCmdPushDescriptorSetWithTemplateKHR)load(context, "vkCmdPushDescriptorSetWithTemplateKHR"); +#endif /* (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + table->vkGetDeviceGroupPresentCapabilitiesKHR = (PFN_vkGetDeviceGroupPresentCapabilitiesKHR)load(context, "vkGetDeviceGroupPresentCapabilitiesKHR"); + table->vkGetDeviceGroupSurfacePresentModesKHR = (PFN_vkGetDeviceGroupSurfacePresentModesKHR)load(context, "vkGetDeviceGroupSurfacePresentModesKHR"); +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + table->vkAcquireNextImage2KHR = (PFN_vkAcquireNextImage2KHR)load(context, "vkAcquireNextImage2KHR"); +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ + /* VOLK_GENERATE_LOAD_DEVICE_TABLE */ +} + +#ifdef __GNUC__ +#ifdef VOLK_DEFAULT_VISIBILITY +# pragma GCC visibility push(default) +#else +# pragma GCC visibility push(hidden) +#endif +#endif + +/* VOLK_GENERATE_PROTOTYPES_C */ +#if defined(VK_VERSION_1_0) +PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; +PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; +PFN_vkAllocateMemory vkAllocateMemory; +PFN_vkBeginCommandBuffer vkBeginCommandBuffer; +PFN_vkBindBufferMemory vkBindBufferMemory; +PFN_vkBindImageMemory vkBindImageMemory; +PFN_vkCmdBeginQuery vkCmdBeginQuery; +PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; +PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; +PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; +PFN_vkCmdBindPipeline vkCmdBindPipeline; +PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; +PFN_vkCmdBlitImage vkCmdBlitImage; +PFN_vkCmdClearAttachments vkCmdClearAttachments; +PFN_vkCmdClearColorImage vkCmdClearColorImage; +PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage; +PFN_vkCmdCopyBuffer vkCmdCopyBuffer; +PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; +PFN_vkCmdCopyImage vkCmdCopyImage; +PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; +PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults; +PFN_vkCmdDispatch vkCmdDispatch; +PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect; +PFN_vkCmdDraw vkCmdDraw; +PFN_vkCmdDrawIndexed vkCmdDrawIndexed; +PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect; +PFN_vkCmdDrawIndirect vkCmdDrawIndirect; +PFN_vkCmdEndQuery vkCmdEndQuery; +PFN_vkCmdEndRenderPass vkCmdEndRenderPass; +PFN_vkCmdExecuteCommands vkCmdExecuteCommands; +PFN_vkCmdFillBuffer vkCmdFillBuffer; +PFN_vkCmdNextSubpass vkCmdNextSubpass; +PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; +PFN_vkCmdPushConstants vkCmdPushConstants; +PFN_vkCmdResetEvent vkCmdResetEvent; +PFN_vkCmdResetQueryPool vkCmdResetQueryPool; +PFN_vkCmdResolveImage vkCmdResolveImage; +PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; +PFN_vkCmdSetDepthBias vkCmdSetDepthBias; +PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; +PFN_vkCmdSetEvent vkCmdSetEvent; +PFN_vkCmdSetLineWidth vkCmdSetLineWidth; +PFN_vkCmdSetScissor vkCmdSetScissor; +PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; +PFN_vkCmdSetStencilReference vkCmdSetStencilReference; +PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; +PFN_vkCmdSetViewport vkCmdSetViewport; +PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer; +PFN_vkCmdWaitEvents vkCmdWaitEvents; +PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp; +PFN_vkCreateBuffer vkCreateBuffer; +PFN_vkCreateBufferView vkCreateBufferView; +PFN_vkCreateCommandPool vkCreateCommandPool; +PFN_vkCreateComputePipelines vkCreateComputePipelines; +PFN_vkCreateDescriptorPool vkCreateDescriptorPool; +PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; +PFN_vkCreateDevice vkCreateDevice; +PFN_vkCreateEvent vkCreateEvent; +PFN_vkCreateFence vkCreateFence; +PFN_vkCreateFramebuffer vkCreateFramebuffer; +PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; +PFN_vkCreateImage vkCreateImage; +PFN_vkCreateImageView vkCreateImageView; +PFN_vkCreateInstance vkCreateInstance; +PFN_vkCreatePipelineCache vkCreatePipelineCache; +PFN_vkCreatePipelineLayout vkCreatePipelineLayout; +PFN_vkCreateQueryPool vkCreateQueryPool; +PFN_vkCreateRenderPass vkCreateRenderPass; +PFN_vkCreateSampler vkCreateSampler; +PFN_vkCreateSemaphore vkCreateSemaphore; +PFN_vkCreateShaderModule vkCreateShaderModule; +PFN_vkDestroyBuffer vkDestroyBuffer; +PFN_vkDestroyBufferView vkDestroyBufferView; +PFN_vkDestroyCommandPool vkDestroyCommandPool; +PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; +PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; +PFN_vkDestroyDevice vkDestroyDevice; +PFN_vkDestroyEvent vkDestroyEvent; +PFN_vkDestroyFence vkDestroyFence; +PFN_vkDestroyFramebuffer vkDestroyFramebuffer; +PFN_vkDestroyImage vkDestroyImage; +PFN_vkDestroyImageView vkDestroyImageView; +PFN_vkDestroyInstance vkDestroyInstance; +PFN_vkDestroyPipeline vkDestroyPipeline; +PFN_vkDestroyPipelineCache vkDestroyPipelineCache; +PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; +PFN_vkDestroyQueryPool vkDestroyQueryPool; +PFN_vkDestroyRenderPass vkDestroyRenderPass; +PFN_vkDestroySampler vkDestroySampler; +PFN_vkDestroySemaphore vkDestroySemaphore; +PFN_vkDestroyShaderModule vkDestroyShaderModule; +PFN_vkDeviceWaitIdle vkDeviceWaitIdle; +PFN_vkEndCommandBuffer vkEndCommandBuffer; +PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; +PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties; +PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; +PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; +PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; +PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; +PFN_vkFreeCommandBuffers vkFreeCommandBuffers; +PFN_vkFreeDescriptorSets vkFreeDescriptorSets; +PFN_vkFreeMemory vkFreeMemory; +PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; +PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment; +PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; +PFN_vkGetDeviceQueue vkGetDeviceQueue; +PFN_vkGetEventStatus vkGetEventStatus; +PFN_vkGetFenceStatus vkGetFenceStatus; +PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; +PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements; +PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout; +PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; +PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures; +PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; +PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties; +PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; +PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; +PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties; +PFN_vkGetPipelineCacheData vkGetPipelineCacheData; +PFN_vkGetQueryPoolResults vkGetQueryPoolResults; +PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity; +PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; +PFN_vkMapMemory vkMapMemory; +PFN_vkMergePipelineCaches vkMergePipelineCaches; +PFN_vkQueueBindSparse vkQueueBindSparse; +PFN_vkQueueSubmit vkQueueSubmit; +PFN_vkQueueWaitIdle vkQueueWaitIdle; +PFN_vkResetCommandBuffer vkResetCommandBuffer; +PFN_vkResetCommandPool vkResetCommandPool; +PFN_vkResetDescriptorPool vkResetDescriptorPool; +PFN_vkResetEvent vkResetEvent; +PFN_vkResetFences vkResetFences; +PFN_vkSetEvent vkSetEvent; +PFN_vkUnmapMemory vkUnmapMemory; +PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; +PFN_vkWaitForFences vkWaitForFences; +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) +PFN_vkBindBufferMemory2 vkBindBufferMemory2; +PFN_vkBindImageMemory2 vkBindImageMemory2; +PFN_vkCmdDispatchBase vkCmdDispatchBase; +PFN_vkCmdSetDeviceMask vkCmdSetDeviceMask; +PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate; +PFN_vkCreateSamplerYcbcrConversion vkCreateSamplerYcbcrConversion; +PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate; +PFN_vkDestroySamplerYcbcrConversion vkDestroySamplerYcbcrConversion; +PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion; +PFN_vkEnumeratePhysicalDeviceGroups vkEnumeratePhysicalDeviceGroups; +PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; +PFN_vkGetDescriptorSetLayoutSupport vkGetDescriptorSetLayoutSupport; +PFN_vkGetDeviceGroupPeerMemoryFeatures vkGetDeviceGroupPeerMemoryFeatures; +PFN_vkGetDeviceQueue2 vkGetDeviceQueue2; +PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; +PFN_vkGetImageSparseMemoryRequirements2 vkGetImageSparseMemoryRequirements2; +PFN_vkGetPhysicalDeviceExternalBufferProperties vkGetPhysicalDeviceExternalBufferProperties; +PFN_vkGetPhysicalDeviceExternalFenceProperties vkGetPhysicalDeviceExternalFenceProperties; +PFN_vkGetPhysicalDeviceExternalSemaphoreProperties vkGetPhysicalDeviceExternalSemaphoreProperties; +PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2; +PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2; +PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2; +PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; +PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2; +PFN_vkGetPhysicalDeviceQueueFamilyProperties2 vkGetPhysicalDeviceQueueFamilyProperties2; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 vkGetPhysicalDeviceSparseImageFormatProperties2; +PFN_vkTrimCommandPool vkTrimCommandPool; +PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate; +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_2) +PFN_vkCmdBeginRenderPass2 vkCmdBeginRenderPass2; +PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount; +PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount; +PFN_vkCmdEndRenderPass2 vkCmdEndRenderPass2; +PFN_vkCmdNextSubpass2 vkCmdNextSubpass2; +PFN_vkCreateRenderPass2 vkCreateRenderPass2; +PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress; +PFN_vkGetBufferOpaqueCaptureAddress vkGetBufferOpaqueCaptureAddress; +PFN_vkGetDeviceMemoryOpaqueCaptureAddress vkGetDeviceMemoryOpaqueCaptureAddress; +PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue; +PFN_vkResetQueryPool vkResetQueryPool; +PFN_vkSignalSemaphore vkSignalSemaphore; +PFN_vkWaitSemaphores vkWaitSemaphores; +#endif /* defined(VK_VERSION_1_2) */ +#if defined(VK_VERSION_1_3) +PFN_vkCmdBeginRendering vkCmdBeginRendering; +PFN_vkCmdBindVertexBuffers2 vkCmdBindVertexBuffers2; +PFN_vkCmdBlitImage2 vkCmdBlitImage2; +PFN_vkCmdCopyBuffer2 vkCmdCopyBuffer2; +PFN_vkCmdCopyBufferToImage2 vkCmdCopyBufferToImage2; +PFN_vkCmdCopyImage2 vkCmdCopyImage2; +PFN_vkCmdCopyImageToBuffer2 vkCmdCopyImageToBuffer2; +PFN_vkCmdEndRendering vkCmdEndRendering; +PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2; +PFN_vkCmdResetEvent2 vkCmdResetEvent2; +PFN_vkCmdResolveImage2 vkCmdResolveImage2; +PFN_vkCmdSetCullMode vkCmdSetCullMode; +PFN_vkCmdSetDepthBiasEnable vkCmdSetDepthBiasEnable; +PFN_vkCmdSetDepthBoundsTestEnable vkCmdSetDepthBoundsTestEnable; +PFN_vkCmdSetDepthCompareOp vkCmdSetDepthCompareOp; +PFN_vkCmdSetDepthTestEnable vkCmdSetDepthTestEnable; +PFN_vkCmdSetDepthWriteEnable vkCmdSetDepthWriteEnable; +PFN_vkCmdSetEvent2 vkCmdSetEvent2; +PFN_vkCmdSetFrontFace vkCmdSetFrontFace; +PFN_vkCmdSetPrimitiveRestartEnable vkCmdSetPrimitiveRestartEnable; +PFN_vkCmdSetPrimitiveTopology vkCmdSetPrimitiveTopology; +PFN_vkCmdSetRasterizerDiscardEnable vkCmdSetRasterizerDiscardEnable; +PFN_vkCmdSetScissorWithCount vkCmdSetScissorWithCount; +PFN_vkCmdSetStencilOp vkCmdSetStencilOp; +PFN_vkCmdSetStencilTestEnable vkCmdSetStencilTestEnable; +PFN_vkCmdSetViewportWithCount vkCmdSetViewportWithCount; +PFN_vkCmdWaitEvents2 vkCmdWaitEvents2; +PFN_vkCmdWriteTimestamp2 vkCmdWriteTimestamp2; +PFN_vkCreatePrivateDataSlot vkCreatePrivateDataSlot; +PFN_vkDestroyPrivateDataSlot vkDestroyPrivateDataSlot; +PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements; +PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements; +PFN_vkGetDeviceImageSparseMemoryRequirements vkGetDeviceImageSparseMemoryRequirements; +PFN_vkGetPhysicalDeviceToolProperties vkGetPhysicalDeviceToolProperties; +PFN_vkGetPrivateData vkGetPrivateData; +PFN_vkQueueSubmit2 vkQueueSubmit2; +PFN_vkSetPrivateData vkSetPrivateData; +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_AMDX_shader_enqueue) +PFN_vkCmdDispatchGraphAMDX vkCmdDispatchGraphAMDX; +PFN_vkCmdDispatchGraphIndirectAMDX vkCmdDispatchGraphIndirectAMDX; +PFN_vkCmdDispatchGraphIndirectCountAMDX vkCmdDispatchGraphIndirectCountAMDX; +PFN_vkCmdInitializeGraphScratchMemoryAMDX vkCmdInitializeGraphScratchMemoryAMDX; +PFN_vkCreateExecutionGraphPipelinesAMDX vkCreateExecutionGraphPipelinesAMDX; +PFN_vkGetExecutionGraphPipelineNodeIndexAMDX vkGetExecutionGraphPipelineNodeIndexAMDX; +PFN_vkGetExecutionGraphPipelineScratchSizeAMDX vkGetExecutionGraphPipelineScratchSizeAMDX; +#endif /* defined(VK_AMDX_shader_enqueue) */ +#if defined(VK_AMD_anti_lag) +PFN_vkAntiLagUpdateAMD vkAntiLagUpdateAMD; +#endif /* defined(VK_AMD_anti_lag) */ +#if defined(VK_AMD_buffer_marker) +PFN_vkCmdWriteBufferMarkerAMD vkCmdWriteBufferMarkerAMD; +#endif /* defined(VK_AMD_buffer_marker) */ +#if defined(VK_AMD_display_native_hdr) +PFN_vkSetLocalDimmingAMD vkSetLocalDimmingAMD; +#endif /* defined(VK_AMD_display_native_hdr) */ +#if defined(VK_AMD_draw_indirect_count) +PFN_vkCmdDrawIndexedIndirectCountAMD vkCmdDrawIndexedIndirectCountAMD; +PFN_vkCmdDrawIndirectCountAMD vkCmdDrawIndirectCountAMD; +#endif /* defined(VK_AMD_draw_indirect_count) */ +#if defined(VK_AMD_shader_info) +PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD; +#endif /* defined(VK_AMD_shader_info) */ +#if defined(VK_ANDROID_external_memory_android_hardware_buffer) +PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID; +PFN_vkGetMemoryAndroidHardwareBufferANDROID vkGetMemoryAndroidHardwareBufferANDROID; +#endif /* defined(VK_ANDROID_external_memory_android_hardware_buffer) */ +#if defined(VK_EXT_acquire_drm_display) +PFN_vkAcquireDrmDisplayEXT vkAcquireDrmDisplayEXT; +PFN_vkGetDrmDisplayEXT vkGetDrmDisplayEXT; +#endif /* defined(VK_EXT_acquire_drm_display) */ +#if defined(VK_EXT_acquire_xlib_display) +PFN_vkAcquireXlibDisplayEXT vkAcquireXlibDisplayEXT; +PFN_vkGetRandROutputDisplayEXT vkGetRandROutputDisplayEXT; +#endif /* defined(VK_EXT_acquire_xlib_display) */ +#if defined(VK_EXT_attachment_feedback_loop_dynamic_state) +PFN_vkCmdSetAttachmentFeedbackLoopEnableEXT vkCmdSetAttachmentFeedbackLoopEnableEXT; +#endif /* defined(VK_EXT_attachment_feedback_loop_dynamic_state) */ +#if defined(VK_EXT_buffer_device_address) +PFN_vkGetBufferDeviceAddressEXT vkGetBufferDeviceAddressEXT; +#endif /* defined(VK_EXT_buffer_device_address) */ +#if defined(VK_EXT_calibrated_timestamps) +PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT; +PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT vkGetPhysicalDeviceCalibrateableTimeDomainsEXT; +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_color_write_enable) +PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT; +#endif /* defined(VK_EXT_color_write_enable) */ +#if defined(VK_EXT_conditional_rendering) +PFN_vkCmdBeginConditionalRenderingEXT vkCmdBeginConditionalRenderingEXT; +PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT; +#endif /* defined(VK_EXT_conditional_rendering) */ +#if defined(VK_EXT_debug_marker) +PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBeginEXT; +PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEndEXT; +PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsertEXT; +PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectNameEXT; +PFN_vkDebugMarkerSetObjectTagEXT vkDebugMarkerSetObjectTagEXT; +#endif /* defined(VK_EXT_debug_marker) */ +#if defined(VK_EXT_debug_report) +PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; +PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT; +PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; +#endif /* defined(VK_EXT_debug_report) */ +#if defined(VK_EXT_debug_utils) +PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT; +PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT; +PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT; +PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; +PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; +PFN_vkQueueBeginDebugUtilsLabelEXT vkQueueBeginDebugUtilsLabelEXT; +PFN_vkQueueEndDebugUtilsLabelEXT vkQueueEndDebugUtilsLabelEXT; +PFN_vkQueueInsertDebugUtilsLabelEXT vkQueueInsertDebugUtilsLabelEXT; +PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT; +PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT; +PFN_vkSubmitDebugUtilsMessageEXT vkSubmitDebugUtilsMessageEXT; +#endif /* defined(VK_EXT_debug_utils) */ +#if defined(VK_EXT_depth_bias_control) +PFN_vkCmdSetDepthBias2EXT vkCmdSetDepthBias2EXT; +#endif /* defined(VK_EXT_depth_bias_control) */ +#if defined(VK_EXT_descriptor_buffer) +PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT vkCmdBindDescriptorBufferEmbeddedSamplersEXT; +PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffersEXT; +PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsetsEXT; +PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT vkGetBufferOpaqueCaptureDescriptorDataEXT; +PFN_vkGetDescriptorEXT vkGetDescriptorEXT; +PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffsetEXT; +PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT; +PFN_vkGetImageOpaqueCaptureDescriptorDataEXT vkGetImageOpaqueCaptureDescriptorDataEXT; +PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT vkGetImageViewOpaqueCaptureDescriptorDataEXT; +PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT vkGetSamplerOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) +PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) */ +#if defined(VK_EXT_device_fault) +PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT; +#endif /* defined(VK_EXT_device_fault) */ +#if defined(VK_EXT_direct_mode_display) +PFN_vkReleaseDisplayEXT vkReleaseDisplayEXT; +#endif /* defined(VK_EXT_direct_mode_display) */ +#if defined(VK_EXT_directfb_surface) +PFN_vkCreateDirectFBSurfaceEXT vkCreateDirectFBSurfaceEXT; +PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT vkGetPhysicalDeviceDirectFBPresentationSupportEXT; +#endif /* defined(VK_EXT_directfb_surface) */ +#if defined(VK_EXT_discard_rectangles) +PFN_vkCmdSetDiscardRectangleEXT vkCmdSetDiscardRectangleEXT; +#endif /* defined(VK_EXT_discard_rectangles) */ +#if defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 +PFN_vkCmdSetDiscardRectangleEnableEXT vkCmdSetDiscardRectangleEnableEXT; +PFN_vkCmdSetDiscardRectangleModeEXT vkCmdSetDiscardRectangleModeEXT; +#endif /* defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 */ +#if defined(VK_EXT_display_control) +PFN_vkDisplayPowerControlEXT vkDisplayPowerControlEXT; +PFN_vkGetSwapchainCounterEXT vkGetSwapchainCounterEXT; +PFN_vkRegisterDeviceEventEXT vkRegisterDeviceEventEXT; +PFN_vkRegisterDisplayEventEXT vkRegisterDisplayEventEXT; +#endif /* defined(VK_EXT_display_control) */ +#if defined(VK_EXT_display_surface_counter) +PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT vkGetPhysicalDeviceSurfaceCapabilities2EXT; +#endif /* defined(VK_EXT_display_surface_counter) */ +#if defined(VK_EXT_external_memory_host) +PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesEXT; +#endif /* defined(VK_EXT_external_memory_host) */ +#if defined(VK_EXT_full_screen_exclusive) +PFN_vkAcquireFullScreenExclusiveModeEXT vkAcquireFullScreenExclusiveModeEXT; +PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT vkGetPhysicalDeviceSurfacePresentModes2EXT; +PFN_vkReleaseFullScreenExclusiveModeEXT vkReleaseFullScreenExclusiveModeEXT; +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) +PFN_vkGetDeviceGroupSurfacePresentModes2EXT vkGetDeviceGroupSurfacePresentModes2EXT; +#endif /* defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) */ +#if defined(VK_EXT_hdr_metadata) +PFN_vkSetHdrMetadataEXT vkSetHdrMetadataEXT; +#endif /* defined(VK_EXT_hdr_metadata) */ +#if defined(VK_EXT_headless_surface) +PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT; +#endif /* defined(VK_EXT_headless_surface) */ +#if defined(VK_EXT_host_image_copy) +PFN_vkCopyImageToImageEXT vkCopyImageToImageEXT; +PFN_vkCopyImageToMemoryEXT vkCopyImageToMemoryEXT; +PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImageEXT; +PFN_vkTransitionImageLayoutEXT vkTransitionImageLayoutEXT; +#endif /* defined(VK_EXT_host_image_copy) */ +#if defined(VK_EXT_host_query_reset) +PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; +#endif /* defined(VK_EXT_host_query_reset) */ +#if defined(VK_EXT_image_drm_format_modifier) +PFN_vkGetImageDrmFormatModifierPropertiesEXT vkGetImageDrmFormatModifierPropertiesEXT; +#endif /* defined(VK_EXT_image_drm_format_modifier) */ +#if defined(VK_EXT_line_rasterization) +PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT; +#endif /* defined(VK_EXT_line_rasterization) */ +#if defined(VK_EXT_mesh_shader) +PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXT; +PFN_vkCmdDrawMeshTasksIndirectCountEXT vkCmdDrawMeshTasksIndirectCountEXT; +PFN_vkCmdDrawMeshTasksIndirectEXT vkCmdDrawMeshTasksIndirectEXT; +#endif /* defined(VK_EXT_mesh_shader) */ +#if defined(VK_EXT_metal_objects) +PFN_vkExportMetalObjectsEXT vkExportMetalObjectsEXT; +#endif /* defined(VK_EXT_metal_objects) */ +#if defined(VK_EXT_metal_surface) +PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; +#endif /* defined(VK_EXT_metal_surface) */ +#if defined(VK_EXT_multi_draw) +PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT; +PFN_vkCmdDrawMultiIndexedEXT vkCmdDrawMultiIndexedEXT; +#endif /* defined(VK_EXT_multi_draw) */ +#if defined(VK_EXT_opacity_micromap) +PFN_vkBuildMicromapsEXT vkBuildMicromapsEXT; +PFN_vkCmdBuildMicromapsEXT vkCmdBuildMicromapsEXT; +PFN_vkCmdCopyMemoryToMicromapEXT vkCmdCopyMemoryToMicromapEXT; +PFN_vkCmdCopyMicromapEXT vkCmdCopyMicromapEXT; +PFN_vkCmdCopyMicromapToMemoryEXT vkCmdCopyMicromapToMemoryEXT; +PFN_vkCmdWriteMicromapsPropertiesEXT vkCmdWriteMicromapsPropertiesEXT; +PFN_vkCopyMemoryToMicromapEXT vkCopyMemoryToMicromapEXT; +PFN_vkCopyMicromapEXT vkCopyMicromapEXT; +PFN_vkCopyMicromapToMemoryEXT vkCopyMicromapToMemoryEXT; +PFN_vkCreateMicromapEXT vkCreateMicromapEXT; +PFN_vkDestroyMicromapEXT vkDestroyMicromapEXT; +PFN_vkGetDeviceMicromapCompatibilityEXT vkGetDeviceMicromapCompatibilityEXT; +PFN_vkGetMicromapBuildSizesEXT vkGetMicromapBuildSizesEXT; +PFN_vkWriteMicromapsPropertiesEXT vkWriteMicromapsPropertiesEXT; +#endif /* defined(VK_EXT_opacity_micromap) */ +#if defined(VK_EXT_pageable_device_local_memory) +PFN_vkSetDeviceMemoryPriorityEXT vkSetDeviceMemoryPriorityEXT; +#endif /* defined(VK_EXT_pageable_device_local_memory) */ +#if defined(VK_EXT_pipeline_properties) +PFN_vkGetPipelinePropertiesEXT vkGetPipelinePropertiesEXT; +#endif /* defined(VK_EXT_pipeline_properties) */ +#if defined(VK_EXT_private_data) +PFN_vkCreatePrivateDataSlotEXT vkCreatePrivateDataSlotEXT; +PFN_vkDestroyPrivateDataSlotEXT vkDestroyPrivateDataSlotEXT; +PFN_vkGetPrivateDataEXT vkGetPrivateDataEXT; +PFN_vkSetPrivateDataEXT vkSetPrivateDataEXT; +#endif /* defined(VK_EXT_private_data) */ +#if defined(VK_EXT_sample_locations) +PFN_vkCmdSetSampleLocationsEXT vkCmdSetSampleLocationsEXT; +PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT vkGetPhysicalDeviceMultisamplePropertiesEXT; +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_shader_module_identifier) +PFN_vkGetShaderModuleCreateInfoIdentifierEXT vkGetShaderModuleCreateInfoIdentifierEXT; +PFN_vkGetShaderModuleIdentifierEXT vkGetShaderModuleIdentifierEXT; +#endif /* defined(VK_EXT_shader_module_identifier) */ +#if defined(VK_EXT_shader_object) +PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT; +PFN_vkCreateShadersEXT vkCreateShadersEXT; +PFN_vkDestroyShaderEXT vkDestroyShaderEXT; +PFN_vkGetShaderBinaryDataEXT vkGetShaderBinaryDataEXT; +#endif /* defined(VK_EXT_shader_object) */ +#if defined(VK_EXT_swapchain_maintenance1) +PFN_vkReleaseSwapchainImagesEXT vkReleaseSwapchainImagesEXT; +#endif /* defined(VK_EXT_swapchain_maintenance1) */ +#if defined(VK_EXT_tooling_info) +PFN_vkGetPhysicalDeviceToolPropertiesEXT vkGetPhysicalDeviceToolPropertiesEXT; +#endif /* defined(VK_EXT_tooling_info) */ +#if defined(VK_EXT_transform_feedback) +PFN_vkCmdBeginQueryIndexedEXT vkCmdBeginQueryIndexedEXT; +PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; +PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; +PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT; +PFN_vkCmdEndQueryIndexedEXT vkCmdEndQueryIndexedEXT; +PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; +#endif /* defined(VK_EXT_transform_feedback) */ +#if defined(VK_EXT_validation_cache) +PFN_vkCreateValidationCacheEXT vkCreateValidationCacheEXT; +PFN_vkDestroyValidationCacheEXT vkDestroyValidationCacheEXT; +PFN_vkGetValidationCacheDataEXT vkGetValidationCacheDataEXT; +PFN_vkMergeValidationCachesEXT vkMergeValidationCachesEXT; +#endif /* defined(VK_EXT_validation_cache) */ +#if defined(VK_FUCHSIA_buffer_collection) +PFN_vkCreateBufferCollectionFUCHSIA vkCreateBufferCollectionFUCHSIA; +PFN_vkDestroyBufferCollectionFUCHSIA vkDestroyBufferCollectionFUCHSIA; +PFN_vkGetBufferCollectionPropertiesFUCHSIA vkGetBufferCollectionPropertiesFUCHSIA; +PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA vkSetBufferCollectionBufferConstraintsFUCHSIA; +PFN_vkSetBufferCollectionImageConstraintsFUCHSIA vkSetBufferCollectionImageConstraintsFUCHSIA; +#endif /* defined(VK_FUCHSIA_buffer_collection) */ +#if defined(VK_FUCHSIA_external_memory) +PFN_vkGetMemoryZirconHandleFUCHSIA vkGetMemoryZirconHandleFUCHSIA; +PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA vkGetMemoryZirconHandlePropertiesFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_memory) */ +#if defined(VK_FUCHSIA_external_semaphore) +PFN_vkGetSemaphoreZirconHandleFUCHSIA vkGetSemaphoreZirconHandleFUCHSIA; +PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_semaphore) */ +#if defined(VK_FUCHSIA_imagepipe_surface) +PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA; +#endif /* defined(VK_FUCHSIA_imagepipe_surface) */ +#if defined(VK_GGP_stream_descriptor_surface) +PFN_vkCreateStreamDescriptorSurfaceGGP vkCreateStreamDescriptorSurfaceGGP; +#endif /* defined(VK_GGP_stream_descriptor_surface) */ +#if defined(VK_GOOGLE_display_timing) +PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE; +PFN_vkGetRefreshCycleDurationGOOGLE vkGetRefreshCycleDurationGOOGLE; +#endif /* defined(VK_GOOGLE_display_timing) */ +#if defined(VK_HUAWEI_cluster_culling_shader) +PFN_vkCmdDrawClusterHUAWEI vkCmdDrawClusterHUAWEI; +PFN_vkCmdDrawClusterIndirectHUAWEI vkCmdDrawClusterIndirectHUAWEI; +#endif /* defined(VK_HUAWEI_cluster_culling_shader) */ +#if defined(VK_HUAWEI_invocation_mask) +PFN_vkCmdBindInvocationMaskHUAWEI vkCmdBindInvocationMaskHUAWEI; +#endif /* defined(VK_HUAWEI_invocation_mask) */ +#if defined(VK_HUAWEI_subpass_shading) +PFN_vkCmdSubpassShadingHUAWEI vkCmdSubpassShadingHUAWEI; +PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI; +#endif /* defined(VK_HUAWEI_subpass_shading) */ +#if defined(VK_INTEL_performance_query) +PFN_vkAcquirePerformanceConfigurationINTEL vkAcquirePerformanceConfigurationINTEL; +PFN_vkCmdSetPerformanceMarkerINTEL vkCmdSetPerformanceMarkerINTEL; +PFN_vkCmdSetPerformanceOverrideINTEL vkCmdSetPerformanceOverrideINTEL; +PFN_vkCmdSetPerformanceStreamMarkerINTEL vkCmdSetPerformanceStreamMarkerINTEL; +PFN_vkGetPerformanceParameterINTEL vkGetPerformanceParameterINTEL; +PFN_vkInitializePerformanceApiINTEL vkInitializePerformanceApiINTEL; +PFN_vkQueueSetPerformanceConfigurationINTEL vkQueueSetPerformanceConfigurationINTEL; +PFN_vkReleasePerformanceConfigurationINTEL vkReleasePerformanceConfigurationINTEL; +PFN_vkUninitializePerformanceApiINTEL vkUninitializePerformanceApiINTEL; +#endif /* defined(VK_INTEL_performance_query) */ +#if defined(VK_KHR_acceleration_structure) +PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR; +PFN_vkCmdBuildAccelerationStructuresIndirectKHR vkCmdBuildAccelerationStructuresIndirectKHR; +PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; +PFN_vkCmdCopyAccelerationStructureKHR vkCmdCopyAccelerationStructureKHR; +PFN_vkCmdCopyAccelerationStructureToMemoryKHR vkCmdCopyAccelerationStructureToMemoryKHR; +PFN_vkCmdCopyMemoryToAccelerationStructureKHR vkCmdCopyMemoryToAccelerationStructureKHR; +PFN_vkCmdWriteAccelerationStructuresPropertiesKHR vkCmdWriteAccelerationStructuresPropertiesKHR; +PFN_vkCopyAccelerationStructureKHR vkCopyAccelerationStructureKHR; +PFN_vkCopyAccelerationStructureToMemoryKHR vkCopyAccelerationStructureToMemoryKHR; +PFN_vkCopyMemoryToAccelerationStructureKHR vkCopyMemoryToAccelerationStructureKHR; +PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; +PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR; +PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; +PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; +PFN_vkGetDeviceAccelerationStructureCompatibilityKHR vkGetDeviceAccelerationStructureCompatibilityKHR; +PFN_vkWriteAccelerationStructuresPropertiesKHR vkWriteAccelerationStructuresPropertiesKHR; +#endif /* defined(VK_KHR_acceleration_structure) */ +#if defined(VK_KHR_android_surface) +PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; +#endif /* defined(VK_KHR_android_surface) */ +#if defined(VK_KHR_bind_memory2) +PFN_vkBindBufferMemory2KHR vkBindBufferMemory2KHR; +PFN_vkBindImageMemory2KHR vkBindImageMemory2KHR; +#endif /* defined(VK_KHR_bind_memory2) */ +#if defined(VK_KHR_buffer_device_address) +PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; +PFN_vkGetBufferOpaqueCaptureAddressKHR vkGetBufferOpaqueCaptureAddressKHR; +PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR vkGetDeviceMemoryOpaqueCaptureAddressKHR; +#endif /* defined(VK_KHR_buffer_device_address) */ +#if defined(VK_KHR_calibrated_timestamps) +PFN_vkGetCalibratedTimestampsKHR vkGetCalibratedTimestampsKHR; +PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR vkGetPhysicalDeviceCalibrateableTimeDomainsKHR; +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_cooperative_matrix) +PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR; +#endif /* defined(VK_KHR_cooperative_matrix) */ +#if defined(VK_KHR_copy_commands2) +PFN_vkCmdBlitImage2KHR vkCmdBlitImage2KHR; +PFN_vkCmdCopyBuffer2KHR vkCmdCopyBuffer2KHR; +PFN_vkCmdCopyBufferToImage2KHR vkCmdCopyBufferToImage2KHR; +PFN_vkCmdCopyImage2KHR vkCmdCopyImage2KHR; +PFN_vkCmdCopyImageToBuffer2KHR vkCmdCopyImageToBuffer2KHR; +PFN_vkCmdResolveImage2KHR vkCmdResolveImage2KHR; +#endif /* defined(VK_KHR_copy_commands2) */ +#if defined(VK_KHR_create_renderpass2) +PFN_vkCmdBeginRenderPass2KHR vkCmdBeginRenderPass2KHR; +PFN_vkCmdEndRenderPass2KHR vkCmdEndRenderPass2KHR; +PFN_vkCmdNextSubpass2KHR vkCmdNextSubpass2KHR; +PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR; +#endif /* defined(VK_KHR_create_renderpass2) */ +#if defined(VK_KHR_deferred_host_operations) +PFN_vkCreateDeferredOperationKHR vkCreateDeferredOperationKHR; +PFN_vkDeferredOperationJoinKHR vkDeferredOperationJoinKHR; +PFN_vkDestroyDeferredOperationKHR vkDestroyDeferredOperationKHR; +PFN_vkGetDeferredOperationMaxConcurrencyKHR vkGetDeferredOperationMaxConcurrencyKHR; +PFN_vkGetDeferredOperationResultKHR vkGetDeferredOperationResultKHR; +#endif /* defined(VK_KHR_deferred_host_operations) */ +#if defined(VK_KHR_descriptor_update_template) +PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; +PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; +PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; +#endif /* defined(VK_KHR_descriptor_update_template) */ +#if defined(VK_KHR_device_group) +PFN_vkCmdDispatchBaseKHR vkCmdDispatchBaseKHR; +PFN_vkCmdSetDeviceMaskKHR vkCmdSetDeviceMaskKHR; +PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR vkGetDeviceGroupPeerMemoryFeaturesKHR; +#endif /* defined(VK_KHR_device_group) */ +#if defined(VK_KHR_device_group_creation) +PFN_vkEnumeratePhysicalDeviceGroupsKHR vkEnumeratePhysicalDeviceGroupsKHR; +#endif /* defined(VK_KHR_device_group_creation) */ +#if defined(VK_KHR_display) +PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR; +PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR; +PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR; +PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR; +PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR; +PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR; +PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR; +#endif /* defined(VK_KHR_display) */ +#if defined(VK_KHR_display_swapchain) +PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR; +#endif /* defined(VK_KHR_display_swapchain) */ +#if defined(VK_KHR_draw_indirect_count) +PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR; +PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR; +#endif /* defined(VK_KHR_draw_indirect_count) */ +#if defined(VK_KHR_dynamic_rendering) +PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHR; +PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHR; +#endif /* defined(VK_KHR_dynamic_rendering) */ +#if defined(VK_KHR_dynamic_rendering_local_read) +PFN_vkCmdSetRenderingAttachmentLocationsKHR vkCmdSetRenderingAttachmentLocationsKHR; +PFN_vkCmdSetRenderingInputAttachmentIndicesKHR vkCmdSetRenderingInputAttachmentIndicesKHR; +#endif /* defined(VK_KHR_dynamic_rendering_local_read) */ +#if defined(VK_KHR_external_fence_capabilities) +PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR vkGetPhysicalDeviceExternalFencePropertiesKHR; +#endif /* defined(VK_KHR_external_fence_capabilities) */ +#if defined(VK_KHR_external_fence_fd) +PFN_vkGetFenceFdKHR vkGetFenceFdKHR; +PFN_vkImportFenceFdKHR vkImportFenceFdKHR; +#endif /* defined(VK_KHR_external_fence_fd) */ +#if defined(VK_KHR_external_fence_win32) +PFN_vkGetFenceWin32HandleKHR vkGetFenceWin32HandleKHR; +PFN_vkImportFenceWin32HandleKHR vkImportFenceWin32HandleKHR; +#endif /* defined(VK_KHR_external_fence_win32) */ +#if defined(VK_KHR_external_memory_capabilities) +PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR vkGetPhysicalDeviceExternalBufferPropertiesKHR; +#endif /* defined(VK_KHR_external_memory_capabilities) */ +#if defined(VK_KHR_external_memory_fd) +PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR; +PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR; +#endif /* defined(VK_KHR_external_memory_fd) */ +#if defined(VK_KHR_external_memory_win32) +PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR; +PFN_vkGetMemoryWin32HandlePropertiesKHR vkGetMemoryWin32HandlePropertiesKHR; +#endif /* defined(VK_KHR_external_memory_win32) */ +#if defined(VK_KHR_external_semaphore_capabilities) +PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR vkGetPhysicalDeviceExternalSemaphorePropertiesKHR; +#endif /* defined(VK_KHR_external_semaphore_capabilities) */ +#if defined(VK_KHR_external_semaphore_fd) +PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; +PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; +#endif /* defined(VK_KHR_external_semaphore_fd) */ +#if defined(VK_KHR_external_semaphore_win32) +PFN_vkGetSemaphoreWin32HandleKHR vkGetSemaphoreWin32HandleKHR; +PFN_vkImportSemaphoreWin32HandleKHR vkImportSemaphoreWin32HandleKHR; +#endif /* defined(VK_KHR_external_semaphore_win32) */ +#if defined(VK_KHR_fragment_shading_rate) +PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR; +PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR; +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_display_properties2) +PFN_vkGetDisplayModeProperties2KHR vkGetDisplayModeProperties2KHR; +PFN_vkGetDisplayPlaneCapabilities2KHR vkGetDisplayPlaneCapabilities2KHR; +PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR vkGetPhysicalDeviceDisplayPlaneProperties2KHR; +PFN_vkGetPhysicalDeviceDisplayProperties2KHR vkGetPhysicalDeviceDisplayProperties2KHR; +#endif /* defined(VK_KHR_get_display_properties2) */ +#if defined(VK_KHR_get_memory_requirements2) +PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; +PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; +PFN_vkGetImageSparseMemoryRequirements2KHR vkGetImageSparseMemoryRequirements2KHR; +#endif /* defined(VK_KHR_get_memory_requirements2) */ +#if defined(VK_KHR_get_physical_device_properties2) +PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; +PFN_vkGetPhysicalDeviceFormatProperties2KHR vkGetPhysicalDeviceFormatProperties2KHR; +PFN_vkGetPhysicalDeviceImageFormatProperties2KHR vkGetPhysicalDeviceImageFormatProperties2KHR; +PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR; +PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; +PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR vkGetPhysicalDeviceQueueFamilyProperties2KHR; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR vkGetPhysicalDeviceSparseImageFormatProperties2KHR; +#endif /* defined(VK_KHR_get_physical_device_properties2) */ +#if defined(VK_KHR_get_surface_capabilities2) +PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR; +PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR; +#endif /* defined(VK_KHR_get_surface_capabilities2) */ +#if defined(VK_KHR_line_rasterization) +PFN_vkCmdSetLineStippleKHR vkCmdSetLineStippleKHR; +#endif /* defined(VK_KHR_line_rasterization) */ +#if defined(VK_KHR_maintenance1) +PFN_vkTrimCommandPoolKHR vkTrimCommandPoolKHR; +#endif /* defined(VK_KHR_maintenance1) */ +#if defined(VK_KHR_maintenance3) +PFN_vkGetDescriptorSetLayoutSupportKHR vkGetDescriptorSetLayoutSupportKHR; +#endif /* defined(VK_KHR_maintenance3) */ +#if defined(VK_KHR_maintenance4) +PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirementsKHR; +PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirementsKHR; +PFN_vkGetDeviceImageSparseMemoryRequirementsKHR vkGetDeviceImageSparseMemoryRequirementsKHR; +#endif /* defined(VK_KHR_maintenance4) */ +#if defined(VK_KHR_maintenance5) +PFN_vkCmdBindIndexBuffer2KHR vkCmdBindIndexBuffer2KHR; +PFN_vkGetDeviceImageSubresourceLayoutKHR vkGetDeviceImageSubresourceLayoutKHR; +PFN_vkGetImageSubresourceLayout2KHR vkGetImageSubresourceLayout2KHR; +PFN_vkGetRenderingAreaGranularityKHR vkGetRenderingAreaGranularityKHR; +#endif /* defined(VK_KHR_maintenance5) */ +#if defined(VK_KHR_maintenance6) +PFN_vkCmdBindDescriptorSets2KHR vkCmdBindDescriptorSets2KHR; +PFN_vkCmdPushConstants2KHR vkCmdPushConstants2KHR; +#endif /* defined(VK_KHR_maintenance6) */ +#if defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) +PFN_vkCmdPushDescriptorSet2KHR vkCmdPushDescriptorSet2KHR; +PFN_vkCmdPushDescriptorSetWithTemplate2KHR vkCmdPushDescriptorSetWithTemplate2KHR; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) +PFN_vkCmdBindDescriptorBufferEmbeddedSamplers2EXT vkCmdBindDescriptorBufferEmbeddedSamplers2EXT; +PFN_vkCmdSetDescriptorBufferOffsets2EXT vkCmdSetDescriptorBufferOffsets2EXT; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_KHR_map_memory2) +PFN_vkMapMemory2KHR vkMapMemory2KHR; +PFN_vkUnmapMemory2KHR vkUnmapMemory2KHR; +#endif /* defined(VK_KHR_map_memory2) */ +#if defined(VK_KHR_performance_query) +PFN_vkAcquireProfilingLockKHR vkAcquireProfilingLockKHR; +PFN_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR; +PFN_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR; +PFN_vkReleaseProfilingLockKHR vkReleaseProfilingLockKHR; +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_pipeline_binary) +PFN_vkCreatePipelineBinariesKHR vkCreatePipelineBinariesKHR; +PFN_vkDestroyPipelineBinaryKHR vkDestroyPipelineBinaryKHR; +PFN_vkGetPipelineBinaryDataKHR vkGetPipelineBinaryDataKHR; +PFN_vkGetPipelineKeyKHR vkGetPipelineKeyKHR; +PFN_vkReleaseCapturedPipelineDataKHR vkReleaseCapturedPipelineDataKHR; +#endif /* defined(VK_KHR_pipeline_binary) */ +#if defined(VK_KHR_pipeline_executable_properties) +PFN_vkGetPipelineExecutableInternalRepresentationsKHR vkGetPipelineExecutableInternalRepresentationsKHR; +PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR; +PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR; +#endif /* defined(VK_KHR_pipeline_executable_properties) */ +#if defined(VK_KHR_present_wait) +PFN_vkWaitForPresentKHR vkWaitForPresentKHR; +#endif /* defined(VK_KHR_present_wait) */ +#if defined(VK_KHR_push_descriptor) +PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR; +#endif /* defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) +PFN_vkCmdTraceRaysIndirect2KHR vkCmdTraceRaysIndirect2KHR; +#endif /* defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_ray_tracing_pipeline) +PFN_vkCmdSetRayTracingPipelineStackSizeKHR vkCmdSetRayTracingPipelineStackSizeKHR; +PFN_vkCmdTraceRaysIndirectKHR vkCmdTraceRaysIndirectKHR; +PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; +PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; +PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR vkGetRayTracingCaptureReplayShaderGroupHandlesKHR; +PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; +PFN_vkGetRayTracingShaderGroupStackSizeKHR vkGetRayTracingShaderGroupStackSizeKHR; +#endif /* defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_sampler_ycbcr_conversion) +PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; +PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; +#endif /* defined(VK_KHR_sampler_ycbcr_conversion) */ +#if defined(VK_KHR_shared_presentable_image) +PFN_vkGetSwapchainStatusKHR vkGetSwapchainStatusKHR; +#endif /* defined(VK_KHR_shared_presentable_image) */ +#if defined(VK_KHR_surface) +PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; +PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; +PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; +PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; +PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; +#endif /* defined(VK_KHR_surface) */ +#if defined(VK_KHR_swapchain) +PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; +PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; +PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; +PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; +PFN_vkQueuePresentKHR vkQueuePresentKHR; +#endif /* defined(VK_KHR_swapchain) */ +#if defined(VK_KHR_synchronization2) +PFN_vkCmdPipelineBarrier2KHR vkCmdPipelineBarrier2KHR; +PFN_vkCmdResetEvent2KHR vkCmdResetEvent2KHR; +PFN_vkCmdSetEvent2KHR vkCmdSetEvent2KHR; +PFN_vkCmdWaitEvents2KHR vkCmdWaitEvents2KHR; +PFN_vkCmdWriteTimestamp2KHR vkCmdWriteTimestamp2KHR; +PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; +#endif /* defined(VK_KHR_synchronization2) */ +#if defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) +PFN_vkCmdWriteBufferMarker2AMD vkCmdWriteBufferMarker2AMD; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) */ +#if defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) +PFN_vkGetQueueCheckpointData2NV vkGetQueueCheckpointData2NV; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_KHR_timeline_semaphore) +PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; +PFN_vkSignalSemaphoreKHR vkSignalSemaphoreKHR; +PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; +#endif /* defined(VK_KHR_timeline_semaphore) */ +#if defined(VK_KHR_video_decode_queue) +PFN_vkCmdDecodeVideoKHR vkCmdDecodeVideoKHR; +#endif /* defined(VK_KHR_video_decode_queue) */ +#if defined(VK_KHR_video_encode_queue) +PFN_vkCmdEncodeVideoKHR vkCmdEncodeVideoKHR; +PFN_vkGetEncodedVideoSessionParametersKHR vkGetEncodedVideoSessionParametersKHR; +PFN_vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR; +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) +PFN_vkBindVideoSessionMemoryKHR vkBindVideoSessionMemoryKHR; +PFN_vkCmdBeginVideoCodingKHR vkCmdBeginVideoCodingKHR; +PFN_vkCmdControlVideoCodingKHR vkCmdControlVideoCodingKHR; +PFN_vkCmdEndVideoCodingKHR vkCmdEndVideoCodingKHR; +PFN_vkCreateVideoSessionKHR vkCreateVideoSessionKHR; +PFN_vkCreateVideoSessionParametersKHR vkCreateVideoSessionParametersKHR; +PFN_vkDestroyVideoSessionKHR vkDestroyVideoSessionKHR; +PFN_vkDestroyVideoSessionParametersKHR vkDestroyVideoSessionParametersKHR; +PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR vkGetPhysicalDeviceVideoCapabilitiesKHR; +PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR vkGetPhysicalDeviceVideoFormatPropertiesKHR; +PFN_vkGetVideoSessionMemoryRequirementsKHR vkGetVideoSessionMemoryRequirementsKHR; +PFN_vkUpdateVideoSessionParametersKHR vkUpdateVideoSessionParametersKHR; +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_KHR_wayland_surface) +PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; +PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR; +#endif /* defined(VK_KHR_wayland_surface) */ +#if defined(VK_KHR_win32_surface) +PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; +PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR; +#endif /* defined(VK_KHR_win32_surface) */ +#if defined(VK_KHR_xcb_surface) +PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; +PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR; +#endif /* defined(VK_KHR_xcb_surface) */ +#if defined(VK_KHR_xlib_surface) +PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; +PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR; +#endif /* defined(VK_KHR_xlib_surface) */ +#if defined(VK_MVK_ios_surface) +PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK; +#endif /* defined(VK_MVK_ios_surface) */ +#if defined(VK_MVK_macos_surface) +PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; +#endif /* defined(VK_MVK_macos_surface) */ +#if defined(VK_NN_vi_surface) +PFN_vkCreateViSurfaceNN vkCreateViSurfaceNN; +#endif /* defined(VK_NN_vi_surface) */ +#if defined(VK_NVX_binary_import) +PFN_vkCmdCuLaunchKernelNVX vkCmdCuLaunchKernelNVX; +PFN_vkCreateCuFunctionNVX vkCreateCuFunctionNVX; +PFN_vkCreateCuModuleNVX vkCreateCuModuleNVX; +PFN_vkDestroyCuFunctionNVX vkDestroyCuFunctionNVX; +PFN_vkDestroyCuModuleNVX vkDestroyCuModuleNVX; +#endif /* defined(VK_NVX_binary_import) */ +#if defined(VK_NVX_image_view_handle) +PFN_vkGetImageViewAddressNVX vkGetImageViewAddressNVX; +PFN_vkGetImageViewHandleNVX vkGetImageViewHandleNVX; +#endif /* defined(VK_NVX_image_view_handle) */ +#if defined(VK_NV_acquire_winrt_display) +PFN_vkAcquireWinrtDisplayNV vkAcquireWinrtDisplayNV; +PFN_vkGetWinrtDisplayNV vkGetWinrtDisplayNV; +#endif /* defined(VK_NV_acquire_winrt_display) */ +#if defined(VK_NV_clip_space_w_scaling) +PFN_vkCmdSetViewportWScalingNV vkCmdSetViewportWScalingNV; +#endif /* defined(VK_NV_clip_space_w_scaling) */ +#if defined(VK_NV_cooperative_matrix) +PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV vkGetPhysicalDeviceCooperativeMatrixPropertiesNV; +#endif /* defined(VK_NV_cooperative_matrix) */ +#if defined(VK_NV_copy_memory_indirect) +PFN_vkCmdCopyMemoryIndirectNV vkCmdCopyMemoryIndirectNV; +PFN_vkCmdCopyMemoryToImageIndirectNV vkCmdCopyMemoryToImageIndirectNV; +#endif /* defined(VK_NV_copy_memory_indirect) */ +#if defined(VK_NV_coverage_reduction_mode) +PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV; +#endif /* defined(VK_NV_coverage_reduction_mode) */ +#if defined(VK_NV_cuda_kernel_launch) +PFN_vkCmdCudaLaunchKernelNV vkCmdCudaLaunchKernelNV; +PFN_vkCreateCudaFunctionNV vkCreateCudaFunctionNV; +PFN_vkCreateCudaModuleNV vkCreateCudaModuleNV; +PFN_vkDestroyCudaFunctionNV vkDestroyCudaFunctionNV; +PFN_vkDestroyCudaModuleNV vkDestroyCudaModuleNV; +PFN_vkGetCudaModuleCacheNV vkGetCudaModuleCacheNV; +#endif /* defined(VK_NV_cuda_kernel_launch) */ +#if defined(VK_NV_device_diagnostic_checkpoints) +PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; +PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; +#endif /* defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_NV_device_generated_commands) +PFN_vkCmdBindPipelineShaderGroupNV vkCmdBindPipelineShaderGroupNV; +PFN_vkCmdExecuteGeneratedCommandsNV vkCmdExecuteGeneratedCommandsNV; +PFN_vkCmdPreprocessGeneratedCommandsNV vkCmdPreprocessGeneratedCommandsNV; +PFN_vkCreateIndirectCommandsLayoutNV vkCreateIndirectCommandsLayoutNV; +PFN_vkDestroyIndirectCommandsLayoutNV vkDestroyIndirectCommandsLayoutNV; +PFN_vkGetGeneratedCommandsMemoryRequirementsNV vkGetGeneratedCommandsMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands) */ +#if defined(VK_NV_device_generated_commands_compute) +PFN_vkCmdUpdatePipelineIndirectBufferNV vkCmdUpdatePipelineIndirectBufferNV; +PFN_vkGetPipelineIndirectDeviceAddressNV vkGetPipelineIndirectDeviceAddressNV; +PFN_vkGetPipelineIndirectMemoryRequirementsNV vkGetPipelineIndirectMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands_compute) */ +#if defined(VK_NV_external_memory_capabilities) +PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV vkGetPhysicalDeviceExternalImageFormatPropertiesNV; +#endif /* defined(VK_NV_external_memory_capabilities) */ +#if defined(VK_NV_external_memory_rdma) +PFN_vkGetMemoryRemoteAddressNV vkGetMemoryRemoteAddressNV; +#endif /* defined(VK_NV_external_memory_rdma) */ +#if defined(VK_NV_external_memory_win32) +PFN_vkGetMemoryWin32HandleNV vkGetMemoryWin32HandleNV; +#endif /* defined(VK_NV_external_memory_win32) */ +#if defined(VK_NV_fragment_shading_rate_enums) +PFN_vkCmdSetFragmentShadingRateEnumNV vkCmdSetFragmentShadingRateEnumNV; +#endif /* defined(VK_NV_fragment_shading_rate_enums) */ +#if defined(VK_NV_low_latency2) +PFN_vkGetLatencyTimingsNV vkGetLatencyTimingsNV; +PFN_vkLatencySleepNV vkLatencySleepNV; +PFN_vkQueueNotifyOutOfBandNV vkQueueNotifyOutOfBandNV; +PFN_vkSetLatencyMarkerNV vkSetLatencyMarkerNV; +PFN_vkSetLatencySleepModeNV vkSetLatencySleepModeNV; +#endif /* defined(VK_NV_low_latency2) */ +#if defined(VK_NV_memory_decompression) +PFN_vkCmdDecompressMemoryIndirectCountNV vkCmdDecompressMemoryIndirectCountNV; +PFN_vkCmdDecompressMemoryNV vkCmdDecompressMemoryNV; +#endif /* defined(VK_NV_memory_decompression) */ +#if defined(VK_NV_mesh_shader) +PFN_vkCmdDrawMeshTasksIndirectCountNV vkCmdDrawMeshTasksIndirectCountNV; +PFN_vkCmdDrawMeshTasksIndirectNV vkCmdDrawMeshTasksIndirectNV; +PFN_vkCmdDrawMeshTasksNV vkCmdDrawMeshTasksNV; +#endif /* defined(VK_NV_mesh_shader) */ +#if defined(VK_NV_optical_flow) +PFN_vkBindOpticalFlowSessionImageNV vkBindOpticalFlowSessionImageNV; +PFN_vkCmdOpticalFlowExecuteNV vkCmdOpticalFlowExecuteNV; +PFN_vkCreateOpticalFlowSessionNV vkCreateOpticalFlowSessionNV; +PFN_vkDestroyOpticalFlowSessionNV vkDestroyOpticalFlowSessionNV; +PFN_vkGetPhysicalDeviceOpticalFlowImageFormatsNV vkGetPhysicalDeviceOpticalFlowImageFormatsNV; +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_NV_ray_tracing) +PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; +PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; +PFN_vkCmdCopyAccelerationStructureNV vkCmdCopyAccelerationStructureNV; +PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; +PFN_vkCmdWriteAccelerationStructuresPropertiesNV vkCmdWriteAccelerationStructuresPropertiesNV; +PFN_vkCompileDeferredNV vkCompileDeferredNV; +PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; +PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; +PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV; +PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; +PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; +PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; +#endif /* defined(VK_NV_ray_tracing) */ +#if defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 +PFN_vkCmdSetExclusiveScissorEnableNV vkCmdSetExclusiveScissorEnableNV; +#endif /* defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 */ +#if defined(VK_NV_scissor_exclusive) +PFN_vkCmdSetExclusiveScissorNV vkCmdSetExclusiveScissorNV; +#endif /* defined(VK_NV_scissor_exclusive) */ +#if defined(VK_NV_shading_rate_image) +PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV; +PFN_vkCmdSetCoarseSampleOrderNV vkCmdSetCoarseSampleOrderNV; +PFN_vkCmdSetViewportShadingRatePaletteNV vkCmdSetViewportShadingRatePaletteNV; +#endif /* defined(VK_NV_shading_rate_image) */ +#if defined(VK_QCOM_tile_properties) +PFN_vkGetDynamicRenderingTilePropertiesQCOM vkGetDynamicRenderingTilePropertiesQCOM; +PFN_vkGetFramebufferTilePropertiesQCOM vkGetFramebufferTilePropertiesQCOM; +#endif /* defined(VK_QCOM_tile_properties) */ +#if defined(VK_QNX_external_memory_screen_buffer) +PFN_vkGetScreenBufferPropertiesQNX vkGetScreenBufferPropertiesQNX; +#endif /* defined(VK_QNX_external_memory_screen_buffer) */ +#if defined(VK_QNX_screen_surface) +PFN_vkCreateScreenSurfaceQNX vkCreateScreenSurfaceQNX; +PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX vkGetPhysicalDeviceScreenPresentationSupportQNX; +#endif /* defined(VK_QNX_screen_surface) */ +#if defined(VK_VALVE_descriptor_set_host_mapping) +PFN_vkGetDescriptorSetHostMappingVALVE vkGetDescriptorSetHostMappingVALVE; +PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE vkGetDescriptorSetLayoutHostMappingInfoVALVE; +#endif /* defined(VK_VALVE_descriptor_set_host_mapping) */ +#if (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) +PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; +PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; +PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; +PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; +PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; +PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; +PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; +PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; +PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT; +PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; +PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; +PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) +PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT; +PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT; +PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT; +PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT; +PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) +PFN_vkCmdSetAlphaToCoverageEnableEXT vkCmdSetAlphaToCoverageEnableEXT; +PFN_vkCmdSetAlphaToOneEnableEXT vkCmdSetAlphaToOneEnableEXT; +PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT; +PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT; +PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT; +PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT; +PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT; +PFN_vkCmdSetPolygonModeEXT vkCmdSetPolygonModeEXT; +PFN_vkCmdSetRasterizationSamplesEXT vkCmdSetRasterizationSamplesEXT; +PFN_vkCmdSetSampleMaskEXT vkCmdSetSampleMaskEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) +PFN_vkCmdSetTessellationDomainOriginEXT vkCmdSetTessellationDomainOriginEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) +PFN_vkCmdSetRasterizationStreamEXT vkCmdSetRasterizationStreamEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) +PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT; +PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT vkCmdSetExtraPrimitiveOverestimationSizeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) +PFN_vkCmdSetDepthClipEnableEXT vkCmdSetDepthClipEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) +PFN_vkCmdSetSampleLocationsEnableEXT vkCmdSetSampleLocationsEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) +PFN_vkCmdSetColorBlendAdvancedEXT vkCmdSetColorBlendAdvancedEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) +PFN_vkCmdSetProvokingVertexModeEXT vkCmdSetProvokingVertexModeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) +PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT; +PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) +PFN_vkCmdSetDepthClipNegativeOneToOneEXT vkCmdSetDepthClipNegativeOneToOneEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) +PFN_vkCmdSetViewportWScalingEnableNV vkCmdSetViewportWScalingEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) +PFN_vkCmdSetViewportSwizzleNV vkCmdSetViewportSwizzleNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) +PFN_vkCmdSetCoverageToColorEnableNV vkCmdSetCoverageToColorEnableNV; +PFN_vkCmdSetCoverageToColorLocationNV vkCmdSetCoverageToColorLocationNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) +PFN_vkCmdSetCoverageModulationModeNV vkCmdSetCoverageModulationModeNV; +PFN_vkCmdSetCoverageModulationTableEnableNV vkCmdSetCoverageModulationTableEnableNV; +PFN_vkCmdSetCoverageModulationTableNV vkCmdSetCoverageModulationTableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) +PFN_vkCmdSetShadingRateImageEnableNV vkCmdSetShadingRateImageEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) +PFN_vkCmdSetRepresentativeFragmentTestEnableNV vkCmdSetRepresentativeFragmentTestEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) +PFN_vkCmdSetCoverageReductionModeNV vkCmdSetCoverageReductionModeNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) */ +#if (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) +PFN_vkGetImageSubresourceLayout2EXT vkGetImageSubresourceLayout2EXT; +#endif /* (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) */ +#if (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) +PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT; +#endif /* (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) */ +#if (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) +PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR; +#endif /* (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) +PFN_vkGetDeviceGroupPresentCapabilitiesKHR vkGetDeviceGroupPresentCapabilitiesKHR; +PFN_vkGetDeviceGroupSurfacePresentModesKHR vkGetDeviceGroupSurfacePresentModesKHR; +PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) +PFN_vkAcquireNextImage2KHR vkAcquireNextImage2KHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +/* VOLK_GENERATE_PROTOTYPES_C */ + +#ifdef __GNUC__ +# pragma GCC visibility pop +#endif + +#ifdef __cplusplus +} +#endif +/* clang-format on */ diff --git a/lib/volk.h b/lib/volk.h new file mode 100644 index 0000000..7e9edd4 --- /dev/null +++ b/lib/volk.h @@ -0,0 +1,2089 @@ +/** + * volk + * + * Copyright (C) 2018-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://github.com/zeux/volk + * + * This library is distributed under the MIT License. See notice at the end of this file. + */ +/* clang-format off */ +#ifndef VOLK_H_ +#define VOLK_H_ + +#if defined(VULKAN_H_) && !defined(VK_NO_PROTOTYPES) +# error To use volk, you need to define VK_NO_PROTOTYPES before including vulkan.h +#endif + +/* VOLK_GENERATE_VERSION_DEFINE */ +#define VOLK_HEADER_VERSION 295 +/* VOLK_GENERATE_VERSION_DEFINE */ + +#ifndef VK_NO_PROTOTYPES +# define VK_NO_PROTOTYPES +#endif + +#ifndef VULKAN_H_ +# ifdef VOLK_VULKAN_H_PATH +# include VOLK_VULKAN_H_PATH +# elif defined(VK_USE_PLATFORM_WIN32_KHR) +# include +# include + + /* When VK_USE_PLATFORM_WIN32_KHR is defined, instead of including vulkan.h directly, we include individual parts of the SDK + * This is necessary to avoid including which is very heavy - it takes 200ms to parse without WIN32_LEAN_AND_MEAN + * and 100ms to parse with it. vulkan_win32.h only needs a few symbols that are easy to redefine ourselves. + */ + typedef unsigned long DWORD; + typedef const wchar_t* LPCWSTR; + typedef void* HANDLE; + typedef struct HINSTANCE__* HINSTANCE; + typedef struct HWND__* HWND; + typedef struct HMONITOR__* HMONITOR; + typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES; + +# include + +# ifdef VK_ENABLE_BETA_EXTENSIONS +# include +# endif +# else +# include +# endif +#endif + +/* Disable several extensions on earlier SDKs because later SDKs introduce a backwards incompatible change to function signatures */ +#if VK_HEADER_VERSION < 140 +# undef VK_NVX_image_view_handle +#endif +#if VK_HEADER_VERSION < 184 +# undef VK_HUAWEI_subpass_shading +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct VolkDeviceTable; + +/** + * Initialize library by loading Vulkan loader; call this function before creating the Vulkan instance. + * + * Returns VK_SUCCESS on success and VK_ERROR_INITIALIZATION_FAILED otherwise. + */ +VkResult volkInitialize(void); + +/** + * Initialize library by providing a custom handler to load global symbols. + * + * This function can be used instead of volkInitialize. + * The handler function pointer will be asked to load global Vulkan symbols which require no instance + * (such as vkCreateInstance, vkEnumerateInstance* and vkEnumerateInstanceVersion if available). + */ +void volkInitializeCustom(PFN_vkGetInstanceProcAddr handler); + +/** + * Finalize library by unloading Vulkan loader and resetting global symbols to NULL. + * + * This function does not need to be called on process exit (as loader will be unloaded automatically) or if volkInitialize failed. + * In general this function is optional to call but may be useful in rare cases eg if volk needs to be reinitialized multiple times. + */ +void volkFinalize(void); + +/** + * Get Vulkan instance version supported by the Vulkan loader, or 0 if Vulkan isn't supported + * + * Returns 0 if volkInitialize wasn't called or failed. + */ +uint32_t volkGetInstanceVersion(void); + +/** + * Load global function pointers using application-created VkInstance; call this function after creating the Vulkan instance. + */ +void volkLoadInstance(VkInstance instance); + +/** + * Load global function pointers using application-created VkInstance; call this function after creating the Vulkan instance. + * Skips loading device-based function pointers, requires usage of volkLoadDevice afterwards. + */ +void volkLoadInstanceOnly(VkInstance instance); + +/** + * Load global function pointers using application-created VkDevice; call this function after creating the Vulkan device. + * + * Note: this is not suitable for applications that want to use multiple VkDevice objects concurrently. + */ +void volkLoadDevice(VkDevice device); + +/** + * Return last VkInstance for which global function pointers have been loaded via volkLoadInstance(), + * or VK_NULL_HANDLE if volkLoadInstance() has not been called. + */ +VkInstance volkGetLoadedInstance(void); + +/** + * Return last VkDevice for which global function pointers have been loaded via volkLoadDevice(), + * or VK_NULL_HANDLE if volkLoadDevice() has not been called. + */ +VkDevice volkGetLoadedDevice(void); + +/** + * Load function pointers using application-created VkDevice into a table. + * Application should use function pointers from that table instead of using global function pointers. + */ +void volkLoadDeviceTable(struct VolkDeviceTable* table, VkDevice device); + +/** + * Device-specific function pointer table + */ +struct VolkDeviceTable +{ + /* VOLK_GENERATE_DEVICE_TABLE */ +#if defined(VK_VERSION_1_0) + PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; + PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; + PFN_vkAllocateMemory vkAllocateMemory; + PFN_vkBeginCommandBuffer vkBeginCommandBuffer; + PFN_vkBindBufferMemory vkBindBufferMemory; + PFN_vkBindImageMemory vkBindImageMemory; + PFN_vkCmdBeginQuery vkCmdBeginQuery; + PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; + PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; + PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; + PFN_vkCmdBindPipeline vkCmdBindPipeline; + PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; + PFN_vkCmdBlitImage vkCmdBlitImage; + PFN_vkCmdClearAttachments vkCmdClearAttachments; + PFN_vkCmdClearColorImage vkCmdClearColorImage; + PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage; + PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; + PFN_vkCmdCopyImage vkCmdCopyImage; + PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; + PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults; + PFN_vkCmdDispatch vkCmdDispatch; + PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect; + PFN_vkCmdDraw vkCmdDraw; + PFN_vkCmdDrawIndexed vkCmdDrawIndexed; + PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect; + PFN_vkCmdDrawIndirect vkCmdDrawIndirect; + PFN_vkCmdEndQuery vkCmdEndQuery; + PFN_vkCmdEndRenderPass vkCmdEndRenderPass; + PFN_vkCmdExecuteCommands vkCmdExecuteCommands; + PFN_vkCmdFillBuffer vkCmdFillBuffer; + PFN_vkCmdNextSubpass vkCmdNextSubpass; + PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; + PFN_vkCmdPushConstants vkCmdPushConstants; + PFN_vkCmdResetEvent vkCmdResetEvent; + PFN_vkCmdResetQueryPool vkCmdResetQueryPool; + PFN_vkCmdResolveImage vkCmdResolveImage; + PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; + PFN_vkCmdSetDepthBias vkCmdSetDepthBias; + PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; + PFN_vkCmdSetEvent vkCmdSetEvent; + PFN_vkCmdSetLineWidth vkCmdSetLineWidth; + PFN_vkCmdSetScissor vkCmdSetScissor; + PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; + PFN_vkCmdSetStencilReference vkCmdSetStencilReference; + PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; + PFN_vkCmdSetViewport vkCmdSetViewport; + PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer; + PFN_vkCmdWaitEvents vkCmdWaitEvents; + PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp; + PFN_vkCreateBuffer vkCreateBuffer; + PFN_vkCreateBufferView vkCreateBufferView; + PFN_vkCreateCommandPool vkCreateCommandPool; + PFN_vkCreateComputePipelines vkCreateComputePipelines; + PFN_vkCreateDescriptorPool vkCreateDescriptorPool; + PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; + PFN_vkCreateEvent vkCreateEvent; + PFN_vkCreateFence vkCreateFence; + PFN_vkCreateFramebuffer vkCreateFramebuffer; + PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; + PFN_vkCreateImage vkCreateImage; + PFN_vkCreateImageView vkCreateImageView; + PFN_vkCreatePipelineCache vkCreatePipelineCache; + PFN_vkCreatePipelineLayout vkCreatePipelineLayout; + PFN_vkCreateQueryPool vkCreateQueryPool; + PFN_vkCreateRenderPass vkCreateRenderPass; + PFN_vkCreateSampler vkCreateSampler; + PFN_vkCreateSemaphore vkCreateSemaphore; + PFN_vkCreateShaderModule vkCreateShaderModule; + PFN_vkDestroyBuffer vkDestroyBuffer; + PFN_vkDestroyBufferView vkDestroyBufferView; + PFN_vkDestroyCommandPool vkDestroyCommandPool; + PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; + PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; + PFN_vkDestroyDevice vkDestroyDevice; + PFN_vkDestroyEvent vkDestroyEvent; + PFN_vkDestroyFence vkDestroyFence; + PFN_vkDestroyFramebuffer vkDestroyFramebuffer; + PFN_vkDestroyImage vkDestroyImage; + PFN_vkDestroyImageView vkDestroyImageView; + PFN_vkDestroyPipeline vkDestroyPipeline; + PFN_vkDestroyPipelineCache vkDestroyPipelineCache; + PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; + PFN_vkDestroyQueryPool vkDestroyQueryPool; + PFN_vkDestroyRenderPass vkDestroyRenderPass; + PFN_vkDestroySampler vkDestroySampler; + PFN_vkDestroySemaphore vkDestroySemaphore; + PFN_vkDestroyShaderModule vkDestroyShaderModule; + PFN_vkDeviceWaitIdle vkDeviceWaitIdle; + PFN_vkEndCommandBuffer vkEndCommandBuffer; + PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + PFN_vkFreeCommandBuffers vkFreeCommandBuffers; + PFN_vkFreeDescriptorSets vkFreeDescriptorSets; + PFN_vkFreeMemory vkFreeMemory; + PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment; + PFN_vkGetDeviceQueue vkGetDeviceQueue; + PFN_vkGetEventStatus vkGetEventStatus; + PFN_vkGetFenceStatus vkGetFenceStatus; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements; + PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout; + PFN_vkGetPipelineCacheData vkGetPipelineCacheData; + PFN_vkGetQueryPoolResults vkGetQueryPoolResults; + PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity; + PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + PFN_vkMapMemory vkMapMemory; + PFN_vkMergePipelineCaches vkMergePipelineCaches; + PFN_vkQueueBindSparse vkQueueBindSparse; + PFN_vkQueueSubmit vkQueueSubmit; + PFN_vkQueueWaitIdle vkQueueWaitIdle; + PFN_vkResetCommandBuffer vkResetCommandBuffer; + PFN_vkResetCommandPool vkResetCommandPool; + PFN_vkResetDescriptorPool vkResetDescriptorPool; + PFN_vkResetEvent vkResetEvent; + PFN_vkResetFences vkResetFences; + PFN_vkSetEvent vkSetEvent; + PFN_vkUnmapMemory vkUnmapMemory; + PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; + PFN_vkWaitForFences vkWaitForFences; +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) + PFN_vkBindBufferMemory2 vkBindBufferMemory2; + PFN_vkBindImageMemory2 vkBindImageMemory2; + PFN_vkCmdDispatchBase vkCmdDispatchBase; + PFN_vkCmdSetDeviceMask vkCmdSetDeviceMask; + PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate; + PFN_vkCreateSamplerYcbcrConversion vkCreateSamplerYcbcrConversion; + PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate; + PFN_vkDestroySamplerYcbcrConversion vkDestroySamplerYcbcrConversion; + PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; + PFN_vkGetDescriptorSetLayoutSupport vkGetDescriptorSetLayoutSupport; + PFN_vkGetDeviceGroupPeerMemoryFeatures vkGetDeviceGroupPeerMemoryFeatures; + PFN_vkGetDeviceQueue2 vkGetDeviceQueue2; + PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; + PFN_vkGetImageSparseMemoryRequirements2 vkGetImageSparseMemoryRequirements2; + PFN_vkTrimCommandPool vkTrimCommandPool; + PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate; +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_2) + PFN_vkCmdBeginRenderPass2 vkCmdBeginRenderPass2; + PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount; + PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount; + PFN_vkCmdEndRenderPass2 vkCmdEndRenderPass2; + PFN_vkCmdNextSubpass2 vkCmdNextSubpass2; + PFN_vkCreateRenderPass2 vkCreateRenderPass2; + PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress; + PFN_vkGetBufferOpaqueCaptureAddress vkGetBufferOpaqueCaptureAddress; + PFN_vkGetDeviceMemoryOpaqueCaptureAddress vkGetDeviceMemoryOpaqueCaptureAddress; + PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue; + PFN_vkResetQueryPool vkResetQueryPool; + PFN_vkSignalSemaphore vkSignalSemaphore; + PFN_vkWaitSemaphores vkWaitSemaphores; +#endif /* defined(VK_VERSION_1_2) */ +#if defined(VK_VERSION_1_3) + PFN_vkCmdBeginRendering vkCmdBeginRendering; + PFN_vkCmdBindVertexBuffers2 vkCmdBindVertexBuffers2; + PFN_vkCmdBlitImage2 vkCmdBlitImage2; + PFN_vkCmdCopyBuffer2 vkCmdCopyBuffer2; + PFN_vkCmdCopyBufferToImage2 vkCmdCopyBufferToImage2; + PFN_vkCmdCopyImage2 vkCmdCopyImage2; + PFN_vkCmdCopyImageToBuffer2 vkCmdCopyImageToBuffer2; + PFN_vkCmdEndRendering vkCmdEndRendering; + PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2; + PFN_vkCmdResetEvent2 vkCmdResetEvent2; + PFN_vkCmdResolveImage2 vkCmdResolveImage2; + PFN_vkCmdSetCullMode vkCmdSetCullMode; + PFN_vkCmdSetDepthBiasEnable vkCmdSetDepthBiasEnable; + PFN_vkCmdSetDepthBoundsTestEnable vkCmdSetDepthBoundsTestEnable; + PFN_vkCmdSetDepthCompareOp vkCmdSetDepthCompareOp; + PFN_vkCmdSetDepthTestEnable vkCmdSetDepthTestEnable; + PFN_vkCmdSetDepthWriteEnable vkCmdSetDepthWriteEnable; + PFN_vkCmdSetEvent2 vkCmdSetEvent2; + PFN_vkCmdSetFrontFace vkCmdSetFrontFace; + PFN_vkCmdSetPrimitiveRestartEnable vkCmdSetPrimitiveRestartEnable; + PFN_vkCmdSetPrimitiveTopology vkCmdSetPrimitiveTopology; + PFN_vkCmdSetRasterizerDiscardEnable vkCmdSetRasterizerDiscardEnable; + PFN_vkCmdSetScissorWithCount vkCmdSetScissorWithCount; + PFN_vkCmdSetStencilOp vkCmdSetStencilOp; + PFN_vkCmdSetStencilTestEnable vkCmdSetStencilTestEnable; + PFN_vkCmdSetViewportWithCount vkCmdSetViewportWithCount; + PFN_vkCmdWaitEvents2 vkCmdWaitEvents2; + PFN_vkCmdWriteTimestamp2 vkCmdWriteTimestamp2; + PFN_vkCreatePrivateDataSlot vkCreatePrivateDataSlot; + PFN_vkDestroyPrivateDataSlot vkDestroyPrivateDataSlot; + PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements; + PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements; + PFN_vkGetDeviceImageSparseMemoryRequirements vkGetDeviceImageSparseMemoryRequirements; + PFN_vkGetPrivateData vkGetPrivateData; + PFN_vkQueueSubmit2 vkQueueSubmit2; + PFN_vkSetPrivateData vkSetPrivateData; +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_AMDX_shader_enqueue) + PFN_vkCmdDispatchGraphAMDX vkCmdDispatchGraphAMDX; + PFN_vkCmdDispatchGraphIndirectAMDX vkCmdDispatchGraphIndirectAMDX; + PFN_vkCmdDispatchGraphIndirectCountAMDX vkCmdDispatchGraphIndirectCountAMDX; + PFN_vkCmdInitializeGraphScratchMemoryAMDX vkCmdInitializeGraphScratchMemoryAMDX; + PFN_vkCreateExecutionGraphPipelinesAMDX vkCreateExecutionGraphPipelinesAMDX; + PFN_vkGetExecutionGraphPipelineNodeIndexAMDX vkGetExecutionGraphPipelineNodeIndexAMDX; + PFN_vkGetExecutionGraphPipelineScratchSizeAMDX vkGetExecutionGraphPipelineScratchSizeAMDX; +#endif /* defined(VK_AMDX_shader_enqueue) */ +#if defined(VK_AMD_anti_lag) + PFN_vkAntiLagUpdateAMD vkAntiLagUpdateAMD; +#endif /* defined(VK_AMD_anti_lag) */ +#if defined(VK_AMD_buffer_marker) + PFN_vkCmdWriteBufferMarkerAMD vkCmdWriteBufferMarkerAMD; +#endif /* defined(VK_AMD_buffer_marker) */ +#if defined(VK_AMD_display_native_hdr) + PFN_vkSetLocalDimmingAMD vkSetLocalDimmingAMD; +#endif /* defined(VK_AMD_display_native_hdr) */ +#if defined(VK_AMD_draw_indirect_count) + PFN_vkCmdDrawIndexedIndirectCountAMD vkCmdDrawIndexedIndirectCountAMD; + PFN_vkCmdDrawIndirectCountAMD vkCmdDrawIndirectCountAMD; +#endif /* defined(VK_AMD_draw_indirect_count) */ +#if defined(VK_AMD_shader_info) + PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD; +#endif /* defined(VK_AMD_shader_info) */ +#if defined(VK_ANDROID_external_memory_android_hardware_buffer) + PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID; + PFN_vkGetMemoryAndroidHardwareBufferANDROID vkGetMemoryAndroidHardwareBufferANDROID; +#endif /* defined(VK_ANDROID_external_memory_android_hardware_buffer) */ +#if defined(VK_EXT_attachment_feedback_loop_dynamic_state) + PFN_vkCmdSetAttachmentFeedbackLoopEnableEXT vkCmdSetAttachmentFeedbackLoopEnableEXT; +#endif /* defined(VK_EXT_attachment_feedback_loop_dynamic_state) */ +#if defined(VK_EXT_buffer_device_address) + PFN_vkGetBufferDeviceAddressEXT vkGetBufferDeviceAddressEXT; +#endif /* defined(VK_EXT_buffer_device_address) */ +#if defined(VK_EXT_calibrated_timestamps) + PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT; +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_color_write_enable) + PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT; +#endif /* defined(VK_EXT_color_write_enable) */ +#if defined(VK_EXT_conditional_rendering) + PFN_vkCmdBeginConditionalRenderingEXT vkCmdBeginConditionalRenderingEXT; + PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT; +#endif /* defined(VK_EXT_conditional_rendering) */ +#if defined(VK_EXT_debug_marker) + PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBeginEXT; + PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEndEXT; + PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsertEXT; + PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectNameEXT; + PFN_vkDebugMarkerSetObjectTagEXT vkDebugMarkerSetObjectTagEXT; +#endif /* defined(VK_EXT_debug_marker) */ +#if defined(VK_EXT_depth_bias_control) + PFN_vkCmdSetDepthBias2EXT vkCmdSetDepthBias2EXT; +#endif /* defined(VK_EXT_depth_bias_control) */ +#if defined(VK_EXT_descriptor_buffer) + PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT vkCmdBindDescriptorBufferEmbeddedSamplersEXT; + PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffersEXT; + PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsetsEXT; + PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT vkGetBufferOpaqueCaptureDescriptorDataEXT; + PFN_vkGetDescriptorEXT vkGetDescriptorEXT; + PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffsetEXT; + PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT; + PFN_vkGetImageOpaqueCaptureDescriptorDataEXT vkGetImageOpaqueCaptureDescriptorDataEXT; + PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT vkGetImageViewOpaqueCaptureDescriptorDataEXT; + PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT vkGetSamplerOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) + PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) */ +#if defined(VK_EXT_device_fault) + PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT; +#endif /* defined(VK_EXT_device_fault) */ +#if defined(VK_EXT_discard_rectangles) + PFN_vkCmdSetDiscardRectangleEXT vkCmdSetDiscardRectangleEXT; +#endif /* defined(VK_EXT_discard_rectangles) */ +#if defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 + PFN_vkCmdSetDiscardRectangleEnableEXT vkCmdSetDiscardRectangleEnableEXT; + PFN_vkCmdSetDiscardRectangleModeEXT vkCmdSetDiscardRectangleModeEXT; +#endif /* defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 */ +#if defined(VK_EXT_display_control) + PFN_vkDisplayPowerControlEXT vkDisplayPowerControlEXT; + PFN_vkGetSwapchainCounterEXT vkGetSwapchainCounterEXT; + PFN_vkRegisterDeviceEventEXT vkRegisterDeviceEventEXT; + PFN_vkRegisterDisplayEventEXT vkRegisterDisplayEventEXT; +#endif /* defined(VK_EXT_display_control) */ +#if defined(VK_EXT_external_memory_host) + PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesEXT; +#endif /* defined(VK_EXT_external_memory_host) */ +#if defined(VK_EXT_full_screen_exclusive) + PFN_vkAcquireFullScreenExclusiveModeEXT vkAcquireFullScreenExclusiveModeEXT; + PFN_vkReleaseFullScreenExclusiveModeEXT vkReleaseFullScreenExclusiveModeEXT; +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) + PFN_vkGetDeviceGroupSurfacePresentModes2EXT vkGetDeviceGroupSurfacePresentModes2EXT; +#endif /* defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) */ +#if defined(VK_EXT_hdr_metadata) + PFN_vkSetHdrMetadataEXT vkSetHdrMetadataEXT; +#endif /* defined(VK_EXT_hdr_metadata) */ +#if defined(VK_EXT_host_image_copy) + PFN_vkCopyImageToImageEXT vkCopyImageToImageEXT; + PFN_vkCopyImageToMemoryEXT vkCopyImageToMemoryEXT; + PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImageEXT; + PFN_vkTransitionImageLayoutEXT vkTransitionImageLayoutEXT; +#endif /* defined(VK_EXT_host_image_copy) */ +#if defined(VK_EXT_host_query_reset) + PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; +#endif /* defined(VK_EXT_host_query_reset) */ +#if defined(VK_EXT_image_drm_format_modifier) + PFN_vkGetImageDrmFormatModifierPropertiesEXT vkGetImageDrmFormatModifierPropertiesEXT; +#endif /* defined(VK_EXT_image_drm_format_modifier) */ +#if defined(VK_EXT_line_rasterization) + PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT; +#endif /* defined(VK_EXT_line_rasterization) */ +#if defined(VK_EXT_mesh_shader) + PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXT; + PFN_vkCmdDrawMeshTasksIndirectCountEXT vkCmdDrawMeshTasksIndirectCountEXT; + PFN_vkCmdDrawMeshTasksIndirectEXT vkCmdDrawMeshTasksIndirectEXT; +#endif /* defined(VK_EXT_mesh_shader) */ +#if defined(VK_EXT_metal_objects) + PFN_vkExportMetalObjectsEXT vkExportMetalObjectsEXT; +#endif /* defined(VK_EXT_metal_objects) */ +#if defined(VK_EXT_multi_draw) + PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT; + PFN_vkCmdDrawMultiIndexedEXT vkCmdDrawMultiIndexedEXT; +#endif /* defined(VK_EXT_multi_draw) */ +#if defined(VK_EXT_opacity_micromap) + PFN_vkBuildMicromapsEXT vkBuildMicromapsEXT; + PFN_vkCmdBuildMicromapsEXT vkCmdBuildMicromapsEXT; + PFN_vkCmdCopyMemoryToMicromapEXT vkCmdCopyMemoryToMicromapEXT; + PFN_vkCmdCopyMicromapEXT vkCmdCopyMicromapEXT; + PFN_vkCmdCopyMicromapToMemoryEXT vkCmdCopyMicromapToMemoryEXT; + PFN_vkCmdWriteMicromapsPropertiesEXT vkCmdWriteMicromapsPropertiesEXT; + PFN_vkCopyMemoryToMicromapEXT vkCopyMemoryToMicromapEXT; + PFN_vkCopyMicromapEXT vkCopyMicromapEXT; + PFN_vkCopyMicromapToMemoryEXT vkCopyMicromapToMemoryEXT; + PFN_vkCreateMicromapEXT vkCreateMicromapEXT; + PFN_vkDestroyMicromapEXT vkDestroyMicromapEXT; + PFN_vkGetDeviceMicromapCompatibilityEXT vkGetDeviceMicromapCompatibilityEXT; + PFN_vkGetMicromapBuildSizesEXT vkGetMicromapBuildSizesEXT; + PFN_vkWriteMicromapsPropertiesEXT vkWriteMicromapsPropertiesEXT; +#endif /* defined(VK_EXT_opacity_micromap) */ +#if defined(VK_EXT_pageable_device_local_memory) + PFN_vkSetDeviceMemoryPriorityEXT vkSetDeviceMemoryPriorityEXT; +#endif /* defined(VK_EXT_pageable_device_local_memory) */ +#if defined(VK_EXT_pipeline_properties) + PFN_vkGetPipelinePropertiesEXT vkGetPipelinePropertiesEXT; +#endif /* defined(VK_EXT_pipeline_properties) */ +#if defined(VK_EXT_private_data) + PFN_vkCreatePrivateDataSlotEXT vkCreatePrivateDataSlotEXT; + PFN_vkDestroyPrivateDataSlotEXT vkDestroyPrivateDataSlotEXT; + PFN_vkGetPrivateDataEXT vkGetPrivateDataEXT; + PFN_vkSetPrivateDataEXT vkSetPrivateDataEXT; +#endif /* defined(VK_EXT_private_data) */ +#if defined(VK_EXT_sample_locations) + PFN_vkCmdSetSampleLocationsEXT vkCmdSetSampleLocationsEXT; +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_shader_module_identifier) + PFN_vkGetShaderModuleCreateInfoIdentifierEXT vkGetShaderModuleCreateInfoIdentifierEXT; + PFN_vkGetShaderModuleIdentifierEXT vkGetShaderModuleIdentifierEXT; +#endif /* defined(VK_EXT_shader_module_identifier) */ +#if defined(VK_EXT_shader_object) + PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT; + PFN_vkCreateShadersEXT vkCreateShadersEXT; + PFN_vkDestroyShaderEXT vkDestroyShaderEXT; + PFN_vkGetShaderBinaryDataEXT vkGetShaderBinaryDataEXT; +#endif /* defined(VK_EXT_shader_object) */ +#if defined(VK_EXT_swapchain_maintenance1) + PFN_vkReleaseSwapchainImagesEXT vkReleaseSwapchainImagesEXT; +#endif /* defined(VK_EXT_swapchain_maintenance1) */ +#if defined(VK_EXT_transform_feedback) + PFN_vkCmdBeginQueryIndexedEXT vkCmdBeginQueryIndexedEXT; + PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; + PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; + PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT; + PFN_vkCmdEndQueryIndexedEXT vkCmdEndQueryIndexedEXT; + PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; +#endif /* defined(VK_EXT_transform_feedback) */ +#if defined(VK_EXT_validation_cache) + PFN_vkCreateValidationCacheEXT vkCreateValidationCacheEXT; + PFN_vkDestroyValidationCacheEXT vkDestroyValidationCacheEXT; + PFN_vkGetValidationCacheDataEXT vkGetValidationCacheDataEXT; + PFN_vkMergeValidationCachesEXT vkMergeValidationCachesEXT; +#endif /* defined(VK_EXT_validation_cache) */ +#if defined(VK_FUCHSIA_buffer_collection) + PFN_vkCreateBufferCollectionFUCHSIA vkCreateBufferCollectionFUCHSIA; + PFN_vkDestroyBufferCollectionFUCHSIA vkDestroyBufferCollectionFUCHSIA; + PFN_vkGetBufferCollectionPropertiesFUCHSIA vkGetBufferCollectionPropertiesFUCHSIA; + PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA vkSetBufferCollectionBufferConstraintsFUCHSIA; + PFN_vkSetBufferCollectionImageConstraintsFUCHSIA vkSetBufferCollectionImageConstraintsFUCHSIA; +#endif /* defined(VK_FUCHSIA_buffer_collection) */ +#if defined(VK_FUCHSIA_external_memory) + PFN_vkGetMemoryZirconHandleFUCHSIA vkGetMemoryZirconHandleFUCHSIA; + PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA vkGetMemoryZirconHandlePropertiesFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_memory) */ +#if defined(VK_FUCHSIA_external_semaphore) + PFN_vkGetSemaphoreZirconHandleFUCHSIA vkGetSemaphoreZirconHandleFUCHSIA; + PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_semaphore) */ +#if defined(VK_GOOGLE_display_timing) + PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE; + PFN_vkGetRefreshCycleDurationGOOGLE vkGetRefreshCycleDurationGOOGLE; +#endif /* defined(VK_GOOGLE_display_timing) */ +#if defined(VK_HUAWEI_cluster_culling_shader) + PFN_vkCmdDrawClusterHUAWEI vkCmdDrawClusterHUAWEI; + PFN_vkCmdDrawClusterIndirectHUAWEI vkCmdDrawClusterIndirectHUAWEI; +#endif /* defined(VK_HUAWEI_cluster_culling_shader) */ +#if defined(VK_HUAWEI_invocation_mask) + PFN_vkCmdBindInvocationMaskHUAWEI vkCmdBindInvocationMaskHUAWEI; +#endif /* defined(VK_HUAWEI_invocation_mask) */ +#if defined(VK_HUAWEI_subpass_shading) + PFN_vkCmdSubpassShadingHUAWEI vkCmdSubpassShadingHUAWEI; + PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI; +#endif /* defined(VK_HUAWEI_subpass_shading) */ +#if defined(VK_INTEL_performance_query) + PFN_vkAcquirePerformanceConfigurationINTEL vkAcquirePerformanceConfigurationINTEL; + PFN_vkCmdSetPerformanceMarkerINTEL vkCmdSetPerformanceMarkerINTEL; + PFN_vkCmdSetPerformanceOverrideINTEL vkCmdSetPerformanceOverrideINTEL; + PFN_vkCmdSetPerformanceStreamMarkerINTEL vkCmdSetPerformanceStreamMarkerINTEL; + PFN_vkGetPerformanceParameterINTEL vkGetPerformanceParameterINTEL; + PFN_vkInitializePerformanceApiINTEL vkInitializePerformanceApiINTEL; + PFN_vkQueueSetPerformanceConfigurationINTEL vkQueueSetPerformanceConfigurationINTEL; + PFN_vkReleasePerformanceConfigurationINTEL vkReleasePerformanceConfigurationINTEL; + PFN_vkUninitializePerformanceApiINTEL vkUninitializePerformanceApiINTEL; +#endif /* defined(VK_INTEL_performance_query) */ +#if defined(VK_KHR_acceleration_structure) + PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR; + PFN_vkCmdBuildAccelerationStructuresIndirectKHR vkCmdBuildAccelerationStructuresIndirectKHR; + PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; + PFN_vkCmdCopyAccelerationStructureKHR vkCmdCopyAccelerationStructureKHR; + PFN_vkCmdCopyAccelerationStructureToMemoryKHR vkCmdCopyAccelerationStructureToMemoryKHR; + PFN_vkCmdCopyMemoryToAccelerationStructureKHR vkCmdCopyMemoryToAccelerationStructureKHR; + PFN_vkCmdWriteAccelerationStructuresPropertiesKHR vkCmdWriteAccelerationStructuresPropertiesKHR; + PFN_vkCopyAccelerationStructureKHR vkCopyAccelerationStructureKHR; + PFN_vkCopyAccelerationStructureToMemoryKHR vkCopyAccelerationStructureToMemoryKHR; + PFN_vkCopyMemoryToAccelerationStructureKHR vkCopyMemoryToAccelerationStructureKHR; + PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; + PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR; + PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; + PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; + PFN_vkGetDeviceAccelerationStructureCompatibilityKHR vkGetDeviceAccelerationStructureCompatibilityKHR; + PFN_vkWriteAccelerationStructuresPropertiesKHR vkWriteAccelerationStructuresPropertiesKHR; +#endif /* defined(VK_KHR_acceleration_structure) */ +#if defined(VK_KHR_bind_memory2) + PFN_vkBindBufferMemory2KHR vkBindBufferMemory2KHR; + PFN_vkBindImageMemory2KHR vkBindImageMemory2KHR; +#endif /* defined(VK_KHR_bind_memory2) */ +#if defined(VK_KHR_buffer_device_address) + PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; + PFN_vkGetBufferOpaqueCaptureAddressKHR vkGetBufferOpaqueCaptureAddressKHR; + PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR vkGetDeviceMemoryOpaqueCaptureAddressKHR; +#endif /* defined(VK_KHR_buffer_device_address) */ +#if defined(VK_KHR_calibrated_timestamps) + PFN_vkGetCalibratedTimestampsKHR vkGetCalibratedTimestampsKHR; +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_copy_commands2) + PFN_vkCmdBlitImage2KHR vkCmdBlitImage2KHR; + PFN_vkCmdCopyBuffer2KHR vkCmdCopyBuffer2KHR; + PFN_vkCmdCopyBufferToImage2KHR vkCmdCopyBufferToImage2KHR; + PFN_vkCmdCopyImage2KHR vkCmdCopyImage2KHR; + PFN_vkCmdCopyImageToBuffer2KHR vkCmdCopyImageToBuffer2KHR; + PFN_vkCmdResolveImage2KHR vkCmdResolveImage2KHR; +#endif /* defined(VK_KHR_copy_commands2) */ +#if defined(VK_KHR_create_renderpass2) + PFN_vkCmdBeginRenderPass2KHR vkCmdBeginRenderPass2KHR; + PFN_vkCmdEndRenderPass2KHR vkCmdEndRenderPass2KHR; + PFN_vkCmdNextSubpass2KHR vkCmdNextSubpass2KHR; + PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR; +#endif /* defined(VK_KHR_create_renderpass2) */ +#if defined(VK_KHR_deferred_host_operations) + PFN_vkCreateDeferredOperationKHR vkCreateDeferredOperationKHR; + PFN_vkDeferredOperationJoinKHR vkDeferredOperationJoinKHR; + PFN_vkDestroyDeferredOperationKHR vkDestroyDeferredOperationKHR; + PFN_vkGetDeferredOperationMaxConcurrencyKHR vkGetDeferredOperationMaxConcurrencyKHR; + PFN_vkGetDeferredOperationResultKHR vkGetDeferredOperationResultKHR; +#endif /* defined(VK_KHR_deferred_host_operations) */ +#if defined(VK_KHR_descriptor_update_template) + PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; + PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; + PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; +#endif /* defined(VK_KHR_descriptor_update_template) */ +#if defined(VK_KHR_device_group) + PFN_vkCmdDispatchBaseKHR vkCmdDispatchBaseKHR; + PFN_vkCmdSetDeviceMaskKHR vkCmdSetDeviceMaskKHR; + PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR vkGetDeviceGroupPeerMemoryFeaturesKHR; +#endif /* defined(VK_KHR_device_group) */ +#if defined(VK_KHR_display_swapchain) + PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR; +#endif /* defined(VK_KHR_display_swapchain) */ +#if defined(VK_KHR_draw_indirect_count) + PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR; + PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR; +#endif /* defined(VK_KHR_draw_indirect_count) */ +#if defined(VK_KHR_dynamic_rendering) + PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHR; + PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHR; +#endif /* defined(VK_KHR_dynamic_rendering) */ +#if defined(VK_KHR_dynamic_rendering_local_read) + PFN_vkCmdSetRenderingAttachmentLocationsKHR vkCmdSetRenderingAttachmentLocationsKHR; + PFN_vkCmdSetRenderingInputAttachmentIndicesKHR vkCmdSetRenderingInputAttachmentIndicesKHR; +#endif /* defined(VK_KHR_dynamic_rendering_local_read) */ +#if defined(VK_KHR_external_fence_fd) + PFN_vkGetFenceFdKHR vkGetFenceFdKHR; + PFN_vkImportFenceFdKHR vkImportFenceFdKHR; +#endif /* defined(VK_KHR_external_fence_fd) */ +#if defined(VK_KHR_external_fence_win32) + PFN_vkGetFenceWin32HandleKHR vkGetFenceWin32HandleKHR; + PFN_vkImportFenceWin32HandleKHR vkImportFenceWin32HandleKHR; +#endif /* defined(VK_KHR_external_fence_win32) */ +#if defined(VK_KHR_external_memory_fd) + PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR; + PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR; +#endif /* defined(VK_KHR_external_memory_fd) */ +#if defined(VK_KHR_external_memory_win32) + PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR; + PFN_vkGetMemoryWin32HandlePropertiesKHR vkGetMemoryWin32HandlePropertiesKHR; +#endif /* defined(VK_KHR_external_memory_win32) */ +#if defined(VK_KHR_external_semaphore_fd) + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; +#endif /* defined(VK_KHR_external_semaphore_fd) */ +#if defined(VK_KHR_external_semaphore_win32) + PFN_vkGetSemaphoreWin32HandleKHR vkGetSemaphoreWin32HandleKHR; + PFN_vkImportSemaphoreWin32HandleKHR vkImportSemaphoreWin32HandleKHR; +#endif /* defined(VK_KHR_external_semaphore_win32) */ +#if defined(VK_KHR_fragment_shading_rate) + PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR; +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_memory_requirements2) + PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; + PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; + PFN_vkGetImageSparseMemoryRequirements2KHR vkGetImageSparseMemoryRequirements2KHR; +#endif /* defined(VK_KHR_get_memory_requirements2) */ +#if defined(VK_KHR_line_rasterization) + PFN_vkCmdSetLineStippleKHR vkCmdSetLineStippleKHR; +#endif /* defined(VK_KHR_line_rasterization) */ +#if defined(VK_KHR_maintenance1) + PFN_vkTrimCommandPoolKHR vkTrimCommandPoolKHR; +#endif /* defined(VK_KHR_maintenance1) */ +#if defined(VK_KHR_maintenance3) + PFN_vkGetDescriptorSetLayoutSupportKHR vkGetDescriptorSetLayoutSupportKHR; +#endif /* defined(VK_KHR_maintenance3) */ +#if defined(VK_KHR_maintenance4) + PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirementsKHR; + PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirementsKHR; + PFN_vkGetDeviceImageSparseMemoryRequirementsKHR vkGetDeviceImageSparseMemoryRequirementsKHR; +#endif /* defined(VK_KHR_maintenance4) */ +#if defined(VK_KHR_maintenance5) + PFN_vkCmdBindIndexBuffer2KHR vkCmdBindIndexBuffer2KHR; + PFN_vkGetDeviceImageSubresourceLayoutKHR vkGetDeviceImageSubresourceLayoutKHR; + PFN_vkGetImageSubresourceLayout2KHR vkGetImageSubresourceLayout2KHR; + PFN_vkGetRenderingAreaGranularityKHR vkGetRenderingAreaGranularityKHR; +#endif /* defined(VK_KHR_maintenance5) */ +#if defined(VK_KHR_maintenance6) + PFN_vkCmdBindDescriptorSets2KHR vkCmdBindDescriptorSets2KHR; + PFN_vkCmdPushConstants2KHR vkCmdPushConstants2KHR; +#endif /* defined(VK_KHR_maintenance6) */ +#if defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) + PFN_vkCmdPushDescriptorSet2KHR vkCmdPushDescriptorSet2KHR; + PFN_vkCmdPushDescriptorSetWithTemplate2KHR vkCmdPushDescriptorSetWithTemplate2KHR; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) + PFN_vkCmdBindDescriptorBufferEmbeddedSamplers2EXT vkCmdBindDescriptorBufferEmbeddedSamplers2EXT; + PFN_vkCmdSetDescriptorBufferOffsets2EXT vkCmdSetDescriptorBufferOffsets2EXT; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_KHR_map_memory2) + PFN_vkMapMemory2KHR vkMapMemory2KHR; + PFN_vkUnmapMemory2KHR vkUnmapMemory2KHR; +#endif /* defined(VK_KHR_map_memory2) */ +#if defined(VK_KHR_performance_query) + PFN_vkAcquireProfilingLockKHR vkAcquireProfilingLockKHR; + PFN_vkReleaseProfilingLockKHR vkReleaseProfilingLockKHR; +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_pipeline_binary) + PFN_vkCreatePipelineBinariesKHR vkCreatePipelineBinariesKHR; + PFN_vkDestroyPipelineBinaryKHR vkDestroyPipelineBinaryKHR; + PFN_vkGetPipelineBinaryDataKHR vkGetPipelineBinaryDataKHR; + PFN_vkGetPipelineKeyKHR vkGetPipelineKeyKHR; + PFN_vkReleaseCapturedPipelineDataKHR vkReleaseCapturedPipelineDataKHR; +#endif /* defined(VK_KHR_pipeline_binary) */ +#if defined(VK_KHR_pipeline_executable_properties) + PFN_vkGetPipelineExecutableInternalRepresentationsKHR vkGetPipelineExecutableInternalRepresentationsKHR; + PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR; + PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR; +#endif /* defined(VK_KHR_pipeline_executable_properties) */ +#if defined(VK_KHR_present_wait) + PFN_vkWaitForPresentKHR vkWaitForPresentKHR; +#endif /* defined(VK_KHR_present_wait) */ +#if defined(VK_KHR_push_descriptor) + PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR; +#endif /* defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) + PFN_vkCmdTraceRaysIndirect2KHR vkCmdTraceRaysIndirect2KHR; +#endif /* defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_ray_tracing_pipeline) + PFN_vkCmdSetRayTracingPipelineStackSizeKHR vkCmdSetRayTracingPipelineStackSizeKHR; + PFN_vkCmdTraceRaysIndirectKHR vkCmdTraceRaysIndirectKHR; + PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; + PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; + PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR vkGetRayTracingCaptureReplayShaderGroupHandlesKHR; + PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; + PFN_vkGetRayTracingShaderGroupStackSizeKHR vkGetRayTracingShaderGroupStackSizeKHR; +#endif /* defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_sampler_ycbcr_conversion) + PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; + PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; +#endif /* defined(VK_KHR_sampler_ycbcr_conversion) */ +#if defined(VK_KHR_shared_presentable_image) + PFN_vkGetSwapchainStatusKHR vkGetSwapchainStatusKHR; +#endif /* defined(VK_KHR_shared_presentable_image) */ +#if defined(VK_KHR_swapchain) + PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; + PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; + PFN_vkQueuePresentKHR vkQueuePresentKHR; +#endif /* defined(VK_KHR_swapchain) */ +#if defined(VK_KHR_synchronization2) + PFN_vkCmdPipelineBarrier2KHR vkCmdPipelineBarrier2KHR; + PFN_vkCmdResetEvent2KHR vkCmdResetEvent2KHR; + PFN_vkCmdSetEvent2KHR vkCmdSetEvent2KHR; + PFN_vkCmdWaitEvents2KHR vkCmdWaitEvents2KHR; + PFN_vkCmdWriteTimestamp2KHR vkCmdWriteTimestamp2KHR; + PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; +#endif /* defined(VK_KHR_synchronization2) */ +#if defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) + PFN_vkCmdWriteBufferMarker2AMD vkCmdWriteBufferMarker2AMD; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) */ +#if defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) + PFN_vkGetQueueCheckpointData2NV vkGetQueueCheckpointData2NV; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_KHR_timeline_semaphore) + PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; + PFN_vkSignalSemaphoreKHR vkSignalSemaphoreKHR; + PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; +#endif /* defined(VK_KHR_timeline_semaphore) */ +#if defined(VK_KHR_video_decode_queue) + PFN_vkCmdDecodeVideoKHR vkCmdDecodeVideoKHR; +#endif /* defined(VK_KHR_video_decode_queue) */ +#if defined(VK_KHR_video_encode_queue) + PFN_vkCmdEncodeVideoKHR vkCmdEncodeVideoKHR; + PFN_vkGetEncodedVideoSessionParametersKHR vkGetEncodedVideoSessionParametersKHR; +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) + PFN_vkBindVideoSessionMemoryKHR vkBindVideoSessionMemoryKHR; + PFN_vkCmdBeginVideoCodingKHR vkCmdBeginVideoCodingKHR; + PFN_vkCmdControlVideoCodingKHR vkCmdControlVideoCodingKHR; + PFN_vkCmdEndVideoCodingKHR vkCmdEndVideoCodingKHR; + PFN_vkCreateVideoSessionKHR vkCreateVideoSessionKHR; + PFN_vkCreateVideoSessionParametersKHR vkCreateVideoSessionParametersKHR; + PFN_vkDestroyVideoSessionKHR vkDestroyVideoSessionKHR; + PFN_vkDestroyVideoSessionParametersKHR vkDestroyVideoSessionParametersKHR; + PFN_vkGetVideoSessionMemoryRequirementsKHR vkGetVideoSessionMemoryRequirementsKHR; + PFN_vkUpdateVideoSessionParametersKHR vkUpdateVideoSessionParametersKHR; +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_NVX_binary_import) + PFN_vkCmdCuLaunchKernelNVX vkCmdCuLaunchKernelNVX; + PFN_vkCreateCuFunctionNVX vkCreateCuFunctionNVX; + PFN_vkCreateCuModuleNVX vkCreateCuModuleNVX; + PFN_vkDestroyCuFunctionNVX vkDestroyCuFunctionNVX; + PFN_vkDestroyCuModuleNVX vkDestroyCuModuleNVX; +#endif /* defined(VK_NVX_binary_import) */ +#if defined(VK_NVX_image_view_handle) + PFN_vkGetImageViewAddressNVX vkGetImageViewAddressNVX; + PFN_vkGetImageViewHandleNVX vkGetImageViewHandleNVX; +#endif /* defined(VK_NVX_image_view_handle) */ +#if defined(VK_NV_clip_space_w_scaling) + PFN_vkCmdSetViewportWScalingNV vkCmdSetViewportWScalingNV; +#endif /* defined(VK_NV_clip_space_w_scaling) */ +#if defined(VK_NV_copy_memory_indirect) + PFN_vkCmdCopyMemoryIndirectNV vkCmdCopyMemoryIndirectNV; + PFN_vkCmdCopyMemoryToImageIndirectNV vkCmdCopyMemoryToImageIndirectNV; +#endif /* defined(VK_NV_copy_memory_indirect) */ +#if defined(VK_NV_cuda_kernel_launch) + PFN_vkCmdCudaLaunchKernelNV vkCmdCudaLaunchKernelNV; + PFN_vkCreateCudaFunctionNV vkCreateCudaFunctionNV; + PFN_vkCreateCudaModuleNV vkCreateCudaModuleNV; + PFN_vkDestroyCudaFunctionNV vkDestroyCudaFunctionNV; + PFN_vkDestroyCudaModuleNV vkDestroyCudaModuleNV; + PFN_vkGetCudaModuleCacheNV vkGetCudaModuleCacheNV; +#endif /* defined(VK_NV_cuda_kernel_launch) */ +#if defined(VK_NV_device_diagnostic_checkpoints) + PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; + PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; +#endif /* defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_NV_device_generated_commands) + PFN_vkCmdBindPipelineShaderGroupNV vkCmdBindPipelineShaderGroupNV; + PFN_vkCmdExecuteGeneratedCommandsNV vkCmdExecuteGeneratedCommandsNV; + PFN_vkCmdPreprocessGeneratedCommandsNV vkCmdPreprocessGeneratedCommandsNV; + PFN_vkCreateIndirectCommandsLayoutNV vkCreateIndirectCommandsLayoutNV; + PFN_vkDestroyIndirectCommandsLayoutNV vkDestroyIndirectCommandsLayoutNV; + PFN_vkGetGeneratedCommandsMemoryRequirementsNV vkGetGeneratedCommandsMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands) */ +#if defined(VK_NV_device_generated_commands_compute) + PFN_vkCmdUpdatePipelineIndirectBufferNV vkCmdUpdatePipelineIndirectBufferNV; + PFN_vkGetPipelineIndirectDeviceAddressNV vkGetPipelineIndirectDeviceAddressNV; + PFN_vkGetPipelineIndirectMemoryRequirementsNV vkGetPipelineIndirectMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands_compute) */ +#if defined(VK_NV_external_memory_rdma) + PFN_vkGetMemoryRemoteAddressNV vkGetMemoryRemoteAddressNV; +#endif /* defined(VK_NV_external_memory_rdma) */ +#if defined(VK_NV_external_memory_win32) + PFN_vkGetMemoryWin32HandleNV vkGetMemoryWin32HandleNV; +#endif /* defined(VK_NV_external_memory_win32) */ +#if defined(VK_NV_fragment_shading_rate_enums) + PFN_vkCmdSetFragmentShadingRateEnumNV vkCmdSetFragmentShadingRateEnumNV; +#endif /* defined(VK_NV_fragment_shading_rate_enums) */ +#if defined(VK_NV_low_latency2) + PFN_vkGetLatencyTimingsNV vkGetLatencyTimingsNV; + PFN_vkLatencySleepNV vkLatencySleepNV; + PFN_vkQueueNotifyOutOfBandNV vkQueueNotifyOutOfBandNV; + PFN_vkSetLatencyMarkerNV vkSetLatencyMarkerNV; + PFN_vkSetLatencySleepModeNV vkSetLatencySleepModeNV; +#endif /* defined(VK_NV_low_latency2) */ +#if defined(VK_NV_memory_decompression) + PFN_vkCmdDecompressMemoryIndirectCountNV vkCmdDecompressMemoryIndirectCountNV; + PFN_vkCmdDecompressMemoryNV vkCmdDecompressMemoryNV; +#endif /* defined(VK_NV_memory_decompression) */ +#if defined(VK_NV_mesh_shader) + PFN_vkCmdDrawMeshTasksIndirectCountNV vkCmdDrawMeshTasksIndirectCountNV; + PFN_vkCmdDrawMeshTasksIndirectNV vkCmdDrawMeshTasksIndirectNV; + PFN_vkCmdDrawMeshTasksNV vkCmdDrawMeshTasksNV; +#endif /* defined(VK_NV_mesh_shader) */ +#if defined(VK_NV_optical_flow) + PFN_vkBindOpticalFlowSessionImageNV vkBindOpticalFlowSessionImageNV; + PFN_vkCmdOpticalFlowExecuteNV vkCmdOpticalFlowExecuteNV; + PFN_vkCreateOpticalFlowSessionNV vkCreateOpticalFlowSessionNV; + PFN_vkDestroyOpticalFlowSessionNV vkDestroyOpticalFlowSessionNV; +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_NV_ray_tracing) + PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; + PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; + PFN_vkCmdCopyAccelerationStructureNV vkCmdCopyAccelerationStructureNV; + PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; + PFN_vkCmdWriteAccelerationStructuresPropertiesNV vkCmdWriteAccelerationStructuresPropertiesNV; + PFN_vkCompileDeferredNV vkCompileDeferredNV; + PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; + PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; + PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV; + PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; + PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; + PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; +#endif /* defined(VK_NV_ray_tracing) */ +#if defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 + PFN_vkCmdSetExclusiveScissorEnableNV vkCmdSetExclusiveScissorEnableNV; +#endif /* defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 */ +#if defined(VK_NV_scissor_exclusive) + PFN_vkCmdSetExclusiveScissorNV vkCmdSetExclusiveScissorNV; +#endif /* defined(VK_NV_scissor_exclusive) */ +#if defined(VK_NV_shading_rate_image) + PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV; + PFN_vkCmdSetCoarseSampleOrderNV vkCmdSetCoarseSampleOrderNV; + PFN_vkCmdSetViewportShadingRatePaletteNV vkCmdSetViewportShadingRatePaletteNV; +#endif /* defined(VK_NV_shading_rate_image) */ +#if defined(VK_QCOM_tile_properties) + PFN_vkGetDynamicRenderingTilePropertiesQCOM vkGetDynamicRenderingTilePropertiesQCOM; + PFN_vkGetFramebufferTilePropertiesQCOM vkGetFramebufferTilePropertiesQCOM; +#endif /* defined(VK_QCOM_tile_properties) */ +#if defined(VK_QNX_external_memory_screen_buffer) + PFN_vkGetScreenBufferPropertiesQNX vkGetScreenBufferPropertiesQNX; +#endif /* defined(VK_QNX_external_memory_screen_buffer) */ +#if defined(VK_VALVE_descriptor_set_host_mapping) + PFN_vkGetDescriptorSetHostMappingVALVE vkGetDescriptorSetHostMappingVALVE; + PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE vkGetDescriptorSetLayoutHostMappingInfoVALVE; +#endif /* defined(VK_VALVE_descriptor_set_host_mapping) */ +#if (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) + PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; + PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; + PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; + PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; + PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; + PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; + PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; + PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; + PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT; + PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; + PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; + PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) + PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT; + PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT; + PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT; + PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT; + PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) + PFN_vkCmdSetAlphaToCoverageEnableEXT vkCmdSetAlphaToCoverageEnableEXT; + PFN_vkCmdSetAlphaToOneEnableEXT vkCmdSetAlphaToOneEnableEXT; + PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT; + PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT; + PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT; + PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT; + PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT; + PFN_vkCmdSetPolygonModeEXT vkCmdSetPolygonModeEXT; + PFN_vkCmdSetRasterizationSamplesEXT vkCmdSetRasterizationSamplesEXT; + PFN_vkCmdSetSampleMaskEXT vkCmdSetSampleMaskEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) + PFN_vkCmdSetTessellationDomainOriginEXT vkCmdSetTessellationDomainOriginEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) + PFN_vkCmdSetRasterizationStreamEXT vkCmdSetRasterizationStreamEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) + PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT; + PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT vkCmdSetExtraPrimitiveOverestimationSizeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) + PFN_vkCmdSetDepthClipEnableEXT vkCmdSetDepthClipEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) + PFN_vkCmdSetSampleLocationsEnableEXT vkCmdSetSampleLocationsEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) + PFN_vkCmdSetColorBlendAdvancedEXT vkCmdSetColorBlendAdvancedEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) + PFN_vkCmdSetProvokingVertexModeEXT vkCmdSetProvokingVertexModeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) + PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT; + PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) + PFN_vkCmdSetDepthClipNegativeOneToOneEXT vkCmdSetDepthClipNegativeOneToOneEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) + PFN_vkCmdSetViewportWScalingEnableNV vkCmdSetViewportWScalingEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) + PFN_vkCmdSetViewportSwizzleNV vkCmdSetViewportSwizzleNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) + PFN_vkCmdSetCoverageToColorEnableNV vkCmdSetCoverageToColorEnableNV; + PFN_vkCmdSetCoverageToColorLocationNV vkCmdSetCoverageToColorLocationNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) + PFN_vkCmdSetCoverageModulationModeNV vkCmdSetCoverageModulationModeNV; + PFN_vkCmdSetCoverageModulationTableEnableNV vkCmdSetCoverageModulationTableEnableNV; + PFN_vkCmdSetCoverageModulationTableNV vkCmdSetCoverageModulationTableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) + PFN_vkCmdSetShadingRateImageEnableNV vkCmdSetShadingRateImageEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) + PFN_vkCmdSetRepresentativeFragmentTestEnableNV vkCmdSetRepresentativeFragmentTestEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) + PFN_vkCmdSetCoverageReductionModeNV vkCmdSetCoverageReductionModeNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) */ +#if (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) + PFN_vkGetImageSubresourceLayout2EXT vkGetImageSubresourceLayout2EXT; +#endif /* (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) */ +#if (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) + PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT; +#endif /* (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) */ +#if (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) + PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR; +#endif /* (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + PFN_vkGetDeviceGroupPresentCapabilitiesKHR vkGetDeviceGroupPresentCapabilitiesKHR; + PFN_vkGetDeviceGroupSurfacePresentModesKHR vkGetDeviceGroupSurfacePresentModesKHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) + PFN_vkAcquireNextImage2KHR vkAcquireNextImage2KHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ + /* VOLK_GENERATE_DEVICE_TABLE */ +}; + +/* VOLK_GENERATE_PROTOTYPES_H */ +#if defined(VK_VERSION_1_0) +extern PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; +extern PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; +extern PFN_vkAllocateMemory vkAllocateMemory; +extern PFN_vkBeginCommandBuffer vkBeginCommandBuffer; +extern PFN_vkBindBufferMemory vkBindBufferMemory; +extern PFN_vkBindImageMemory vkBindImageMemory; +extern PFN_vkCmdBeginQuery vkCmdBeginQuery; +extern PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; +extern PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; +extern PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; +extern PFN_vkCmdBindPipeline vkCmdBindPipeline; +extern PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; +extern PFN_vkCmdBlitImage vkCmdBlitImage; +extern PFN_vkCmdClearAttachments vkCmdClearAttachments; +extern PFN_vkCmdClearColorImage vkCmdClearColorImage; +extern PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage; +extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer; +extern PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; +extern PFN_vkCmdCopyImage vkCmdCopyImage; +extern PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; +extern PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults; +extern PFN_vkCmdDispatch vkCmdDispatch; +extern PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect; +extern PFN_vkCmdDraw vkCmdDraw; +extern PFN_vkCmdDrawIndexed vkCmdDrawIndexed; +extern PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect; +extern PFN_vkCmdDrawIndirect vkCmdDrawIndirect; +extern PFN_vkCmdEndQuery vkCmdEndQuery; +extern PFN_vkCmdEndRenderPass vkCmdEndRenderPass; +extern PFN_vkCmdExecuteCommands vkCmdExecuteCommands; +extern PFN_vkCmdFillBuffer vkCmdFillBuffer; +extern PFN_vkCmdNextSubpass vkCmdNextSubpass; +extern PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; +extern PFN_vkCmdPushConstants vkCmdPushConstants; +extern PFN_vkCmdResetEvent vkCmdResetEvent; +extern PFN_vkCmdResetQueryPool vkCmdResetQueryPool; +extern PFN_vkCmdResolveImage vkCmdResolveImage; +extern PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; +extern PFN_vkCmdSetDepthBias vkCmdSetDepthBias; +extern PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; +extern PFN_vkCmdSetEvent vkCmdSetEvent; +extern PFN_vkCmdSetLineWidth vkCmdSetLineWidth; +extern PFN_vkCmdSetScissor vkCmdSetScissor; +extern PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; +extern PFN_vkCmdSetStencilReference vkCmdSetStencilReference; +extern PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; +extern PFN_vkCmdSetViewport vkCmdSetViewport; +extern PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer; +extern PFN_vkCmdWaitEvents vkCmdWaitEvents; +extern PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp; +extern PFN_vkCreateBuffer vkCreateBuffer; +extern PFN_vkCreateBufferView vkCreateBufferView; +extern PFN_vkCreateCommandPool vkCreateCommandPool; +extern PFN_vkCreateComputePipelines vkCreateComputePipelines; +extern PFN_vkCreateDescriptorPool vkCreateDescriptorPool; +extern PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; +extern PFN_vkCreateDevice vkCreateDevice; +extern PFN_vkCreateEvent vkCreateEvent; +extern PFN_vkCreateFence vkCreateFence; +extern PFN_vkCreateFramebuffer vkCreateFramebuffer; +extern PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; +extern PFN_vkCreateImage vkCreateImage; +extern PFN_vkCreateImageView vkCreateImageView; +extern PFN_vkCreateInstance vkCreateInstance; +extern PFN_vkCreatePipelineCache vkCreatePipelineCache; +extern PFN_vkCreatePipelineLayout vkCreatePipelineLayout; +extern PFN_vkCreateQueryPool vkCreateQueryPool; +extern PFN_vkCreateRenderPass vkCreateRenderPass; +extern PFN_vkCreateSampler vkCreateSampler; +extern PFN_vkCreateSemaphore vkCreateSemaphore; +extern PFN_vkCreateShaderModule vkCreateShaderModule; +extern PFN_vkDestroyBuffer vkDestroyBuffer; +extern PFN_vkDestroyBufferView vkDestroyBufferView; +extern PFN_vkDestroyCommandPool vkDestroyCommandPool; +extern PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; +extern PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; +extern PFN_vkDestroyDevice vkDestroyDevice; +extern PFN_vkDestroyEvent vkDestroyEvent; +extern PFN_vkDestroyFence vkDestroyFence; +extern PFN_vkDestroyFramebuffer vkDestroyFramebuffer; +extern PFN_vkDestroyImage vkDestroyImage; +extern PFN_vkDestroyImageView vkDestroyImageView; +extern PFN_vkDestroyInstance vkDestroyInstance; +extern PFN_vkDestroyPipeline vkDestroyPipeline; +extern PFN_vkDestroyPipelineCache vkDestroyPipelineCache; +extern PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; +extern PFN_vkDestroyQueryPool vkDestroyQueryPool; +extern PFN_vkDestroyRenderPass vkDestroyRenderPass; +extern PFN_vkDestroySampler vkDestroySampler; +extern PFN_vkDestroySemaphore vkDestroySemaphore; +extern PFN_vkDestroyShaderModule vkDestroyShaderModule; +extern PFN_vkDeviceWaitIdle vkDeviceWaitIdle; +extern PFN_vkEndCommandBuffer vkEndCommandBuffer; +extern PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; +extern PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties; +extern PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; +extern PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; +extern PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; +extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; +extern PFN_vkFreeCommandBuffers vkFreeCommandBuffers; +extern PFN_vkFreeDescriptorSets vkFreeDescriptorSets; +extern PFN_vkFreeMemory vkFreeMemory; +extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; +extern PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment; +extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; +extern PFN_vkGetDeviceQueue vkGetDeviceQueue; +extern PFN_vkGetEventStatus vkGetEventStatus; +extern PFN_vkGetFenceStatus vkGetFenceStatus; +extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; +extern PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements; +extern PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout; +extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; +extern PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures; +extern PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; +extern PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties; +extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; +extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; +extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; +extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties; +extern PFN_vkGetPipelineCacheData vkGetPipelineCacheData; +extern PFN_vkGetQueryPoolResults vkGetQueryPoolResults; +extern PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity; +extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; +extern PFN_vkMapMemory vkMapMemory; +extern PFN_vkMergePipelineCaches vkMergePipelineCaches; +extern PFN_vkQueueBindSparse vkQueueBindSparse; +extern PFN_vkQueueSubmit vkQueueSubmit; +extern PFN_vkQueueWaitIdle vkQueueWaitIdle; +extern PFN_vkResetCommandBuffer vkResetCommandBuffer; +extern PFN_vkResetCommandPool vkResetCommandPool; +extern PFN_vkResetDescriptorPool vkResetDescriptorPool; +extern PFN_vkResetEvent vkResetEvent; +extern PFN_vkResetFences vkResetFences; +extern PFN_vkSetEvent vkSetEvent; +extern PFN_vkUnmapMemory vkUnmapMemory; +extern PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; +extern PFN_vkWaitForFences vkWaitForFences; +#endif /* defined(VK_VERSION_1_0) */ +#if defined(VK_VERSION_1_1) +extern PFN_vkBindBufferMemory2 vkBindBufferMemory2; +extern PFN_vkBindImageMemory2 vkBindImageMemory2; +extern PFN_vkCmdDispatchBase vkCmdDispatchBase; +extern PFN_vkCmdSetDeviceMask vkCmdSetDeviceMask; +extern PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate; +extern PFN_vkCreateSamplerYcbcrConversion vkCreateSamplerYcbcrConversion; +extern PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate; +extern PFN_vkDestroySamplerYcbcrConversion vkDestroySamplerYcbcrConversion; +extern PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion; +extern PFN_vkEnumeratePhysicalDeviceGroups vkEnumeratePhysicalDeviceGroups; +extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; +extern PFN_vkGetDescriptorSetLayoutSupport vkGetDescriptorSetLayoutSupport; +extern PFN_vkGetDeviceGroupPeerMemoryFeatures vkGetDeviceGroupPeerMemoryFeatures; +extern PFN_vkGetDeviceQueue2 vkGetDeviceQueue2; +extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; +extern PFN_vkGetImageSparseMemoryRequirements2 vkGetImageSparseMemoryRequirements2; +extern PFN_vkGetPhysicalDeviceExternalBufferProperties vkGetPhysicalDeviceExternalBufferProperties; +extern PFN_vkGetPhysicalDeviceExternalFenceProperties vkGetPhysicalDeviceExternalFenceProperties; +extern PFN_vkGetPhysicalDeviceExternalSemaphoreProperties vkGetPhysicalDeviceExternalSemaphoreProperties; +extern PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2; +extern PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2; +extern PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2; +extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; +extern PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2; +extern PFN_vkGetPhysicalDeviceQueueFamilyProperties2 vkGetPhysicalDeviceQueueFamilyProperties2; +extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 vkGetPhysicalDeviceSparseImageFormatProperties2; +extern PFN_vkTrimCommandPool vkTrimCommandPool; +extern PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate; +#endif /* defined(VK_VERSION_1_1) */ +#if defined(VK_VERSION_1_2) +extern PFN_vkCmdBeginRenderPass2 vkCmdBeginRenderPass2; +extern PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount; +extern PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount; +extern PFN_vkCmdEndRenderPass2 vkCmdEndRenderPass2; +extern PFN_vkCmdNextSubpass2 vkCmdNextSubpass2; +extern PFN_vkCreateRenderPass2 vkCreateRenderPass2; +extern PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress; +extern PFN_vkGetBufferOpaqueCaptureAddress vkGetBufferOpaqueCaptureAddress; +extern PFN_vkGetDeviceMemoryOpaqueCaptureAddress vkGetDeviceMemoryOpaqueCaptureAddress; +extern PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue; +extern PFN_vkResetQueryPool vkResetQueryPool; +extern PFN_vkSignalSemaphore vkSignalSemaphore; +extern PFN_vkWaitSemaphores vkWaitSemaphores; +#endif /* defined(VK_VERSION_1_2) */ +#if defined(VK_VERSION_1_3) +extern PFN_vkCmdBeginRendering vkCmdBeginRendering; +extern PFN_vkCmdBindVertexBuffers2 vkCmdBindVertexBuffers2; +extern PFN_vkCmdBlitImage2 vkCmdBlitImage2; +extern PFN_vkCmdCopyBuffer2 vkCmdCopyBuffer2; +extern PFN_vkCmdCopyBufferToImage2 vkCmdCopyBufferToImage2; +extern PFN_vkCmdCopyImage2 vkCmdCopyImage2; +extern PFN_vkCmdCopyImageToBuffer2 vkCmdCopyImageToBuffer2; +extern PFN_vkCmdEndRendering vkCmdEndRendering; +extern PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2; +extern PFN_vkCmdResetEvent2 vkCmdResetEvent2; +extern PFN_vkCmdResolveImage2 vkCmdResolveImage2; +extern PFN_vkCmdSetCullMode vkCmdSetCullMode; +extern PFN_vkCmdSetDepthBiasEnable vkCmdSetDepthBiasEnable; +extern PFN_vkCmdSetDepthBoundsTestEnable vkCmdSetDepthBoundsTestEnable; +extern PFN_vkCmdSetDepthCompareOp vkCmdSetDepthCompareOp; +extern PFN_vkCmdSetDepthTestEnable vkCmdSetDepthTestEnable; +extern PFN_vkCmdSetDepthWriteEnable vkCmdSetDepthWriteEnable; +extern PFN_vkCmdSetEvent2 vkCmdSetEvent2; +extern PFN_vkCmdSetFrontFace vkCmdSetFrontFace; +extern PFN_vkCmdSetPrimitiveRestartEnable vkCmdSetPrimitiveRestartEnable; +extern PFN_vkCmdSetPrimitiveTopology vkCmdSetPrimitiveTopology; +extern PFN_vkCmdSetRasterizerDiscardEnable vkCmdSetRasterizerDiscardEnable; +extern PFN_vkCmdSetScissorWithCount vkCmdSetScissorWithCount; +extern PFN_vkCmdSetStencilOp vkCmdSetStencilOp; +extern PFN_vkCmdSetStencilTestEnable vkCmdSetStencilTestEnable; +extern PFN_vkCmdSetViewportWithCount vkCmdSetViewportWithCount; +extern PFN_vkCmdWaitEvents2 vkCmdWaitEvents2; +extern PFN_vkCmdWriteTimestamp2 vkCmdWriteTimestamp2; +extern PFN_vkCreatePrivateDataSlot vkCreatePrivateDataSlot; +extern PFN_vkDestroyPrivateDataSlot vkDestroyPrivateDataSlot; +extern PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements; +extern PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements; +extern PFN_vkGetDeviceImageSparseMemoryRequirements vkGetDeviceImageSparseMemoryRequirements; +extern PFN_vkGetPhysicalDeviceToolProperties vkGetPhysicalDeviceToolProperties; +extern PFN_vkGetPrivateData vkGetPrivateData; +extern PFN_vkQueueSubmit2 vkQueueSubmit2; +extern PFN_vkSetPrivateData vkSetPrivateData; +#endif /* defined(VK_VERSION_1_3) */ +#if defined(VK_AMDX_shader_enqueue) +extern PFN_vkCmdDispatchGraphAMDX vkCmdDispatchGraphAMDX; +extern PFN_vkCmdDispatchGraphIndirectAMDX vkCmdDispatchGraphIndirectAMDX; +extern PFN_vkCmdDispatchGraphIndirectCountAMDX vkCmdDispatchGraphIndirectCountAMDX; +extern PFN_vkCmdInitializeGraphScratchMemoryAMDX vkCmdInitializeGraphScratchMemoryAMDX; +extern PFN_vkCreateExecutionGraphPipelinesAMDX vkCreateExecutionGraphPipelinesAMDX; +extern PFN_vkGetExecutionGraphPipelineNodeIndexAMDX vkGetExecutionGraphPipelineNodeIndexAMDX; +extern PFN_vkGetExecutionGraphPipelineScratchSizeAMDX vkGetExecutionGraphPipelineScratchSizeAMDX; +#endif /* defined(VK_AMDX_shader_enqueue) */ +#if defined(VK_AMD_anti_lag) +extern PFN_vkAntiLagUpdateAMD vkAntiLagUpdateAMD; +#endif /* defined(VK_AMD_anti_lag) */ +#if defined(VK_AMD_buffer_marker) +extern PFN_vkCmdWriteBufferMarkerAMD vkCmdWriteBufferMarkerAMD; +#endif /* defined(VK_AMD_buffer_marker) */ +#if defined(VK_AMD_display_native_hdr) +extern PFN_vkSetLocalDimmingAMD vkSetLocalDimmingAMD; +#endif /* defined(VK_AMD_display_native_hdr) */ +#if defined(VK_AMD_draw_indirect_count) +extern PFN_vkCmdDrawIndexedIndirectCountAMD vkCmdDrawIndexedIndirectCountAMD; +extern PFN_vkCmdDrawIndirectCountAMD vkCmdDrawIndirectCountAMD; +#endif /* defined(VK_AMD_draw_indirect_count) */ +#if defined(VK_AMD_shader_info) +extern PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD; +#endif /* defined(VK_AMD_shader_info) */ +#if defined(VK_ANDROID_external_memory_android_hardware_buffer) +extern PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID; +extern PFN_vkGetMemoryAndroidHardwareBufferANDROID vkGetMemoryAndroidHardwareBufferANDROID; +#endif /* defined(VK_ANDROID_external_memory_android_hardware_buffer) */ +#if defined(VK_EXT_acquire_drm_display) +extern PFN_vkAcquireDrmDisplayEXT vkAcquireDrmDisplayEXT; +extern PFN_vkGetDrmDisplayEXT vkGetDrmDisplayEXT; +#endif /* defined(VK_EXT_acquire_drm_display) */ +#if defined(VK_EXT_acquire_xlib_display) +extern PFN_vkAcquireXlibDisplayEXT vkAcquireXlibDisplayEXT; +extern PFN_vkGetRandROutputDisplayEXT vkGetRandROutputDisplayEXT; +#endif /* defined(VK_EXT_acquire_xlib_display) */ +#if defined(VK_EXT_attachment_feedback_loop_dynamic_state) +extern PFN_vkCmdSetAttachmentFeedbackLoopEnableEXT vkCmdSetAttachmentFeedbackLoopEnableEXT; +#endif /* defined(VK_EXT_attachment_feedback_loop_dynamic_state) */ +#if defined(VK_EXT_buffer_device_address) +extern PFN_vkGetBufferDeviceAddressEXT vkGetBufferDeviceAddressEXT; +#endif /* defined(VK_EXT_buffer_device_address) */ +#if defined(VK_EXT_calibrated_timestamps) +extern PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT; +extern PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT vkGetPhysicalDeviceCalibrateableTimeDomainsEXT; +#endif /* defined(VK_EXT_calibrated_timestamps) */ +#if defined(VK_EXT_color_write_enable) +extern PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT; +#endif /* defined(VK_EXT_color_write_enable) */ +#if defined(VK_EXT_conditional_rendering) +extern PFN_vkCmdBeginConditionalRenderingEXT vkCmdBeginConditionalRenderingEXT; +extern PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT; +#endif /* defined(VK_EXT_conditional_rendering) */ +#if defined(VK_EXT_debug_marker) +extern PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBeginEXT; +extern PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEndEXT; +extern PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsertEXT; +extern PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectNameEXT; +extern PFN_vkDebugMarkerSetObjectTagEXT vkDebugMarkerSetObjectTagEXT; +#endif /* defined(VK_EXT_debug_marker) */ +#if defined(VK_EXT_debug_report) +extern PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; +extern PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT; +extern PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; +#endif /* defined(VK_EXT_debug_report) */ +#if defined(VK_EXT_debug_utils) +extern PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT; +extern PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT; +extern PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT; +extern PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; +extern PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; +extern PFN_vkQueueBeginDebugUtilsLabelEXT vkQueueBeginDebugUtilsLabelEXT; +extern PFN_vkQueueEndDebugUtilsLabelEXT vkQueueEndDebugUtilsLabelEXT; +extern PFN_vkQueueInsertDebugUtilsLabelEXT vkQueueInsertDebugUtilsLabelEXT; +extern PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT; +extern PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT; +extern PFN_vkSubmitDebugUtilsMessageEXT vkSubmitDebugUtilsMessageEXT; +#endif /* defined(VK_EXT_debug_utils) */ +#if defined(VK_EXT_depth_bias_control) +extern PFN_vkCmdSetDepthBias2EXT vkCmdSetDepthBias2EXT; +#endif /* defined(VK_EXT_depth_bias_control) */ +#if defined(VK_EXT_descriptor_buffer) +extern PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT vkCmdBindDescriptorBufferEmbeddedSamplersEXT; +extern PFN_vkCmdBindDescriptorBuffersEXT vkCmdBindDescriptorBuffersEXT; +extern PFN_vkCmdSetDescriptorBufferOffsetsEXT vkCmdSetDescriptorBufferOffsetsEXT; +extern PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT vkGetBufferOpaqueCaptureDescriptorDataEXT; +extern PFN_vkGetDescriptorEXT vkGetDescriptorEXT; +extern PFN_vkGetDescriptorSetLayoutBindingOffsetEXT vkGetDescriptorSetLayoutBindingOffsetEXT; +extern PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT; +extern PFN_vkGetImageOpaqueCaptureDescriptorDataEXT vkGetImageOpaqueCaptureDescriptorDataEXT; +extern PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT vkGetImageViewOpaqueCaptureDescriptorDataEXT; +extern PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT vkGetSamplerOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) +extern PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT; +#endif /* defined(VK_EXT_descriptor_buffer) && (defined(VK_KHR_acceleration_structure) || defined(VK_NV_ray_tracing)) */ +#if defined(VK_EXT_device_fault) +extern PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT; +#endif /* defined(VK_EXT_device_fault) */ +#if defined(VK_EXT_direct_mode_display) +extern PFN_vkReleaseDisplayEXT vkReleaseDisplayEXT; +#endif /* defined(VK_EXT_direct_mode_display) */ +#if defined(VK_EXT_directfb_surface) +extern PFN_vkCreateDirectFBSurfaceEXT vkCreateDirectFBSurfaceEXT; +extern PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT vkGetPhysicalDeviceDirectFBPresentationSupportEXT; +#endif /* defined(VK_EXT_directfb_surface) */ +#if defined(VK_EXT_discard_rectangles) +extern PFN_vkCmdSetDiscardRectangleEXT vkCmdSetDiscardRectangleEXT; +#endif /* defined(VK_EXT_discard_rectangles) */ +#if defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 +extern PFN_vkCmdSetDiscardRectangleEnableEXT vkCmdSetDiscardRectangleEnableEXT; +extern PFN_vkCmdSetDiscardRectangleModeEXT vkCmdSetDiscardRectangleModeEXT; +#endif /* defined(VK_EXT_discard_rectangles) && VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION >= 2 */ +#if defined(VK_EXT_display_control) +extern PFN_vkDisplayPowerControlEXT vkDisplayPowerControlEXT; +extern PFN_vkGetSwapchainCounterEXT vkGetSwapchainCounterEXT; +extern PFN_vkRegisterDeviceEventEXT vkRegisterDeviceEventEXT; +extern PFN_vkRegisterDisplayEventEXT vkRegisterDisplayEventEXT; +#endif /* defined(VK_EXT_display_control) */ +#if defined(VK_EXT_display_surface_counter) +extern PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT vkGetPhysicalDeviceSurfaceCapabilities2EXT; +#endif /* defined(VK_EXT_display_surface_counter) */ +#if defined(VK_EXT_external_memory_host) +extern PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesEXT; +#endif /* defined(VK_EXT_external_memory_host) */ +#if defined(VK_EXT_full_screen_exclusive) +extern PFN_vkAcquireFullScreenExclusiveModeEXT vkAcquireFullScreenExclusiveModeEXT; +extern PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT vkGetPhysicalDeviceSurfacePresentModes2EXT; +extern PFN_vkReleaseFullScreenExclusiveModeEXT vkReleaseFullScreenExclusiveModeEXT; +#endif /* defined(VK_EXT_full_screen_exclusive) */ +#if defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) +extern PFN_vkGetDeviceGroupSurfacePresentModes2EXT vkGetDeviceGroupSurfacePresentModes2EXT; +#endif /* defined(VK_EXT_full_screen_exclusive) && (defined(VK_KHR_device_group) || defined(VK_VERSION_1_1)) */ +#if defined(VK_EXT_hdr_metadata) +extern PFN_vkSetHdrMetadataEXT vkSetHdrMetadataEXT; +#endif /* defined(VK_EXT_hdr_metadata) */ +#if defined(VK_EXT_headless_surface) +extern PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT; +#endif /* defined(VK_EXT_headless_surface) */ +#if defined(VK_EXT_host_image_copy) +extern PFN_vkCopyImageToImageEXT vkCopyImageToImageEXT; +extern PFN_vkCopyImageToMemoryEXT vkCopyImageToMemoryEXT; +extern PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImageEXT; +extern PFN_vkTransitionImageLayoutEXT vkTransitionImageLayoutEXT; +#endif /* defined(VK_EXT_host_image_copy) */ +#if defined(VK_EXT_host_query_reset) +extern PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; +#endif /* defined(VK_EXT_host_query_reset) */ +#if defined(VK_EXT_image_drm_format_modifier) +extern PFN_vkGetImageDrmFormatModifierPropertiesEXT vkGetImageDrmFormatModifierPropertiesEXT; +#endif /* defined(VK_EXT_image_drm_format_modifier) */ +#if defined(VK_EXT_line_rasterization) +extern PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT; +#endif /* defined(VK_EXT_line_rasterization) */ +#if defined(VK_EXT_mesh_shader) +extern PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXT; +extern PFN_vkCmdDrawMeshTasksIndirectCountEXT vkCmdDrawMeshTasksIndirectCountEXT; +extern PFN_vkCmdDrawMeshTasksIndirectEXT vkCmdDrawMeshTasksIndirectEXT; +#endif /* defined(VK_EXT_mesh_shader) */ +#if defined(VK_EXT_metal_objects) +extern PFN_vkExportMetalObjectsEXT vkExportMetalObjectsEXT; +#endif /* defined(VK_EXT_metal_objects) */ +#if defined(VK_EXT_metal_surface) +extern PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; +#endif /* defined(VK_EXT_metal_surface) */ +#if defined(VK_EXT_multi_draw) +extern PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT; +extern PFN_vkCmdDrawMultiIndexedEXT vkCmdDrawMultiIndexedEXT; +#endif /* defined(VK_EXT_multi_draw) */ +#if defined(VK_EXT_opacity_micromap) +extern PFN_vkBuildMicromapsEXT vkBuildMicromapsEXT; +extern PFN_vkCmdBuildMicromapsEXT vkCmdBuildMicromapsEXT; +extern PFN_vkCmdCopyMemoryToMicromapEXT vkCmdCopyMemoryToMicromapEXT; +extern PFN_vkCmdCopyMicromapEXT vkCmdCopyMicromapEXT; +extern PFN_vkCmdCopyMicromapToMemoryEXT vkCmdCopyMicromapToMemoryEXT; +extern PFN_vkCmdWriteMicromapsPropertiesEXT vkCmdWriteMicromapsPropertiesEXT; +extern PFN_vkCopyMemoryToMicromapEXT vkCopyMemoryToMicromapEXT; +extern PFN_vkCopyMicromapEXT vkCopyMicromapEXT; +extern PFN_vkCopyMicromapToMemoryEXT vkCopyMicromapToMemoryEXT; +extern PFN_vkCreateMicromapEXT vkCreateMicromapEXT; +extern PFN_vkDestroyMicromapEXT vkDestroyMicromapEXT; +extern PFN_vkGetDeviceMicromapCompatibilityEXT vkGetDeviceMicromapCompatibilityEXT; +extern PFN_vkGetMicromapBuildSizesEXT vkGetMicromapBuildSizesEXT; +extern PFN_vkWriteMicromapsPropertiesEXT vkWriteMicromapsPropertiesEXT; +#endif /* defined(VK_EXT_opacity_micromap) */ +#if defined(VK_EXT_pageable_device_local_memory) +extern PFN_vkSetDeviceMemoryPriorityEXT vkSetDeviceMemoryPriorityEXT; +#endif /* defined(VK_EXT_pageable_device_local_memory) */ +#if defined(VK_EXT_pipeline_properties) +extern PFN_vkGetPipelinePropertiesEXT vkGetPipelinePropertiesEXT; +#endif /* defined(VK_EXT_pipeline_properties) */ +#if defined(VK_EXT_private_data) +extern PFN_vkCreatePrivateDataSlotEXT vkCreatePrivateDataSlotEXT; +extern PFN_vkDestroyPrivateDataSlotEXT vkDestroyPrivateDataSlotEXT; +extern PFN_vkGetPrivateDataEXT vkGetPrivateDataEXT; +extern PFN_vkSetPrivateDataEXT vkSetPrivateDataEXT; +#endif /* defined(VK_EXT_private_data) */ +#if defined(VK_EXT_sample_locations) +extern PFN_vkCmdSetSampleLocationsEXT vkCmdSetSampleLocationsEXT; +extern PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT vkGetPhysicalDeviceMultisamplePropertiesEXT; +#endif /* defined(VK_EXT_sample_locations) */ +#if defined(VK_EXT_shader_module_identifier) +extern PFN_vkGetShaderModuleCreateInfoIdentifierEXT vkGetShaderModuleCreateInfoIdentifierEXT; +extern PFN_vkGetShaderModuleIdentifierEXT vkGetShaderModuleIdentifierEXT; +#endif /* defined(VK_EXT_shader_module_identifier) */ +#if defined(VK_EXT_shader_object) +extern PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT; +extern PFN_vkCreateShadersEXT vkCreateShadersEXT; +extern PFN_vkDestroyShaderEXT vkDestroyShaderEXT; +extern PFN_vkGetShaderBinaryDataEXT vkGetShaderBinaryDataEXT; +#endif /* defined(VK_EXT_shader_object) */ +#if defined(VK_EXT_swapchain_maintenance1) +extern PFN_vkReleaseSwapchainImagesEXT vkReleaseSwapchainImagesEXT; +#endif /* defined(VK_EXT_swapchain_maintenance1) */ +#if defined(VK_EXT_tooling_info) +extern PFN_vkGetPhysicalDeviceToolPropertiesEXT vkGetPhysicalDeviceToolPropertiesEXT; +#endif /* defined(VK_EXT_tooling_info) */ +#if defined(VK_EXT_transform_feedback) +extern PFN_vkCmdBeginQueryIndexedEXT vkCmdBeginQueryIndexedEXT; +extern PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; +extern PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; +extern PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT; +extern PFN_vkCmdEndQueryIndexedEXT vkCmdEndQueryIndexedEXT; +extern PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; +#endif /* defined(VK_EXT_transform_feedback) */ +#if defined(VK_EXT_validation_cache) +extern PFN_vkCreateValidationCacheEXT vkCreateValidationCacheEXT; +extern PFN_vkDestroyValidationCacheEXT vkDestroyValidationCacheEXT; +extern PFN_vkGetValidationCacheDataEXT vkGetValidationCacheDataEXT; +extern PFN_vkMergeValidationCachesEXT vkMergeValidationCachesEXT; +#endif /* defined(VK_EXT_validation_cache) */ +#if defined(VK_FUCHSIA_buffer_collection) +extern PFN_vkCreateBufferCollectionFUCHSIA vkCreateBufferCollectionFUCHSIA; +extern PFN_vkDestroyBufferCollectionFUCHSIA vkDestroyBufferCollectionFUCHSIA; +extern PFN_vkGetBufferCollectionPropertiesFUCHSIA vkGetBufferCollectionPropertiesFUCHSIA; +extern PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA vkSetBufferCollectionBufferConstraintsFUCHSIA; +extern PFN_vkSetBufferCollectionImageConstraintsFUCHSIA vkSetBufferCollectionImageConstraintsFUCHSIA; +#endif /* defined(VK_FUCHSIA_buffer_collection) */ +#if defined(VK_FUCHSIA_external_memory) +extern PFN_vkGetMemoryZirconHandleFUCHSIA vkGetMemoryZirconHandleFUCHSIA; +extern PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA vkGetMemoryZirconHandlePropertiesFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_memory) */ +#if defined(VK_FUCHSIA_external_semaphore) +extern PFN_vkGetSemaphoreZirconHandleFUCHSIA vkGetSemaphoreZirconHandleFUCHSIA; +extern PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA; +#endif /* defined(VK_FUCHSIA_external_semaphore) */ +#if defined(VK_FUCHSIA_imagepipe_surface) +extern PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA; +#endif /* defined(VK_FUCHSIA_imagepipe_surface) */ +#if defined(VK_GGP_stream_descriptor_surface) +extern PFN_vkCreateStreamDescriptorSurfaceGGP vkCreateStreamDescriptorSurfaceGGP; +#endif /* defined(VK_GGP_stream_descriptor_surface) */ +#if defined(VK_GOOGLE_display_timing) +extern PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE; +extern PFN_vkGetRefreshCycleDurationGOOGLE vkGetRefreshCycleDurationGOOGLE; +#endif /* defined(VK_GOOGLE_display_timing) */ +#if defined(VK_HUAWEI_cluster_culling_shader) +extern PFN_vkCmdDrawClusterHUAWEI vkCmdDrawClusterHUAWEI; +extern PFN_vkCmdDrawClusterIndirectHUAWEI vkCmdDrawClusterIndirectHUAWEI; +#endif /* defined(VK_HUAWEI_cluster_culling_shader) */ +#if defined(VK_HUAWEI_invocation_mask) +extern PFN_vkCmdBindInvocationMaskHUAWEI vkCmdBindInvocationMaskHUAWEI; +#endif /* defined(VK_HUAWEI_invocation_mask) */ +#if defined(VK_HUAWEI_subpass_shading) +extern PFN_vkCmdSubpassShadingHUAWEI vkCmdSubpassShadingHUAWEI; +extern PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI; +#endif /* defined(VK_HUAWEI_subpass_shading) */ +#if defined(VK_INTEL_performance_query) +extern PFN_vkAcquirePerformanceConfigurationINTEL vkAcquirePerformanceConfigurationINTEL; +extern PFN_vkCmdSetPerformanceMarkerINTEL vkCmdSetPerformanceMarkerINTEL; +extern PFN_vkCmdSetPerformanceOverrideINTEL vkCmdSetPerformanceOverrideINTEL; +extern PFN_vkCmdSetPerformanceStreamMarkerINTEL vkCmdSetPerformanceStreamMarkerINTEL; +extern PFN_vkGetPerformanceParameterINTEL vkGetPerformanceParameterINTEL; +extern PFN_vkInitializePerformanceApiINTEL vkInitializePerformanceApiINTEL; +extern PFN_vkQueueSetPerformanceConfigurationINTEL vkQueueSetPerformanceConfigurationINTEL; +extern PFN_vkReleasePerformanceConfigurationINTEL vkReleasePerformanceConfigurationINTEL; +extern PFN_vkUninitializePerformanceApiINTEL vkUninitializePerformanceApiINTEL; +#endif /* defined(VK_INTEL_performance_query) */ +#if defined(VK_KHR_acceleration_structure) +extern PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR; +extern PFN_vkCmdBuildAccelerationStructuresIndirectKHR vkCmdBuildAccelerationStructuresIndirectKHR; +extern PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; +extern PFN_vkCmdCopyAccelerationStructureKHR vkCmdCopyAccelerationStructureKHR; +extern PFN_vkCmdCopyAccelerationStructureToMemoryKHR vkCmdCopyAccelerationStructureToMemoryKHR; +extern PFN_vkCmdCopyMemoryToAccelerationStructureKHR vkCmdCopyMemoryToAccelerationStructureKHR; +extern PFN_vkCmdWriteAccelerationStructuresPropertiesKHR vkCmdWriteAccelerationStructuresPropertiesKHR; +extern PFN_vkCopyAccelerationStructureKHR vkCopyAccelerationStructureKHR; +extern PFN_vkCopyAccelerationStructureToMemoryKHR vkCopyAccelerationStructureToMemoryKHR; +extern PFN_vkCopyMemoryToAccelerationStructureKHR vkCopyMemoryToAccelerationStructureKHR; +extern PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; +extern PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR; +extern PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; +extern PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; +extern PFN_vkGetDeviceAccelerationStructureCompatibilityKHR vkGetDeviceAccelerationStructureCompatibilityKHR; +extern PFN_vkWriteAccelerationStructuresPropertiesKHR vkWriteAccelerationStructuresPropertiesKHR; +#endif /* defined(VK_KHR_acceleration_structure) */ +#if defined(VK_KHR_android_surface) +extern PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; +#endif /* defined(VK_KHR_android_surface) */ +#if defined(VK_KHR_bind_memory2) +extern PFN_vkBindBufferMemory2KHR vkBindBufferMemory2KHR; +extern PFN_vkBindImageMemory2KHR vkBindImageMemory2KHR; +#endif /* defined(VK_KHR_bind_memory2) */ +#if defined(VK_KHR_buffer_device_address) +extern PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; +extern PFN_vkGetBufferOpaqueCaptureAddressKHR vkGetBufferOpaqueCaptureAddressKHR; +extern PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR vkGetDeviceMemoryOpaqueCaptureAddressKHR; +#endif /* defined(VK_KHR_buffer_device_address) */ +#if defined(VK_KHR_calibrated_timestamps) +extern PFN_vkGetCalibratedTimestampsKHR vkGetCalibratedTimestampsKHR; +extern PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsKHR vkGetPhysicalDeviceCalibrateableTimeDomainsKHR; +#endif /* defined(VK_KHR_calibrated_timestamps) */ +#if defined(VK_KHR_cooperative_matrix) +extern PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR; +#endif /* defined(VK_KHR_cooperative_matrix) */ +#if defined(VK_KHR_copy_commands2) +extern PFN_vkCmdBlitImage2KHR vkCmdBlitImage2KHR; +extern PFN_vkCmdCopyBuffer2KHR vkCmdCopyBuffer2KHR; +extern PFN_vkCmdCopyBufferToImage2KHR vkCmdCopyBufferToImage2KHR; +extern PFN_vkCmdCopyImage2KHR vkCmdCopyImage2KHR; +extern PFN_vkCmdCopyImageToBuffer2KHR vkCmdCopyImageToBuffer2KHR; +extern PFN_vkCmdResolveImage2KHR vkCmdResolveImage2KHR; +#endif /* defined(VK_KHR_copy_commands2) */ +#if defined(VK_KHR_create_renderpass2) +extern PFN_vkCmdBeginRenderPass2KHR vkCmdBeginRenderPass2KHR; +extern PFN_vkCmdEndRenderPass2KHR vkCmdEndRenderPass2KHR; +extern PFN_vkCmdNextSubpass2KHR vkCmdNextSubpass2KHR; +extern PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR; +#endif /* defined(VK_KHR_create_renderpass2) */ +#if defined(VK_KHR_deferred_host_operations) +extern PFN_vkCreateDeferredOperationKHR vkCreateDeferredOperationKHR; +extern PFN_vkDeferredOperationJoinKHR vkDeferredOperationJoinKHR; +extern PFN_vkDestroyDeferredOperationKHR vkDestroyDeferredOperationKHR; +extern PFN_vkGetDeferredOperationMaxConcurrencyKHR vkGetDeferredOperationMaxConcurrencyKHR; +extern PFN_vkGetDeferredOperationResultKHR vkGetDeferredOperationResultKHR; +#endif /* defined(VK_KHR_deferred_host_operations) */ +#if defined(VK_KHR_descriptor_update_template) +extern PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; +extern PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; +extern PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; +#endif /* defined(VK_KHR_descriptor_update_template) */ +#if defined(VK_KHR_device_group) +extern PFN_vkCmdDispatchBaseKHR vkCmdDispatchBaseKHR; +extern PFN_vkCmdSetDeviceMaskKHR vkCmdSetDeviceMaskKHR; +extern PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR vkGetDeviceGroupPeerMemoryFeaturesKHR; +#endif /* defined(VK_KHR_device_group) */ +#if defined(VK_KHR_device_group_creation) +extern PFN_vkEnumeratePhysicalDeviceGroupsKHR vkEnumeratePhysicalDeviceGroupsKHR; +#endif /* defined(VK_KHR_device_group_creation) */ +#if defined(VK_KHR_display) +extern PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR; +extern PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR; +extern PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR; +extern PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR; +extern PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR; +extern PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR; +extern PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR; +#endif /* defined(VK_KHR_display) */ +#if defined(VK_KHR_display_swapchain) +extern PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR; +#endif /* defined(VK_KHR_display_swapchain) */ +#if defined(VK_KHR_draw_indirect_count) +extern PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR; +extern PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR; +#endif /* defined(VK_KHR_draw_indirect_count) */ +#if defined(VK_KHR_dynamic_rendering) +extern PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHR; +extern PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHR; +#endif /* defined(VK_KHR_dynamic_rendering) */ +#if defined(VK_KHR_dynamic_rendering_local_read) +extern PFN_vkCmdSetRenderingAttachmentLocationsKHR vkCmdSetRenderingAttachmentLocationsKHR; +extern PFN_vkCmdSetRenderingInputAttachmentIndicesKHR vkCmdSetRenderingInputAttachmentIndicesKHR; +#endif /* defined(VK_KHR_dynamic_rendering_local_read) */ +#if defined(VK_KHR_external_fence_capabilities) +extern PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR vkGetPhysicalDeviceExternalFencePropertiesKHR; +#endif /* defined(VK_KHR_external_fence_capabilities) */ +#if defined(VK_KHR_external_fence_fd) +extern PFN_vkGetFenceFdKHR vkGetFenceFdKHR; +extern PFN_vkImportFenceFdKHR vkImportFenceFdKHR; +#endif /* defined(VK_KHR_external_fence_fd) */ +#if defined(VK_KHR_external_fence_win32) +extern PFN_vkGetFenceWin32HandleKHR vkGetFenceWin32HandleKHR; +extern PFN_vkImportFenceWin32HandleKHR vkImportFenceWin32HandleKHR; +#endif /* defined(VK_KHR_external_fence_win32) */ +#if defined(VK_KHR_external_memory_capabilities) +extern PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR vkGetPhysicalDeviceExternalBufferPropertiesKHR; +#endif /* defined(VK_KHR_external_memory_capabilities) */ +#if defined(VK_KHR_external_memory_fd) +extern PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR; +extern PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR; +#endif /* defined(VK_KHR_external_memory_fd) */ +#if defined(VK_KHR_external_memory_win32) +extern PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR; +extern PFN_vkGetMemoryWin32HandlePropertiesKHR vkGetMemoryWin32HandlePropertiesKHR; +#endif /* defined(VK_KHR_external_memory_win32) */ +#if defined(VK_KHR_external_semaphore_capabilities) +extern PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR vkGetPhysicalDeviceExternalSemaphorePropertiesKHR; +#endif /* defined(VK_KHR_external_semaphore_capabilities) */ +#if defined(VK_KHR_external_semaphore_fd) +extern PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; +extern PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; +#endif /* defined(VK_KHR_external_semaphore_fd) */ +#if defined(VK_KHR_external_semaphore_win32) +extern PFN_vkGetSemaphoreWin32HandleKHR vkGetSemaphoreWin32HandleKHR; +extern PFN_vkImportSemaphoreWin32HandleKHR vkImportSemaphoreWin32HandleKHR; +#endif /* defined(VK_KHR_external_semaphore_win32) */ +#if defined(VK_KHR_fragment_shading_rate) +extern PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR; +extern PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR vkGetPhysicalDeviceFragmentShadingRatesKHR; +#endif /* defined(VK_KHR_fragment_shading_rate) */ +#if defined(VK_KHR_get_display_properties2) +extern PFN_vkGetDisplayModeProperties2KHR vkGetDisplayModeProperties2KHR; +extern PFN_vkGetDisplayPlaneCapabilities2KHR vkGetDisplayPlaneCapabilities2KHR; +extern PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR vkGetPhysicalDeviceDisplayPlaneProperties2KHR; +extern PFN_vkGetPhysicalDeviceDisplayProperties2KHR vkGetPhysicalDeviceDisplayProperties2KHR; +#endif /* defined(VK_KHR_get_display_properties2) */ +#if defined(VK_KHR_get_memory_requirements2) +extern PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; +extern PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; +extern PFN_vkGetImageSparseMemoryRequirements2KHR vkGetImageSparseMemoryRequirements2KHR; +#endif /* defined(VK_KHR_get_memory_requirements2) */ +#if defined(VK_KHR_get_physical_device_properties2) +extern PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; +extern PFN_vkGetPhysicalDeviceFormatProperties2KHR vkGetPhysicalDeviceFormatProperties2KHR; +extern PFN_vkGetPhysicalDeviceImageFormatProperties2KHR vkGetPhysicalDeviceImageFormatProperties2KHR; +extern PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR; +extern PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; +extern PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR vkGetPhysicalDeviceQueueFamilyProperties2KHR; +extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR vkGetPhysicalDeviceSparseImageFormatProperties2KHR; +#endif /* defined(VK_KHR_get_physical_device_properties2) */ +#if defined(VK_KHR_get_surface_capabilities2) +extern PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR; +extern PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR; +#endif /* defined(VK_KHR_get_surface_capabilities2) */ +#if defined(VK_KHR_line_rasterization) +extern PFN_vkCmdSetLineStippleKHR vkCmdSetLineStippleKHR; +#endif /* defined(VK_KHR_line_rasterization) */ +#if defined(VK_KHR_maintenance1) +extern PFN_vkTrimCommandPoolKHR vkTrimCommandPoolKHR; +#endif /* defined(VK_KHR_maintenance1) */ +#if defined(VK_KHR_maintenance3) +extern PFN_vkGetDescriptorSetLayoutSupportKHR vkGetDescriptorSetLayoutSupportKHR; +#endif /* defined(VK_KHR_maintenance3) */ +#if defined(VK_KHR_maintenance4) +extern PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirementsKHR; +extern PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirementsKHR; +extern PFN_vkGetDeviceImageSparseMemoryRequirementsKHR vkGetDeviceImageSparseMemoryRequirementsKHR; +#endif /* defined(VK_KHR_maintenance4) */ +#if defined(VK_KHR_maintenance5) +extern PFN_vkCmdBindIndexBuffer2KHR vkCmdBindIndexBuffer2KHR; +extern PFN_vkGetDeviceImageSubresourceLayoutKHR vkGetDeviceImageSubresourceLayoutKHR; +extern PFN_vkGetImageSubresourceLayout2KHR vkGetImageSubresourceLayout2KHR; +extern PFN_vkGetRenderingAreaGranularityKHR vkGetRenderingAreaGranularityKHR; +#endif /* defined(VK_KHR_maintenance5) */ +#if defined(VK_KHR_maintenance6) +extern PFN_vkCmdBindDescriptorSets2KHR vkCmdBindDescriptorSets2KHR; +extern PFN_vkCmdPushConstants2KHR vkCmdPushConstants2KHR; +#endif /* defined(VK_KHR_maintenance6) */ +#if defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) +extern PFN_vkCmdPushDescriptorSet2KHR vkCmdPushDescriptorSet2KHR; +extern PFN_vkCmdPushDescriptorSetWithTemplate2KHR vkCmdPushDescriptorSetWithTemplate2KHR; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) +extern PFN_vkCmdBindDescriptorBufferEmbeddedSamplers2EXT vkCmdBindDescriptorBufferEmbeddedSamplers2EXT; +extern PFN_vkCmdSetDescriptorBufferOffsets2EXT vkCmdSetDescriptorBufferOffsets2EXT; +#endif /* defined(VK_KHR_maintenance6) && defined(VK_EXT_descriptor_buffer) */ +#if defined(VK_KHR_map_memory2) +extern PFN_vkMapMemory2KHR vkMapMemory2KHR; +extern PFN_vkUnmapMemory2KHR vkUnmapMemory2KHR; +#endif /* defined(VK_KHR_map_memory2) */ +#if defined(VK_KHR_performance_query) +extern PFN_vkAcquireProfilingLockKHR vkAcquireProfilingLockKHR; +extern PFN_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR; +extern PFN_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR; +extern PFN_vkReleaseProfilingLockKHR vkReleaseProfilingLockKHR; +#endif /* defined(VK_KHR_performance_query) */ +#if defined(VK_KHR_pipeline_binary) +extern PFN_vkCreatePipelineBinariesKHR vkCreatePipelineBinariesKHR; +extern PFN_vkDestroyPipelineBinaryKHR vkDestroyPipelineBinaryKHR; +extern PFN_vkGetPipelineBinaryDataKHR vkGetPipelineBinaryDataKHR; +extern PFN_vkGetPipelineKeyKHR vkGetPipelineKeyKHR; +extern PFN_vkReleaseCapturedPipelineDataKHR vkReleaseCapturedPipelineDataKHR; +#endif /* defined(VK_KHR_pipeline_binary) */ +#if defined(VK_KHR_pipeline_executable_properties) +extern PFN_vkGetPipelineExecutableInternalRepresentationsKHR vkGetPipelineExecutableInternalRepresentationsKHR; +extern PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR; +extern PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR; +#endif /* defined(VK_KHR_pipeline_executable_properties) */ +#if defined(VK_KHR_present_wait) +extern PFN_vkWaitForPresentKHR vkWaitForPresentKHR; +#endif /* defined(VK_KHR_present_wait) */ +#if defined(VK_KHR_push_descriptor) +extern PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR; +#endif /* defined(VK_KHR_push_descriptor) */ +#if defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) +extern PFN_vkCmdTraceRaysIndirect2KHR vkCmdTraceRaysIndirect2KHR; +#endif /* defined(VK_KHR_ray_tracing_maintenance1) && defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_ray_tracing_pipeline) +extern PFN_vkCmdSetRayTracingPipelineStackSizeKHR vkCmdSetRayTracingPipelineStackSizeKHR; +extern PFN_vkCmdTraceRaysIndirectKHR vkCmdTraceRaysIndirectKHR; +extern PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; +extern PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; +extern PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR vkGetRayTracingCaptureReplayShaderGroupHandlesKHR; +extern PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; +extern PFN_vkGetRayTracingShaderGroupStackSizeKHR vkGetRayTracingShaderGroupStackSizeKHR; +#endif /* defined(VK_KHR_ray_tracing_pipeline) */ +#if defined(VK_KHR_sampler_ycbcr_conversion) +extern PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; +extern PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; +#endif /* defined(VK_KHR_sampler_ycbcr_conversion) */ +#if defined(VK_KHR_shared_presentable_image) +extern PFN_vkGetSwapchainStatusKHR vkGetSwapchainStatusKHR; +#endif /* defined(VK_KHR_shared_presentable_image) */ +#if defined(VK_KHR_surface) +extern PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; +extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; +extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; +extern PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; +extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; +#endif /* defined(VK_KHR_surface) */ +#if defined(VK_KHR_swapchain) +extern PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; +extern PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; +extern PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; +extern PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; +extern PFN_vkQueuePresentKHR vkQueuePresentKHR; +#endif /* defined(VK_KHR_swapchain) */ +#if defined(VK_KHR_synchronization2) +extern PFN_vkCmdPipelineBarrier2KHR vkCmdPipelineBarrier2KHR; +extern PFN_vkCmdResetEvent2KHR vkCmdResetEvent2KHR; +extern PFN_vkCmdSetEvent2KHR vkCmdSetEvent2KHR; +extern PFN_vkCmdWaitEvents2KHR vkCmdWaitEvents2KHR; +extern PFN_vkCmdWriteTimestamp2KHR vkCmdWriteTimestamp2KHR; +extern PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; +#endif /* defined(VK_KHR_synchronization2) */ +#if defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) +extern PFN_vkCmdWriteBufferMarker2AMD vkCmdWriteBufferMarker2AMD; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_AMD_buffer_marker) */ +#if defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) +extern PFN_vkGetQueueCheckpointData2NV vkGetQueueCheckpointData2NV; +#endif /* defined(VK_KHR_synchronization2) && defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_KHR_timeline_semaphore) +extern PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; +extern PFN_vkSignalSemaphoreKHR vkSignalSemaphoreKHR; +extern PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; +#endif /* defined(VK_KHR_timeline_semaphore) */ +#if defined(VK_KHR_video_decode_queue) +extern PFN_vkCmdDecodeVideoKHR vkCmdDecodeVideoKHR; +#endif /* defined(VK_KHR_video_decode_queue) */ +#if defined(VK_KHR_video_encode_queue) +extern PFN_vkCmdEncodeVideoKHR vkCmdEncodeVideoKHR; +extern PFN_vkGetEncodedVideoSessionParametersKHR vkGetEncodedVideoSessionParametersKHR; +extern PFN_vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR; +#endif /* defined(VK_KHR_video_encode_queue) */ +#if defined(VK_KHR_video_queue) +extern PFN_vkBindVideoSessionMemoryKHR vkBindVideoSessionMemoryKHR; +extern PFN_vkCmdBeginVideoCodingKHR vkCmdBeginVideoCodingKHR; +extern PFN_vkCmdControlVideoCodingKHR vkCmdControlVideoCodingKHR; +extern PFN_vkCmdEndVideoCodingKHR vkCmdEndVideoCodingKHR; +extern PFN_vkCreateVideoSessionKHR vkCreateVideoSessionKHR; +extern PFN_vkCreateVideoSessionParametersKHR vkCreateVideoSessionParametersKHR; +extern PFN_vkDestroyVideoSessionKHR vkDestroyVideoSessionKHR; +extern PFN_vkDestroyVideoSessionParametersKHR vkDestroyVideoSessionParametersKHR; +extern PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR vkGetPhysicalDeviceVideoCapabilitiesKHR; +extern PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR vkGetPhysicalDeviceVideoFormatPropertiesKHR; +extern PFN_vkGetVideoSessionMemoryRequirementsKHR vkGetVideoSessionMemoryRequirementsKHR; +extern PFN_vkUpdateVideoSessionParametersKHR vkUpdateVideoSessionParametersKHR; +#endif /* defined(VK_KHR_video_queue) */ +#if defined(VK_KHR_wayland_surface) +extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; +extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR; +#endif /* defined(VK_KHR_wayland_surface) */ +#if defined(VK_KHR_win32_surface) +extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; +extern PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR; +#endif /* defined(VK_KHR_win32_surface) */ +#if defined(VK_KHR_xcb_surface) +extern PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; +extern PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR; +#endif /* defined(VK_KHR_xcb_surface) */ +#if defined(VK_KHR_xlib_surface) +extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; +extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR; +#endif /* defined(VK_KHR_xlib_surface) */ +#if defined(VK_MVK_ios_surface) +extern PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK; +#endif /* defined(VK_MVK_ios_surface) */ +#if defined(VK_MVK_macos_surface) +extern PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; +#endif /* defined(VK_MVK_macos_surface) */ +#if defined(VK_NN_vi_surface) +extern PFN_vkCreateViSurfaceNN vkCreateViSurfaceNN; +#endif /* defined(VK_NN_vi_surface) */ +#if defined(VK_NVX_binary_import) +extern PFN_vkCmdCuLaunchKernelNVX vkCmdCuLaunchKernelNVX; +extern PFN_vkCreateCuFunctionNVX vkCreateCuFunctionNVX; +extern PFN_vkCreateCuModuleNVX vkCreateCuModuleNVX; +extern PFN_vkDestroyCuFunctionNVX vkDestroyCuFunctionNVX; +extern PFN_vkDestroyCuModuleNVX vkDestroyCuModuleNVX; +#endif /* defined(VK_NVX_binary_import) */ +#if defined(VK_NVX_image_view_handle) +extern PFN_vkGetImageViewAddressNVX vkGetImageViewAddressNVX; +extern PFN_vkGetImageViewHandleNVX vkGetImageViewHandleNVX; +#endif /* defined(VK_NVX_image_view_handle) */ +#if defined(VK_NV_acquire_winrt_display) +extern PFN_vkAcquireWinrtDisplayNV vkAcquireWinrtDisplayNV; +extern PFN_vkGetWinrtDisplayNV vkGetWinrtDisplayNV; +#endif /* defined(VK_NV_acquire_winrt_display) */ +#if defined(VK_NV_clip_space_w_scaling) +extern PFN_vkCmdSetViewportWScalingNV vkCmdSetViewportWScalingNV; +#endif /* defined(VK_NV_clip_space_w_scaling) */ +#if defined(VK_NV_cooperative_matrix) +extern PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV vkGetPhysicalDeviceCooperativeMatrixPropertiesNV; +#endif /* defined(VK_NV_cooperative_matrix) */ +#if defined(VK_NV_copy_memory_indirect) +extern PFN_vkCmdCopyMemoryIndirectNV vkCmdCopyMemoryIndirectNV; +extern PFN_vkCmdCopyMemoryToImageIndirectNV vkCmdCopyMemoryToImageIndirectNV; +#endif /* defined(VK_NV_copy_memory_indirect) */ +#if defined(VK_NV_coverage_reduction_mode) +extern PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV; +#endif /* defined(VK_NV_coverage_reduction_mode) */ +#if defined(VK_NV_cuda_kernel_launch) +extern PFN_vkCmdCudaLaunchKernelNV vkCmdCudaLaunchKernelNV; +extern PFN_vkCreateCudaFunctionNV vkCreateCudaFunctionNV; +extern PFN_vkCreateCudaModuleNV vkCreateCudaModuleNV; +extern PFN_vkDestroyCudaFunctionNV vkDestroyCudaFunctionNV; +extern PFN_vkDestroyCudaModuleNV vkDestroyCudaModuleNV; +extern PFN_vkGetCudaModuleCacheNV vkGetCudaModuleCacheNV; +#endif /* defined(VK_NV_cuda_kernel_launch) */ +#if defined(VK_NV_device_diagnostic_checkpoints) +extern PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; +extern PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; +#endif /* defined(VK_NV_device_diagnostic_checkpoints) */ +#if defined(VK_NV_device_generated_commands) +extern PFN_vkCmdBindPipelineShaderGroupNV vkCmdBindPipelineShaderGroupNV; +extern PFN_vkCmdExecuteGeneratedCommandsNV vkCmdExecuteGeneratedCommandsNV; +extern PFN_vkCmdPreprocessGeneratedCommandsNV vkCmdPreprocessGeneratedCommandsNV; +extern PFN_vkCreateIndirectCommandsLayoutNV vkCreateIndirectCommandsLayoutNV; +extern PFN_vkDestroyIndirectCommandsLayoutNV vkDestroyIndirectCommandsLayoutNV; +extern PFN_vkGetGeneratedCommandsMemoryRequirementsNV vkGetGeneratedCommandsMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands) */ +#if defined(VK_NV_device_generated_commands_compute) +extern PFN_vkCmdUpdatePipelineIndirectBufferNV vkCmdUpdatePipelineIndirectBufferNV; +extern PFN_vkGetPipelineIndirectDeviceAddressNV vkGetPipelineIndirectDeviceAddressNV; +extern PFN_vkGetPipelineIndirectMemoryRequirementsNV vkGetPipelineIndirectMemoryRequirementsNV; +#endif /* defined(VK_NV_device_generated_commands_compute) */ +#if defined(VK_NV_external_memory_capabilities) +extern PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV vkGetPhysicalDeviceExternalImageFormatPropertiesNV; +#endif /* defined(VK_NV_external_memory_capabilities) */ +#if defined(VK_NV_external_memory_rdma) +extern PFN_vkGetMemoryRemoteAddressNV vkGetMemoryRemoteAddressNV; +#endif /* defined(VK_NV_external_memory_rdma) */ +#if defined(VK_NV_external_memory_win32) +extern PFN_vkGetMemoryWin32HandleNV vkGetMemoryWin32HandleNV; +#endif /* defined(VK_NV_external_memory_win32) */ +#if defined(VK_NV_fragment_shading_rate_enums) +extern PFN_vkCmdSetFragmentShadingRateEnumNV vkCmdSetFragmentShadingRateEnumNV; +#endif /* defined(VK_NV_fragment_shading_rate_enums) */ +#if defined(VK_NV_low_latency2) +extern PFN_vkGetLatencyTimingsNV vkGetLatencyTimingsNV; +extern PFN_vkLatencySleepNV vkLatencySleepNV; +extern PFN_vkQueueNotifyOutOfBandNV vkQueueNotifyOutOfBandNV; +extern PFN_vkSetLatencyMarkerNV vkSetLatencyMarkerNV; +extern PFN_vkSetLatencySleepModeNV vkSetLatencySleepModeNV; +#endif /* defined(VK_NV_low_latency2) */ +#if defined(VK_NV_memory_decompression) +extern PFN_vkCmdDecompressMemoryIndirectCountNV vkCmdDecompressMemoryIndirectCountNV; +extern PFN_vkCmdDecompressMemoryNV vkCmdDecompressMemoryNV; +#endif /* defined(VK_NV_memory_decompression) */ +#if defined(VK_NV_mesh_shader) +extern PFN_vkCmdDrawMeshTasksIndirectCountNV vkCmdDrawMeshTasksIndirectCountNV; +extern PFN_vkCmdDrawMeshTasksIndirectNV vkCmdDrawMeshTasksIndirectNV; +extern PFN_vkCmdDrawMeshTasksNV vkCmdDrawMeshTasksNV; +#endif /* defined(VK_NV_mesh_shader) */ +#if defined(VK_NV_optical_flow) +extern PFN_vkBindOpticalFlowSessionImageNV vkBindOpticalFlowSessionImageNV; +extern PFN_vkCmdOpticalFlowExecuteNV vkCmdOpticalFlowExecuteNV; +extern PFN_vkCreateOpticalFlowSessionNV vkCreateOpticalFlowSessionNV; +extern PFN_vkDestroyOpticalFlowSessionNV vkDestroyOpticalFlowSessionNV; +extern PFN_vkGetPhysicalDeviceOpticalFlowImageFormatsNV vkGetPhysicalDeviceOpticalFlowImageFormatsNV; +#endif /* defined(VK_NV_optical_flow) */ +#if defined(VK_NV_ray_tracing) +extern PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; +extern PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; +extern PFN_vkCmdCopyAccelerationStructureNV vkCmdCopyAccelerationStructureNV; +extern PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; +extern PFN_vkCmdWriteAccelerationStructuresPropertiesNV vkCmdWriteAccelerationStructuresPropertiesNV; +extern PFN_vkCompileDeferredNV vkCompileDeferredNV; +extern PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; +extern PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; +extern PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV; +extern PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; +extern PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; +extern PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; +#endif /* defined(VK_NV_ray_tracing) */ +#if defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 +extern PFN_vkCmdSetExclusiveScissorEnableNV vkCmdSetExclusiveScissorEnableNV; +#endif /* defined(VK_NV_scissor_exclusive) && VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION >= 2 */ +#if defined(VK_NV_scissor_exclusive) +extern PFN_vkCmdSetExclusiveScissorNV vkCmdSetExclusiveScissorNV; +#endif /* defined(VK_NV_scissor_exclusive) */ +#if defined(VK_NV_shading_rate_image) +extern PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV; +extern PFN_vkCmdSetCoarseSampleOrderNV vkCmdSetCoarseSampleOrderNV; +extern PFN_vkCmdSetViewportShadingRatePaletteNV vkCmdSetViewportShadingRatePaletteNV; +#endif /* defined(VK_NV_shading_rate_image) */ +#if defined(VK_QCOM_tile_properties) +extern PFN_vkGetDynamicRenderingTilePropertiesQCOM vkGetDynamicRenderingTilePropertiesQCOM; +extern PFN_vkGetFramebufferTilePropertiesQCOM vkGetFramebufferTilePropertiesQCOM; +#endif /* defined(VK_QCOM_tile_properties) */ +#if defined(VK_QNX_external_memory_screen_buffer) +extern PFN_vkGetScreenBufferPropertiesQNX vkGetScreenBufferPropertiesQNX; +#endif /* defined(VK_QNX_external_memory_screen_buffer) */ +#if defined(VK_QNX_screen_surface) +extern PFN_vkCreateScreenSurfaceQNX vkCreateScreenSurfaceQNX; +extern PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX vkGetPhysicalDeviceScreenPresentationSupportQNX; +#endif /* defined(VK_QNX_screen_surface) */ +#if defined(VK_VALVE_descriptor_set_host_mapping) +extern PFN_vkGetDescriptorSetHostMappingVALVE vkGetDescriptorSetHostMappingVALVE; +extern PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE vkGetDescriptorSetLayoutHostMappingInfoVALVE; +#endif /* defined(VK_VALVE_descriptor_set_host_mapping) */ +#if (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) +extern PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; +extern PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; +extern PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; +extern PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; +extern PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; +extern PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; +extern PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; +extern PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; +extern PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT; +extern PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; +extern PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; +extern PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) +extern PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT; +extern PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT; +extern PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT; +extern PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT; +extern PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state2)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) +extern PFN_vkCmdSetAlphaToCoverageEnableEXT vkCmdSetAlphaToCoverageEnableEXT; +extern PFN_vkCmdSetAlphaToOneEnableEXT vkCmdSetAlphaToOneEnableEXT; +extern PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT; +extern PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT; +extern PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT; +extern PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT; +extern PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT; +extern PFN_vkCmdSetPolygonModeEXT vkCmdSetPolygonModeEXT; +extern PFN_vkCmdSetRasterizationSamplesEXT vkCmdSetRasterizationSamplesEXT; +extern PFN_vkCmdSetSampleMaskEXT vkCmdSetSampleMaskEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3)) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) +extern PFN_vkCmdSetTessellationDomainOriginEXT vkCmdSetTessellationDomainOriginEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && (defined(VK_KHR_maintenance2) || defined(VK_VERSION_1_1))) || (defined(VK_EXT_shader_object)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) +extern PFN_vkCmdSetRasterizationStreamEXT vkCmdSetRasterizationStreamEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_transform_feedback)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_transform_feedback)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) +extern PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT; +extern PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT vkCmdSetExtraPrimitiveOverestimationSizeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_conservative_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_conservative_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) +extern PFN_vkCmdSetDepthClipEnableEXT vkCmdSetDepthClipEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_enable)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_enable)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) +extern PFN_vkCmdSetSampleLocationsEnableEXT vkCmdSetSampleLocationsEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_sample_locations)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_sample_locations)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) +extern PFN_vkCmdSetColorBlendAdvancedEXT vkCmdSetColorBlendAdvancedEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_blend_operation_advanced)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_blend_operation_advanced)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) +extern PFN_vkCmdSetProvokingVertexModeEXT vkCmdSetProvokingVertexModeEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_provoking_vertex)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_provoking_vertex)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) +extern PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT; +extern PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_line_rasterization)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_line_rasterization)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) +extern PFN_vkCmdSetDepthClipNegativeOneToOneEXT vkCmdSetDepthClipNegativeOneToOneEXT; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_EXT_depth_clip_control)) || (defined(VK_EXT_shader_object) && defined(VK_EXT_depth_clip_control)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) +extern PFN_vkCmdSetViewportWScalingEnableNV vkCmdSetViewportWScalingEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_clip_space_w_scaling)) || (defined(VK_EXT_shader_object) && defined(VK_NV_clip_space_w_scaling)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) +extern PFN_vkCmdSetViewportSwizzleNV vkCmdSetViewportSwizzleNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_viewport_swizzle)) || (defined(VK_EXT_shader_object) && defined(VK_NV_viewport_swizzle)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) +extern PFN_vkCmdSetCoverageToColorEnableNV vkCmdSetCoverageToColorEnableNV; +extern PFN_vkCmdSetCoverageToColorLocationNV vkCmdSetCoverageToColorLocationNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_fragment_coverage_to_color)) || (defined(VK_EXT_shader_object) && defined(VK_NV_fragment_coverage_to_color)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) +extern PFN_vkCmdSetCoverageModulationModeNV vkCmdSetCoverageModulationModeNV; +extern PFN_vkCmdSetCoverageModulationTableEnableNV vkCmdSetCoverageModulationTableEnableNV; +extern PFN_vkCmdSetCoverageModulationTableNV vkCmdSetCoverageModulationTableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_framebuffer_mixed_samples)) || (defined(VK_EXT_shader_object) && defined(VK_NV_framebuffer_mixed_samples)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) +extern PFN_vkCmdSetShadingRateImageEnableNV vkCmdSetShadingRateImageEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_shading_rate_image)) || (defined(VK_EXT_shader_object) && defined(VK_NV_shading_rate_image)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) +extern PFN_vkCmdSetRepresentativeFragmentTestEnableNV vkCmdSetRepresentativeFragmentTestEnableNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_representative_fragment_test)) || (defined(VK_EXT_shader_object) && defined(VK_NV_representative_fragment_test)) */ +#if (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) +extern PFN_vkCmdSetCoverageReductionModeNV vkCmdSetCoverageReductionModeNV; +#endif /* (defined(VK_EXT_extended_dynamic_state3) && defined(VK_NV_coverage_reduction_mode)) || (defined(VK_EXT_shader_object) && defined(VK_NV_coverage_reduction_mode)) */ +#if (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) +extern PFN_vkGetImageSubresourceLayout2EXT vkGetImageSubresourceLayout2EXT; +#endif /* (defined(VK_EXT_host_image_copy)) || (defined(VK_EXT_image_compression_control)) */ +#if (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) +extern PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT; +#endif /* (defined(VK_EXT_shader_object)) || (defined(VK_EXT_vertex_input_dynamic_state)) */ +#if (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) +extern PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR; +#endif /* (defined(VK_KHR_descriptor_update_template) && defined(VK_KHR_push_descriptor)) || (defined(VK_KHR_push_descriptor) && (defined(VK_VERSION_1_1) || defined(VK_KHR_descriptor_update_template))) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) +extern PFN_vkGetDeviceGroupPresentCapabilitiesKHR vkGetDeviceGroupPresentCapabilitiesKHR; +extern PFN_vkGetDeviceGroupSurfacePresentModesKHR vkGetDeviceGroupSurfacePresentModesKHR; +extern PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_surface)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +#if (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) +extern PFN_vkAcquireNextImage2KHR vkAcquireNextImage2KHR; +#endif /* (defined(VK_KHR_device_group) && defined(VK_KHR_swapchain)) || (defined(VK_KHR_swapchain) && defined(VK_VERSION_1_1)) */ +/* VOLK_GENERATE_PROTOTYPES_H */ + +#ifdef __cplusplus +} +#endif + +#endif + +#ifdef VOLK_IMPLEMENTATION +#undef VOLK_IMPLEMENTATION +/* Prevent tools like dependency checkers from detecting a cyclic dependency */ +#define VOLK_SOURCE "volk.c" +#include VOLK_SOURCE +#endif + +/** + * Copyright (c) 2018-2024 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ +/* clang-format on */ diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 14d8887..62c6907 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -1,5 +1,4 @@ #include "devicelibrary.h" -#include namespace device_libs { @@ -15,7 +14,9 @@ struct SwapChainSupportDetails { std::vector presentModes; }; const std::vector deviceExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, +}; SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { /* Swap chains are weird ngl, it's another one of those Vulkan platform diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index fe7d6ef..f98d6c7 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -74,11 +74,15 @@ void createInstance() { } void initVulkan() { + // Initialize volk and continue if successful. + volkInitialize(); // Initialize vulkan and set up pipeline. createInstance(); + volkLoadInstance(vulkaninstance); device_libs::DeviceControl::createSurface(vulkaninstance, Global::window); device_libs::DeviceControl::pickPhysicalDevice(vulkaninstance); device_libs::DeviceControl::createLogicalDevice(); + volkLoadDevice(Global::device); device_libs::DeviceControl::createSwapChain(Global::window); device_libs::DeviceControl::createImageViews(); buffers_libs::Buffers::createDescriptorSetLayout(); @@ -96,11 +100,14 @@ void initVulkan() { buffers_libs::Buffers::createDescriptorSets(); graphics_pipeline::Graphics::createCommandBuffer(); render_present::Render::createSyncObject(); + render_present::Render::init_imgui(vulkaninstance); } void mainLoop() { while (!glfwWindowShouldClose(Global::window)) { glfwPollEvents(); + + render_present::Render::drawImGui(); render_present::Render::drawFrame(); } vkDeviceWaitIdle(Global::device); diff --git a/src/entrypoint.h b/src/entrypoint.h index d4a1de9..bcad76a 100644 --- a/src/entrypoint.h +++ b/src/entrypoint.h @@ -1,23 +1,25 @@ -#include "graphics/graphicspipeline.h" -#include "graphics/render.h" +#pragma once #include "global.h" +#include "graphics/graphicspipeline.h" #include "graphics/model.h" +#include "graphics/render.h" #include "graphics/texture.h" class EntryApp { - public: - static EntryApp& getInstance(); - void initialize(); - bool isInitialized() const; - void run(); - void setFramebufferResized(bool frame); - bool getFramebufferResized() const; - private: - EntryApp(); - - EntryApp(const EntryApp&) = delete; - void operator=(const EntryApp&) = delete; +public: + static EntryApp &getInstance(); + void initialize(); + bool isInitialized() const; + void run(); + void setFramebufferResized(bool frame); + bool getFramebufferResized() const; - bool framebufferResized; - bool initialized; +private: + EntryApp(); + + EntryApp(const EntryApp &) = delete; + void operator=(const EntryApp &) = delete; + + bool framebufferResized; + bool initialized; }; diff --git a/src/global.h b/src/global.h index 7546799..1e74944 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,7 @@ #pragma once -#include + +#define VK_NO_PROTOTYPES +#include "volk.h" #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index 06f6676..922d217 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -340,4 +340,5 @@ void Buffers::createDescriptorSets() { void Buffers::destroyDescriptorPool() { vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr); } +VkDescriptorPool Buffers::getDescriptorPool() { return descriptorPool; } } // namespace buffers_libs diff --git a/src/graphics/buffers.h b/src/graphics/buffers.h index c638c12..9df3eb7 100644 --- a/src/graphics/buffers.h +++ b/src/graphics/buffers.h @@ -1,24 +1,28 @@ #pragma once +#include "../devicelibrary.h" #include #include -#include "../devicelibrary.h" namespace buffers_libs { - class Buffers { - public: - static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buffer, VkDeviceMemory& bufferMemory); - static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags); - static void createIndexBuffer(); - static void createVertexBuffer(); - static void destroyBuffers(); - static VkBuffer getVertexBuffer(); - static VkBuffer getIndexBuffer(); - static void createDescriptorSetLayout(); - static void createUniformBuffers(); - static void updateUniformBuffer(uint32_t currentImage); - static void destroyUniformBuffer(); - static void createDescriptorPool(); - static void createDescriptorSets(); - static void destroyDescriptorPool(); - }; -} +class Buffers { +public: + static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, + VkMemoryPropertyFlags props, VkBuffer &buffer, + VkDeviceMemory &bufferMemory); + static uint32_t findMemoryType(uint32_t typeFilter, + VkMemoryPropertyFlags flags); + static void createIndexBuffer(); + static void createVertexBuffer(); + static void destroyBuffers(); + static VkBuffer getVertexBuffer(); + static VkBuffer getIndexBuffer(); + static void createDescriptorSetLayout(); + static void createUniformBuffers(); + static void updateUniformBuffer(uint32_t currentImage); + static void destroyUniformBuffer(); + static void createDescriptorPool(); + static void createDescriptorSets(); + static void destroyDescriptorPool(); + static VkDescriptorPool getDescriptorPool(); +}; +} // namespace buffers_libs diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index a551b35..58f2ba2 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -1,6 +1,7 @@ #include "graphicspipeline.h" +#include "imgui.h" +#include "imgui_impl_vulkan.h" #include "texture.h" -#include namespace graphics_pipeline { @@ -353,6 +354,8 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, vkCmdDrawIndexed(commandBuffer, static_cast(Global::indices.size()), 1, 0, 0, 0); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer); + vkCmdEndRendering(commandBuffer); const VkImageMemoryBarrier2 prePresentImageBarrier{ diff --git a/src/graphics/model.h b/src/graphics/model.h index 7714442..41dcd89 100644 --- a/src/graphics/model.h +++ b/src/graphics/model.h @@ -1,3 +1,4 @@ +#pragma once #include "../global.h" #define TINY_OBJ_IMPLEMENTATION @@ -7,8 +8,8 @@ #include namespace modellib { - class Model { - public: - static void loadModel(); - }; -} +class Model { +public: + static void loadModel(); +}; +} // namespace modellib diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index d72d025..aecdd1b 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -1,14 +1,23 @@ #include "../devicelibrary.h" #include "../entrypoint.h" +#include "buffers.h" #include "graphicspipeline.h" #include "render.h" #include "texture.h" +#include +#include #include + +#include "imgui.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_vulkan.h" + namespace render_present { std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; +VkDescriptorPool imGuiDescriptorPool; void recreateSwapChain() { int width = 0, height = 0; @@ -59,7 +68,8 @@ void Render::drawFrame() { /*VkCommandBufferResetFlagBits*/ 0); graphics_pipeline::Graphics::recordCommandBuffer( Global::commandBuffers[Global::currentFrame], imageIndex); - + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), + Global::commandBuffers[Global::currentFrame]); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -106,6 +116,37 @@ void Render::drawFrame() { Global::currentFrame = (Global::currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT; } +void Render::drawImGui() { + + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + // 2. Show a simple window that we create ourselves. We use a Begin/End pair + // to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Agnosia Debug"); // Create a window called "Hello, world!" and + // append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use + // a format strings too) + ImGui::SliderFloat("float", &f, 0.0f, + 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + + if (ImGui::Button("Button")) // Buttons return true when clicked (most + // widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", + 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } + ImGui::Render(); +} #pragma info // SEMAPHORES // Synchronization of execution on the GPU in Vulkan is *explicit* The Order of @@ -171,4 +212,67 @@ void Render::cleanupSwapChain() { vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr); } +void Render::init_imgui(VkInstance instance) { + auto load_vk_func = [&](const char *fn) { + if (auto proc = vkGetDeviceProcAddr(Global::device, fn)) + return proc; + return vkGetInstanceProcAddr(instance, fn); + }; + ImGui_ImplVulkan_LoadFunctions( + [](const char *fn, void *data) { + return (*(decltype(load_vk_func) *)data)(fn); + }, + &load_vk_func); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + // TODO + ImGuiIO &io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + + ImGui::StyleColorsDark(); + + ImGui_ImplGlfw_InitForVulkan(Global::window, true); + + VkDescriptorPoolSize ImGuiPoolSizes[]{ + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}, + }; + VkDescriptorPoolCreateInfo ImGuiPoolInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = ImGuiPoolSizes, + }; + if (vkCreateDescriptorPool(Global::device, &ImGuiPoolInfo, nullptr, + &imGuiDescriptorPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create ImGui descriptor pool!"); + } + + VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = device_libs::DeviceControl::getImageFormat(), + .depthAttachmentFormat = texture_libs::Texture::findDepthFormat(), + }; + + ImGui_ImplVulkan_InitInfo initInfo{ + .Instance = instance, + .PhysicalDevice = Global::physicalDevice, + .Device = Global::device, + .QueueFamily = Global::findQueueFamilies(Global::physicalDevice) + .graphicsFamily.value(), + .Queue = Global::graphicsQueue, + .DescriptorPool = imGuiDescriptorPool, + .MinImageCount = Global::MAX_FRAMES_IN_FLIGHT, + .ImageCount = Global::MAX_FRAMES_IN_FLIGHT, + .MSAASamples = VK_SAMPLE_COUNT_1_BIT, + .UseDynamicRendering = true, + .PipelineRenderingCreateInfo = pipelineRenderingCreateInfo, + }; + + ImGui_ImplVulkan_Init(&initInfo); + +} // namespace render_present } // namespace render_present diff --git a/src/graphics/render.h b/src/graphics/render.h index ffd7112..975d6c7 100644 --- a/src/graphics/render.h +++ b/src/graphics/render.h @@ -1,13 +1,14 @@ #pragma once #include "../global.h" - namespace render_present { class Render { - public: - static void drawFrame(); - static void createSyncObject(); - static void destroyFenceSemaphores(); - static void cleanupSwapChain(); - }; -} +public: + static void drawFrame(); + static void createSyncObject(); + static void destroyFenceSemaphores(); + static void cleanupSwapChain(); + static void init_imgui(VkInstance instance); + static void drawImGui(); +}; +} // namespace render_present diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index 9d63d6c..052ff2a 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -1,8 +1,7 @@ #include -#include #define STB_IMAGE_IMPLEMENTATION -#include #include "texture.h" +#include uint32_t mipLevels; @@ -12,369 +11,404 @@ VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; namespace texture_libs { - void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { - // This function specifies all the data in an image object, this is called directly after the creation of an image object. - VkImageCreateInfo imageInfo{}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent.width = width; - imageInfo.extent.height = height; - imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; - imageInfo.format = format; - imageInfo.tiling = tiling; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = usage; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.mipLevels = mipLevels; +void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, + VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, + VkMemoryPropertyFlags properties, VkImage &image, + VkDeviceMemory &imageMemory) { + // This function specifies all the data in an image object, this is called + // directly after the creation of an image object. + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = format; + imageInfo.tiling = tiling; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = usage; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.mipLevels = mipLevels; - if (vkCreateImage(Global::device, &imageInfo, nullptr, &image) != VK_SUCCESS) { - throw std::runtime_error("failed to create image!"); - } - - VkMemoryRequirements memRequirements; - vkGetImageMemoryRequirements(Global::device, image, &memRequirements); - - VkMemoryAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = buffers_libs::Buffers::findMemoryType(memRequirements.memoryTypeBits, properties); - - if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate image memory!"); - } - - vkBindImageMemory(Global::device, image, imageMemory, 0); + if (vkCreateImage(Global::device, &imageInfo, nullptr, &image) != + VK_SUCCESS) { + throw std::runtime_error("failed to create image!"); } - VkCommandBuffer beginSingleTimeCommands() { - // This is a neat function! This sets up a command buffer using our previously set command pool - // to return a command buffer to execute commands! - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = Global::commandPool; - allocInfo.commandBufferCount = 1; + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(Global::device, image, &memRequirements); - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer); + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = buffers_libs::Buffers::findMemoryType( + memRequirements.memoryTypeBits, properties); - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - return commandBuffer; + if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &imageMemory) != + VK_SUCCESS) { + throw std::runtime_error("failed to allocate image memory!"); } - void endSingleTimeCommands(VkCommandBuffer commandBuffer) { - // This function takes a command buffer with the data we wish to execute and submits it to the graphics queue. - // Afterwards, it purges the command buffer. - vkEndCommandBuffer(commandBuffer); - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; + vkBindImageMemory(Global::device, image, imageMemory, 0); +} - vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(Global::graphicsQueue); +VkCommandBuffer beginSingleTimeCommands() { + // This is a neat function! This sets up a command buffer using our previously + // set command pool to return a command buffer to execute commands! + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = Global::commandPool; + allocInfo.commandBufferCount = 1; - vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer); - } - void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { - // Copy 1 buffer to another. - VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer); - VkBufferCopy copyRegion{}; - copyRegion.size = size; - vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - endSingleTimeCommands(commandBuffer); - } - void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) { - // This function handles transitioning image layout data from one layout to another. - VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + vkBeginCommandBuffer(commandBuffer, &beginInfo); - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + return commandBuffer; +} +void endSingleTimeCommands(VkCommandBuffer commandBuffer) { + // This function takes a command buffer with the data we wish to execute and + // submits it to the graphics queue. Afterwards, it purges the command buffer. + vkEndCommandBuffer(commandBuffer); - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = mipLevels; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; - if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(Global::graphicsQueue); - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer); +} +void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { + // Copy 1 buffer to another. + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } else { - throw std::invalid_argument("unsupported layout transition!"); - } + VkBufferCopy copyRegion{}; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); - vkCmdPipelineBarrier( - commandBuffer, - sourceStage, destinationStage, - 0, - 0, nullptr, - 0, nullptr, - 1, &barrier - ); + endSingleTimeCommands(commandBuffer); +} +void transitionImageLayout(VkImage image, VkFormat format, + VkImageLayout oldLayout, VkImageLayout newLayout, + uint32_t mipLevels) { + // This function handles transitioning image layout data from one layout to + // another. + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - endSingleTimeCommands(commandBuffer); - } -void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { - //This handles copying from the buffer to the image, specifically what *parts* to copy to the image. - VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - VkBufferImageCopy region{}; - region.bufferOffset = 0; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = mipLevels; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; - region.imageSubresource.layerCount = 1; + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && + newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - region.imageOffset = {0, 0, 0}; - region.imageExtent = { - width, - height, - 1 - }; - - vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - endSingleTimeCommands(commandBuffer); - } - VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { - for(VkFormat format : candidates) { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, format, &props); - - // Do we support linear tiling? - if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { - return format; - // Or do we support optimal tiling? - } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { - return format; - } - } - throw std::runtime_error("failed to find supported depth buffering format!"); - } - bool hasStencilComponent(VkFormat format) { - return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; - } - void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth, int32_t textureHeight, uint32_t mipLevels) { - // Check if image format supports linear blitting - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, imageFormat, &formatProperties); - - if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { - throw std::runtime_error("texture image format does not support linear blitting!"); - } - - VkCommandBuffer commandBuffer = beginSingleTimeCommands(); - - // Specify the parameters of an image memory barrier - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.image = image; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - - int32_t mipWidth = textureWidth; - int32_t mipHeight = textureHeight; - - for(uint32_t mip = 1; mip < mipLevels; mip++) { - barrier.subresourceRange.baseMipLevel = mip - 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - - vkCmdPipelineBarrier(commandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, - 0, nullptr, - 0, nullptr, - 1, &barrier); - - VkImageBlit blit{}; - blit.srcOffsets[0] = { 0, 0, 0 }; - blit.srcOffsets[1] = { mipWidth, mipHeight, 1 }; - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.srcSubresource.mipLevel = mip - 1; - blit.srcSubresource.baseArrayLayer = 0; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0] = { 0, 0, 0 }; - blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.mipLevel = mip; - blit.dstSubresource.baseArrayLayer = 0; - blit.dstSubresource.layerCount = 1; - - vkCmdBlitImage(commandBuffer, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit, VK_FILTER_LINEAR); - - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - vkCmdPipelineBarrier(commandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, - 0, nullptr, - 0, nullptr, - 1, &barrier); - - if (mipWidth > 1) mipWidth /= 2; - if (mipHeight > 1) mipHeight /= 2; - } - barrier.subresourceRange.baseMipLevel = mipLevels - 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && + newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - vkCmdPipelineBarrier(commandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, - 0, nullptr, - 0, nullptr, - 1, &barrier); - - endSingleTimeCommands(commandBuffer); - + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + throw std::invalid_argument("unsupported layout transition!"); } -// -------------------------------- Image Libraries ------------------------------- // - void Texture::createTextureImage() { - // Import pixels from image with data on color channels, width and height, and colorspace! - // Its a lot of kind of complicated memory calls to bring it from a file -> to a buffer -> to a image object. - int textureWidth, textureHeight, textureChannels; - stbi_uc* pixels = stbi_load(Global::TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); - mipLevels = static_cast(std::floor(std::log2(std::max(textureWidth, textureHeight)))) + 1; - VkDeviceSize imageSize = textureWidth * textureHeight * 4; + vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, + nullptr, 0, nullptr, 1, &barrier); - if(!pixels) { - throw std::runtime_error("Failed to load texture!"); + endSingleTimeCommands(commandBuffer); +} +void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, + uint32_t height) { + // This handles copying from the buffer to the image, specifically what + // *parts* to copy to the image. + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = {0, 0, 0}; + region.imageExtent = {width, height, 1}; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + endSingleTimeCommands(commandBuffer); +} +VkFormat findSupportedFormat(const std::vector &candidates, + VkImageTiling tiling, + VkFormatFeatureFlags features) { + for (VkFormat format : candidates) { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, format, &props); + + // Do we support linear tiling? + if (tiling == VK_IMAGE_TILING_LINEAR && + (props.linearTilingFeatures & features) == features) { + return format; + // Or do we support optimal tiling? + } else if (tiling == VK_IMAGE_TILING_OPTIMAL && + (props.optimalTilingFeatures & features) == features) { + return format; } - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - buffers_libs::Buffers::createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); - - void* data; - vkMapMemory(Global::device, stagingBufferMemory, 0, imageSize, 0, &data); - memcpy(data, pixels, static_cast(imageSize)); - vkUnmapMemory(Global::device, stagingBufferMemory); - - stbi_image_free(pixels); - - createImage(textureWidth, textureHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); - copyBufferToImage(stagingBuffer, textureImage, static_cast(textureWidth), static_cast(textureHeight)); - - vkDestroyBuffer(Global::device, stagingBuffer, nullptr); - vkFreeMemory(Global::device, stagingBufferMemory, nullptr); - - generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, textureWidth, textureHeight, mipLevels); } - void Texture::createTextureImageView() { - // Create a texture image view, which is a struct of information about the image. - Global::textureImageView = device_libs::DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels); - } - void Texture::createTextureSampler() { - // Create a sampler to access and parse the texture object. - VkSamplerCreateInfo samplerInfo{}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - // These two options define the filtering method when sampling the texture. - // It also handles zooming in versus out, min vs mag! - samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC - samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC - - // These options define UVW edge modes, ClampToEdge extends the last pixels to the edges when larger than the UVW. - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + throw std::runtime_error("failed to find supported depth buffering format!"); +} +bool hasStencilComponent(VkFormat format) { + return format == VK_FORMAT_D32_SFLOAT_S8_UINT || + format == VK_FORMAT_D24_UNORM_S8_UINT; +} +void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth, + int32_t textureHeight, uint32_t mipLevels) { + // Check if image format supports linear blitting + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, imageFormat, + &formatProperties); - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(Global::physicalDevice, &properties); - // Enable or Disable Anisotropy, and set the amount. - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; - - // When sampling with Clamp to Border, the border color is defined here. - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - // Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1]. - // This is what should ALWAYS be used, because it means you can use varying texture sizes. - // Another TODO: Normalizing - samplerInfo.unnormalizedCoordinates = VK_FALSE; - // Compare texels to a value and use the output in filtering! - // This is mainly used in percentage-closer filtering on shadow maps, this will be revisted eventually... - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - - // Mipmaps are basically LoD's for textures, different resolutions to load based on distance. - // These settings simply describe how to apply mipmapping. - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerInfo.mipLodBias = 0.0f; - samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = VK_LOD_CLAMP_NONE; + if (!(formatProperties.optimalTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { + throw std::runtime_error( + "texture image format does not support linear blitting!"); + } - if (vkCreateSampler(Global::device, &samplerInfo, nullptr, &Global::textureSampler) != VK_SUCCESS) { - throw std::runtime_error("failed to create texture sampler!"); - } + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + // Specify the parameters of an image memory barrier + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + + int32_t mipWidth = textureWidth; + int32_t mipHeight = textureHeight; + + for (uint32_t mip = 1; mip < mipLevels; mip++) { + barrier.subresourceRange.baseMipLevel = mip - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + VkImageBlit blit{}; + blit.srcOffsets[0] = {0, 0, 0}; + blit.srcOffsets[1] = {mipWidth, mipHeight, 1}; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = mip - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = {0, 0, 0}; + blit.dstOffsets[1] = {mipWidth > 1 ? mipWidth / 2 : 1, + mipHeight > 1 ? mipHeight / 2 : 1, 1}; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = mip; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, + VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, + 0, nullptr, 1, &barrier); + + if (mipWidth > 1) + mipWidth /= 2; + if (mipHeight > 1) + mipHeight /= 2; } - void Texture::destroyTextureSampler() { - vkDestroySampler(Global::device, Global::textureSampler, nullptr); - vkDestroyImageView(Global::device, Global::textureImageView, nullptr); + barrier.subresourceRange.baseMipLevel = mipLevels - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + endSingleTimeCommands(commandBuffer); +} +// -------------------------------- Image Libraries +// ------------------------------- // +void Texture::createTextureImage() { + // Import pixels from image with data on color channels, width and height, and + // colorspace! Its a lot of kind of complicated memory calls to bring it from + // a file -> to a buffer -> to a image object. + int textureWidth, textureHeight, textureChannels; + stbi_uc *pixels = stbi_load(Global::TEXTURE_PATH.c_str(), &textureWidth, + &textureHeight, &textureChannels, STBI_rgb_alpha); + mipLevels = static_cast(std::floor( + std::log2(std::max(textureWidth, textureHeight)))) + + 1; + + VkDeviceSize imageSize = textureWidth * textureHeight * 4; + + if (!pixels) { + throw std::runtime_error("Failed to load texture!"); } - void Texture::destroyTextureImage() { - vkDestroyImage(Global::device, textureImage, nullptr); - vkFreeMemory(Global::device, textureImageMemory, nullptr); - } - VkFormat Texture::findDepthFormat() { - return findSupportedFormat( - {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, - VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT - ); - } - void Texture::createDepthResources() { - VkFormat depthFormat = findDepthFormat(); - VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent(); - createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory); - Global::depthImageView = device_libs::DeviceControl::createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1); - // Explicit transition from the layout of the image to the depth attachment is unnecessary here, since that will be handled in the render pass! - - - - } -// ---------------------------- Getters & Setters ---------------------------------// - uint32_t Texture::getMipLevels() { - return mipLevels; + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + buffers_libs::Buffers::createBuffer(imageSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory); + + void *data; + vkMapMemory(Global::device, stagingBufferMemory, 0, imageSize, 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(Global::device, stagingBufferMemory); + + stbi_image_free(pixels); + + createImage(textureWidth, textureHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, + textureImageMemory); + + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); + copyBufferToImage(stagingBuffer, textureImage, + static_cast(textureWidth), + static_cast(textureHeight)); + + vkDestroyBuffer(Global::device, stagingBuffer, nullptr); + vkFreeMemory(Global::device, stagingBufferMemory, nullptr); + + generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, textureWidth, + textureHeight, mipLevels); +} +void Texture::createTextureImageView() { + // Create a texture image view, which is a struct of information about the + // image. + Global::textureImageView = device_libs::DeviceControl::createImageView( + textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, + mipLevels); +} +void Texture::createTextureSampler() { + // Create a sampler to access and parse the texture object. + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + // These two options define the filtering method when sampling the texture. + // It also handles zooming in versus out, min vs mag! + samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC + samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC + + // These options define UVW edge modes, ClampToEdge extends the last pixels to + // the edges when larger than the UVW. + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(Global::physicalDevice, &properties); + // Enable or Disable Anisotropy, and set the amount. + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + + // When sampling with Clamp to Border, the border color is defined here. + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + // Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1]. + // This is what should ALWAYS be used, because it means you can use varying + // texture sizes. Another TODO: Normalizing + samplerInfo.unnormalizedCoordinates = VK_FALSE; + // Compare texels to a value and use the output in filtering! + // This is mainly used in percentage-closer filtering on shadow maps, this + // will be revisted eventually... + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + + // Mipmaps are basically LoD's for textures, different resolutions to load + // based on distance. These settings simply describe how to apply mipmapping. + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; + + if (vkCreateSampler(Global::device, &samplerInfo, nullptr, + &Global::textureSampler) != VK_SUCCESS) { + throw std::runtime_error("failed to create texture sampler!"); } } +void Texture::destroyTextureSampler() { + vkDestroySampler(Global::device, Global::textureSampler, nullptr); + vkDestroyImageView(Global::device, Global::textureImageView, nullptr); +} +void Texture::destroyTextureImage() { + vkDestroyImage(Global::device, textureImage, nullptr); + vkFreeMemory(Global::device, textureImageMemory, nullptr); +} +VkFormat Texture::findDepthFormat() { + return findSupportedFormat( + {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT}, + VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); +} +void Texture::createDepthResources() { + VkFormat depthFormat = findDepthFormat(); + VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent(); + createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, + Global::depthImageMemory); + Global::depthImageView = device_libs::DeviceControl::createImageView( + Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1); + // Explicit transition from the layout of the image to the depth attachment is + // unnecessary here, since that will be handled in the render pass! +} +// ---------------------------- Getters & Setters +// ---------------------------------// +uint32_t Texture::getMipLevels() { return mipLevels; } +} // namespace texture_libs diff --git a/src/main.cpp b/src/main.cpp index 14196ef..dc5daf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include "entrypoint.h" +#define VOLK_IMPLEMENTATION int main() { EntryApp::getInstance().initialize(); try {

juRpxWxiiMK7()!~+r{?n2+B~866;Z# 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 F{{h9a*YN-V literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/volk.h.58430F1E9A139B4F.idx b/.cache/clangd/index/volk.h.58430F1E9A139B4F.idx new file mode 100644 index 0000000000000000000000000000000000000000..cd66cf4b01c0909efd3e3df847fc55d9203ca9e9 GIT binary patch literal 124198 zcmYgX1yEG)+udDii3O2Hy1PrIML|-Kk`@7_5d}d)8tD${2I-OxNlEEYKtSmb5RiuN z{`Sg$KWCgX!<;!!zRw#MG#);9K(h>m+%tRVVr%~TDLMoKAp-utdhKXAzzczpib5eS zDP11cuWr>=W=O5-l=tOZ5$UK_F3#zgw^BVJkQa^4O{`3v<1Hn`4yvO3x;ubp*c;`*>2q z`nktT(FT&Q24=MU|c@9@AQC_a%s=Tt8+R4eCu+ zw2;+(DXix$d~(!OZ(nFPDOy##Q&#GE`n&PlT*H<@YEghwRm1vo;b)vO&O;XcS%t?0 z^y)KQPo@T^6Drd^j9=%K4w-9VD>CEeb31!fv7c%Xdt5%9tgia=w8p+LzrSHiX(Avn zYsu!_sh#taA(|(HdOXGvev9u!_4g7!3u%OZyC_Y!4SxPa{|k-tL~-%+CSJcp>V#B@ zq(U>IJ97c?aWyLwHdRHZj`|f(jO}!fVzy>Pi$x`LCk>6ql|*K$YKVMZH*VKhx*0{k zacQ-vD|$z~eO8vG_qe6#N@)jYPu26wsiFRx;J(ekA%7;(=GlgDSNhZLQEz@$`SW*0 z`0a)+Lo}2lqTHNI;*j=a?Mw8fv)!}&uCuYR{Vr3MagqIG zt-neVJnH7O-Mz8L=a-NF)VRB7eD_o8P1a(YXmd>!`~IZX&Ur%dHGSJ@=Cbc-S%?vd zzelmvL^E+LI_!(W%_WvqhdnyXu%vd*Ckg}6wJJlNTFZ*LpBoH6CVeJ@6jwbSJTn+? zCVxAE(59aLZT!CP zHMCqUe%bM}SXNZFaZC5S>+0t0WcQjs=hy7`K@?t`YF?*cvun9mz)0QuZ~>O3qjzT! z#-7d1E@zfkoo@v1z3UgeD(qv{8%OWv z=*=yucPSU!QY*wdmpcWIQo1?W>Al&g?yz-5RC$FJo|&=d|9aARo3qhYOFaF`HBjCD zTf(Jd$LhxU(fp75kDx2@pWHER{9fd%Ts)HetyjOxGyfyi*fqy?|B<%)x`A*%>*s<( z$`)PMJ;d{>x6N)Y3o+gI(kkq}#m}@@k`*6CP1%f94`J#aEomac~||7uC~u z2a4s~d3MzJ<;dfSlTMdJk*trD^MEs^kD+6l@iYflrd1<%15a?`7yeRj^4{Etwho3j zw(4(8p#~26XDzyt{u+IkQnuPMu96~ldPVh)E%nbiOXC9P7#A-s~&zf0*QVw#2VX=SnRi3T0{@n@TAX)z~ zVh2gsj^A8R{QGV!G>T1byOO_o=5r7$Nxu$*A`NyOm{7h+om93GxiEjr@QK39<)$s7 zdS`m-l3S{r+SRNc##~dk?4+D&R;oWHOGPfqPUCQeyyEb>Wi-<*G+z@IMY=9$ErBMZblOxAdGM_;gBh1;^D0xv#S9+e-NE>>4Sa$=%c>$b zE>adbF}p{k7mn1jM*AIvh%Sv6ejlk#zW)@wbkf^C4r)3SMR*Aqu-&v(ij;0}CSbp7 zkKupN(?bqT-`apm;P@eSBa63XtnC`3a=Hwf9-`_qs+?f0dO!poKW>KyZ znG@3aJ!f2E&4_*CYBbjG$6KS$dDLWt_*O@?{iF_47Pm61SRXy@%RE)L*wE`Y2^Tt(miv#n4l0 zRjSnPo~bZ7Rc#TM*04po!rQi32g_>0!Z$Xb^uxzFEb#1iOl*@@6w_aO6dGy;;V5Lx zGyYLHXyCd-c_(6&6B_97TD?W3te}tSJlx46wnAQ}QmW)wlcqD_*;?(Ex++Tx%S1a9 zc72y#^_In5F9uxvPYyY@CP8=%9qrR9ul9F`u_Kfi3>8@1TTR&?d~RV_VmYpSQD14` z*ma=Lv-)dY+eLHS?5~L5{W@x{op+Jucu1229OZP0uTy2-FzMbOjcgK3m zXnJy_Dj#is_GYrXx1g2Z%&03mEWYDr-A(hC6+?eZ^T*W0L2Ci|+9jLlg@dL|zu3obD?Z z(qi7l(pT~^jduNa^_b5Lp2vvB9WRz3fd=aucOn;en(lzEWRLyM)u=y|8d+H%GnIWa ztog&XV#%?s4@a4ks4}siNbJXSL@U|ORnKz0y=mQ-Zv1y`k&CrT#aaD(|Jz2iym&Dj z9ts(r6k;mgTC6Ir)X~3_B^$T(>*rZ$RYVL3)h!|>rHPfv2Y3g04(Cfn4_-TG8z@i_KSMgJ6^Up0U7 zeF`%``7bGNNMT-u$FD8WMhpCmF#YeRdqp z%jwKAY1OY?N`XzrNI^#PwWBOH(mJ#Q{r4Zdp_ZM={Q4V`Zr@|1wz?B`e7l)~iRvZe zg@-TK!>uW7>(MCxhox<+A^yv4pw>vZ8 zzT-p`PhBv^ah&u?W=f2)o`6~`H7AnTzqN~HzIY7Zqi7~?bSU=fANHjTe}bbu5a+}1Jn1G zpAqZL6?7d-KnNjLfrJ5(;r!ROTYG%a^y4q6*AQ2T))DiH*84yi&4_S_XYEz50zs&ulpxr zVvM>OSuUo%fnjy;qDQ5Yc1w>ZYg76rWYlL#B51rdyk*-mRoEWcT>bdG^72!MTH~{` z?Hh5aYDsWg4mhTEO+esGu*?np!22g|f%k7DV|{jQ4D80Uqm5*#d8eDShH1+Fo|@_N ziN1og^ly4bO+PLZvJt*I!WTDYvGLfZafD!nui!ZH9&(xb962MxLYnK8{261y{zr=a zzKU8`RIiD1*CmLB4XR7?qhubt+m^L?=S?~kzu}M6Zt@k9Av-Rd^2!ZH__1nMwjKWs z^bS!@36IpH(erobO^wmsqhKg*aJQ4CU+B;(bt9XI`61qU znq$0@8k8~~PQciE|I_B=RF`wV_J+ej>B7dess4l30aEy%`Twlf?E zU{9V^E1K<@q^Y&O{4>9l{kEvo)U2VIC^5wGb)spS?7hO{TgM#64poTm6QI8jr5 z|6EmNyL0oDOtaA-Jx2kWQu&x_z%`JyjQYLjby7I z-$PgXPctc2!*}W#ggD*``gd>Q+_wJsiBV$I!_`>ZeCMcwO-QmNa2*~`hC$0Q!fy1c zq_o*QU<^Y_4=!rLK%xF2OLL0A-G{6)(>z7fe!juaLF7>m-RZM}#|&ot0U;$WkE44; z9#EOLG_KlOId5wuJECX)WT}a-oEH=P)<;) zN0G16j@tdpJ38<1&?>$V&a$I0{7UF^B8Fkr%A`1FL!{MC_kq_WmWNnynC#i{ZF&!B zqDe;luX@5_##nNcjZy8)N4aE+>wG%?bvy1WD>%mZFEH0jFosOfLW`aJN!_kWaH_+U z#ROr=dCzP1ASEPORo&{Ao?^f4x5M5(=3Jbgoo%XziC~KPO*`Ql8^LeBJ}&Q0t`j2p zo7}K?xS`77bEk`QRH*O8Ox0$VeLrthsWK17U3@a14S~GY$BInEJwMahbyEUo8it3; ze8=nrJY-2UIP<3#qF6QQ5{fiB**h$PpF}7(*vLgePh>ZvyNBel1331+rz(<_jk8h} zio3%^$LW`(%?agm^FCsksdG3Q;HC3$$ViK6*25pH=ltT!YU7u1==TZ0A9z{PvYkDU zsZO!u>o=MI$?bC>vv{SiX?tb%&6rO0MavE7li1W4;Z&?(xn)WYL8esxIq!8hLr;F_ zFX~!{oqjzD`w%sH4~_fs!QSkOQ3QSDkr9tKEz>{iJPs#%K=6}Ec=`OZ-y4<3VQQGl z!ox)zTO1`ZOeJ@;E-E-XDUO1-pTt#b^;TX|bsVe($m@P)(rcJQ7m@oY&H1#~aMJ3o zKY{42`9fU9F!&0*3%h))t~&>Nu#4i)r!1!hLi}`e#Np1?hhiR)ufI=&(HVX!bXK(Q zq6LRB++<#TbO%N-^4r{3a%q=-k%dX2CL>(Fj@9rZ8r+f}LcJHO8(E1ZX|y@EYTotr z^-qec7g^^7tlXSGs_!=!1;_JZ(sHLUJj2n9Xqi-!fa>)>n(uOF<5&}97^#!Ix0njo z3arx#w2o6)+<9EvMxV5qD4V`K{CBz$tg? z8E2)OAIgSG%*0q$a)J-ozt3IdZQbihfw)e%ycync5e*{8^zb6$k((k88JAS1)vw4B z4EYi8_osv!5r)wtgqmdqeeNqW%i`K5ll#=0ACq6dhwRWYCAv!xb#5to8K_hH9rJSV z4L@Y@{W`#um1atazQ#NWYdkqAov!XN-|>$PO0Lmz8$31jukL9-ogZ)Hy{k_xTWUmn z$lj6c_f*r)Z>FDp662F4FGKR*EO)<4807}0zKZ3!ERKY94D?ih%sU0EZw`B8nT5)c zV588@I05_4u1E7;HHDYga=y{S`@cHI3NXJp!G)d8>4c!Q<|F8n<!>5b(P2NE zhJ7;?ATbp?{{2T^t~6KXaCryQug<27jk(@k%t(E`dWy^S&f~E59oOa4M9+p-H#!X4 zcuXpGjIAhh?n}-!yC3306VAfR89jTR6JwrZyU8hk>PY-H{P|b;JsCeD-T3utv{(x~ zV~*lI39I>5p|qW}+N~@O8sh|yFSja1?+5N+h3l>|myR@+E0ig4zWaJ=Zm!T2=#f&R z_*`L55&qYa%@z@^K>738FPqKbRz_YI{~TW9Z9($-Uie<2+T)q9)r8@FMnQfyY@*$i0xD}nrnpH_qUXU(;UqTnz5u?yal~} zTplr$Sd5h|mSGm0^WN^q03m70!(O^HB)~XFZr`Zq);tMDu;eTew$&M+RwWI;@6GVhd2c zTE2%bm1e~4Loo{z2nP4>>MEu(%e71|;5>jRp%J7rnDc$wc?^&I`@AD$SL)Rh+Ag|1 zF=p|^J`>fhk7C&Z(i{1oYB@$oC&CJu#PpxT1=luvlek+x#af4Sm2mTRPJhsw|N1o; z_ZL>vrFAZafLxc^2`97O`H>ot*z`$}`$>qA0eNyZW#@aApt0fJ!*5IV%*@=c)b5nO ze!#zJi2f{rYaki&@E9FZ5uIZENs#a7L%M3dybOvYqx>aj0mLygR7G$Uqu*VNuE(V}JJJD0n^pj^%v`uK8< zP06Tn=GlYqbOe8foW;5r&WZAEllX!j$dBWGV8ywdj@S;Zo`|T=NFHF1G0FIZRfj*Y z5t(1k_?3N-Z^&h^9`czaD5fu`Z=8@dU-tErr;_zKRvz~sDVy>XHw>)M(Y`T?6Z6mt zFQYSy+CgJV$&6Vpzb%o`ek@pWWg;0lKr?$X6g0~D>B9rZqSqf3D&rVcpG6zLoc_LK zHD{=(noB1U<|e@Z)DzwPckq{wHcePm@+N{>!ews{-=d-v9Uq3+kD9ePq&mx?3WcMx6G(N*bclTy@E0I7hA|$n-LPOHB@%bm}|SP zW_=|gy;J+DFgAIfkek1YSM50Eo>1|Fq^I(v9zK>>DO0W!-cyxC$_EQiwux!XRtmS8 zMdBfxRrgE@7H|HzLLZ2I87Laebb)RE<>-trqT)OCSr3%*OP9C z_TtJXG+W||b28E*)lEMLc#|qw`8}fI80EJ9K1}c37I#h{k*_NiaLUfez8V<;FreJy_gYLwjzj=oCNk7fREcDX7vp&pOJQJzn zt!J72lB6W#o{(f8{5YZF39OF%c)?C%#Zor;>c*#&;%x3{YwmX3(Q|%yscXLWw%+T+ z8$To*g2j$a-_qT(J{`jyxXI$LY&e#Prce|=1b`!{z8U z+OEb7f&N#{Qew4B7-m_j0OvX9A&HAHP3y>1L|%@%B}43A%Ou)e(ILyekIaHf^v}Bw zSnwk2-pjfV`i@<#s!^Zbg2l+S8B-*(Deivsu~8r2e7(QH^Ulb3>Yb~w+kEJU^3eSv z=fHWs#(KeTV;`bsF?07G9&YLIeqF@1uP0uxKY4LqlF$E#lN9Y&(bDhQlF?H<@}W8} zSfbmAer`Xzob5DL>bpg%QSisSha$i6v|EeHsN>*g%`M|o0S#<7)^T6jn~aI+3!5`9 zr2^I}-^p1<)@{CUkDGaV`^G8*(^NvryQcY<0RBn#-;L1ASuknu_N%0(~3jWK{wOPqbBz!rw`tU5Eu`#cE+fS z8?1gNLl=n0`=NlP@M=Wh?R~0`LS=^n6e)uJd9BXnp!gUZ^xo^4VR@iBgD+= z-_OIpls(ElU-jN_3iWg;3)ULxdsJL%#i?A(aaH{)(exRn?Lni>3t>D521)xBs^4hK z3u9s%ucRI-V+vli_XjpFFXsQ%NOk%CUbtAKE!jC&Luv0BPH5E;o9Y|Gd7DwrX$Rpp zyPA(n4CbpwNe_2$qDCsu3tMcvw=Lb`X;&*(pPlzC31@Ss@l0kD-x=!5)PA?ngUVaHCdoi_Nlh9#vdHWp*E|eBPK(-cUOcnYa`_#4#*CrcFP8 zYowD~@6p1>X&2{N6dW$#O!(tG_J`)#xMZ+d0Ha;<_%HW(=gV7_60c&t(}y>1++W>K z(zR3kI3jQlAN^IWyv@U3D?L{lugna}pi{|@cX2#`*ZQKkJ&W37s3(t7R2q$|%uVuL zbIX0HfyYWOeI1zR(Sqx4U5NZ(F(0}i#oe|Cb?-UovOGUbUvnEUD-vWA{ zhgjIR20QNM*IrOPXg=2N#hH_Q&#T_sNN0YraWz_8Z&TCzAV%t&d#7u2-v{(D#a=8y z4Zksq+cr5~U;?=@YJ9gCBb(7tX)%V@Ve z`XMN;l5ibzC+6n?p*Vr!FRxFyRPGh-U%bP9z4Z zrWh_c34swG9rs>Cbs#`THNkmSmqOR^XVD_2R})Q^&e9-2T46l$-GS9luDMM>UxiMQun| zsSg9WAlKSrKh_fk_fW}|@+2-sa~sM$_;YJkp;jHY@6UyeF@7zyGE$dUVE#B%{TXbx z&cdhl>We$^Ch&Fc&2e+UYy7d?VO-g=t89MQnaW0V_DN{;FXdlYpCBs)QlW&Z*q&^> z$AjbRDGypCZwF-`33p`g!YJ}up&jZfatWNTlr$E0N7UB1ZkeBQWW&`Lp%?2vzR)H- zmi>|iYqPXY^bGwjg{$@=>LPjs%C)AH-9|iekUlxa+yR|}VKOJP1CwPhlq-Qh zJnC=aP@s)KD~#+$zE9D=UHxEL~l2{U8vPK-i*Dg)6MP=zO-6A=)Pf0BaUA+ zoqtt1jqhsO&P^tTK z7Z3A!si*xQ!Fuu7u})^bpc58#>Ps!LYJr*zAG9S+xyMM> zNPce7CbXG&$?SS9=(80n3{f}bX(jZv1vyGh(j`^l43~X(^ANw-pyB)Dzdji8(wcbI zT!XzX|NFyx(22E4=QD9~n=o?xTdb`mb(rh8cDIQ;qT_n^4d+kqbCc5I3mC2KdSLG7 z1iOBz^Z$xP>Zwm9xr_a(YC~c_)Q)tZ0_t+(iRr7qzu-Cyi8aaj!HAK@cu*G^^;1ti zSkxf3^KMJ0X2f#bfuq68xvo!ZE}NK25uzb}McfS8Au)Bz#4C^ZE76brWfR4ldC=j% zmGEO2*G=w^W~xju63Q2KH;5Dza{XD@fMM{Nq%4&wJMZ*6OM1i^{w}$CM!mp> zwk%z38^1qDY`r|+AKJ9&@GR{sld1F1_$qF0l zTAXy21v7UxJbU$ow`eZ-qG?`Bttsb}Hvikp%q!c=kMAl(X)C0xMemGJ?O0%W_O}!y zs=P#}{|vvA_!>X5S1!|CzsV|IEy?@RnW-Ud2Li)NgZXI`!7@^CH{$n}PD6cP6JwaL zdv(w*yPV04eIDb4COzhxAm72gMQ^6+WiYY}yEpozEu&14WYWCLnxfL2nvU;zf+}Kv z-6GeGJ+!imc+nJH;iKL8{3*Ry-fJ4in_~Hyg;4S~<{XnmO;1%hWySrcx401R<6kb{ zl+9laSAiYUy~Hb8y^WpD_|#l#=7bvNvC%B@u`J~GdhHP_?GP{ZKkES} zpB=i3>}FnRHwK#Qq0d{hooWaM@ZB^#CP?(i^XtWY>ANhY9ufMMo`L2Uuv+{vKkHFk z^>v7*WDw>cj)tdnN-!kb!c{2LBc=VI>jjgzDF$o7nFIkuj-Gmv28zXnNA>cL*fH}n zX5}0=^sQIr4xPPnfkh|KMteS*w1ub7E`Ak%zJEs=A3EqjpCe4CRr5#?&8g>fXO_)k zVus)yWjXdkQ8)$`{i?>?cS^|5CL{F)rKq13SmcD-Y^;^rq*4cc63Pp!>Z~x9N(Z|w z;tR*HcR@Q@0XKBAF_V&ns<|Z_TUCov75v~NoRH*bNxhvoC|z;yy`FCTXL0;O(hJ}7 z;q8>nONXz@zMk4?LPUs}cw}-mtS3u$j`PMdakz*{l<6w&si@;6u!b;XIM|!ez}9u;V9SJY{V^Ot^wgW0ve#R>N|dpWaVp#SklTJ~dt z51Sb1cs#Vq_Qt!93C7zL`8(e#P$|Ejb%G=Y-r&?rr0X5GH@+%Z!2hqp@n^REHMUht zifrGNW2t>0Pzz68&_#v)tGeSpH0!8TA=b@yKZx>CZGFA8fM5&{`Gh*iu~B&= znCYG^@M-_(mQz$r;XCBF5c3tF%ZXtkxH0>XKZozvYjpnv_1m9A2v@g*Co}d~b}(|L zwc<=DAtoWV-#-9`*JJ7UU3C5IRXEe@{pKo4SwFZ=do5db4Fw+n+J=xT8xe}VsANZv9&T(yf1#ZdT57MH$qDT zeUW-k-0$<-p4mmowk+P-84eu$Q!2Vl1=x9o!J##ldi@vmCkULxA2?&iv>(mGt%Q=j z_8+I4R8I*?hj^0JTW6u4qxS?Odb@HX9yle_d$6{AnK|8zco2i99KSAxNtRuytS>~V zeSt_7GZIOXtm`tN_2eZMzYGrI zVM*tG!cL=`CUDQmrHc2uX%4#NW`CtksEueL1#(dOsGv0hJ{AM1wMo7-DLD)@3KIjO zadmkm+zWhDbBBpD8?|P;bnHj;EPmuN6C`-zRqXGtByQe+s_4O`_-V%Ja>o@??$jTZ zXIQ)@XhJcrO(Rom|0aheO`rFHS3CLU*ZTyt5bq1SI~d6^(sP(C<9)#&79KKhyk`_0 zdAKQ_XjQ@KO-)=6Cz42LnGxXHK4HG6W1R!ZUshyN*2J7bC`VwP9ka{un8ZPAP-c#>T+zy@1{b+SwfS#(hffPX8oz-$>^% zzWgdaI!!%amvQle8oVAk%~QIfK0M;z*_Z(&@d444{R^NnVMTg z)4LLaa-Q9vVA$!|ZRB2bkJe%RJ&|`LGZ5Kx24`X7d*$dCL9euM(1xFLe~~Qe&$hB? zfQ}&vz4s1VYOBImxr>Q!i+{5IhFDFN-0vaRUtb`L%F{B{BW?Li^m#!m8uES0)?QaC zT3oT0IQwzdI`P+zoQ+o*8r+=xU7nK*@CrA;3Y-t)JaF^ziJF?|C6 z{!Gn7^fjJTC&TD%_kOOO<+wbF5@%lp{lJ;9Tu=TEVw?P_F0xXp_(tvH_i46!UaHNS zP#4BB60E^zH*T|MvtZx+q_!5R?BpxGANoK0_wniyy_5 ze^oTFxg2eph`|%yZpTyLvrsa6cNR&jv)6K@$NJkmdKj~AtNJ*^H&=}m`$~x}le|lo zAO`zko+n*_d`=xa&hYOo^Y5;8bvMbg{}hdq-bzyXA-#X`l3np$;%o)%GnH(4V078v z2$iz~Hms;J!p#bI#RH)I&JnvmcXJa8 z?(EC@RYU;4hbd)wm$b|@mBP<{-u?S`ij0ZsMz#drO0x@9*BF|!7*kEo0Z&WMHK zM0)BiA(=qCk-F&pj20%x8Hb1_ z%h=4y=TrCRMRWj+V5${b& z9%OxF*IRgi78X39RwB9mD@Qkipw=lT?$`FMU2C|#o+g(-snCZ=;`qGKaj6?c$q!Zg z1mtLAdJ=O#FE#SCCYn^MdYq_Aux94_N#3R>x-BrlHdlbFWkB;vd2Jx?p;!9D~ z-a*RcQ&W=ItDuw0zuA{gd{g7atJSPrJVK3~M*eCCr*h!iZW3lMZFg`WPkw*cBiA1P z3~3oP!$||fz3(mU3Rf|*ycLjgt61vs0^cP*ltLX`)$Ry=G z(v`Qva6zH0~ z;h1Mmnrx77BYO98)9cR%cJjU!j^|NmeG{gPPnY&zSBxawTHJV~Xt`*wiEEmz7aOr_ zKp{K6P{2;gB=%U`>~ngkK6?5Te}SWKV2>}`-U_9mFwbGi3_yq;yVUJCHC^Q)2^1~<5HvdHvDAnM>pp<-a+1^HOFf& z6y=p04|ee#Df-?^Ej;o}``ys;O(BB~le|9t%|=&E0Da31hsQcYyq8i>_O!}hx!Rf@ ze1bsmiOKD>^`Q`aNCIvY2IK_Z3MFoZGF&4B4*dQ<@Xtwexy*Qv8=%GFA!!jXrU;nq z2^_?0nx0ecd~LoWKnuh{(n_J^rBIF&IEdFY%&=bV0bg4P0@8p0^#Q{R_*4t{To7#F z2cziy7|>HVhI}wO*04=Fz$-=Zeqz)7#O6cty3yN^yf^ZMFKMT90NyX0|LF}7a19VB zBY9`g3#|Y2ZtKZ%Mr|%Z@E~U>J#`Fq3J5kK9>yW!82%sYa;7muMd}a;VjWCLCk9<7 z#+?&5h}S_(w(H99lbAqQZromjM;SYiMTiPtnkdxAbnt!RMOfIz0C50kVHljQ^s z;x$dMOs1}Dvm2mY!jZHUEUFbOffG20*RbRCOfLtvqZ?kv|vv^`ixgqp3WF5M{DZS{}!$d98*a9MnCrIEaSv;d^pgU$y% zHmNFT_>dYfb#*YBI+(}_9K>t0dCxqSFK5yKx#s`6;=g|<@E|~EN^Jt0X5wEnQFkGI!>BM$&8zdq zz=!OEYUgOw=V(GFa1gK6uKo}WN>esrARr?sHD4SCUmP$cmVQVz4hHYxYBS@!G6TOFQlXCMzZaa)>hf z0h{&%HkgtyXfHA+U#RLr&H1DOUI~hKf<<|P#fCI{KN(5ezS+lYI%N;g!cYq*0)8t3 z&Vi)mLgSFU{=uKC!alhGFBBEVFoJIwp^4<>ptm8TKy%Pc+ch}`c;8SB@CAqW3yubo zw}+ODY!o4*fIoqf%2)`<7Zh(1M`#hp0Lhz2t4Hz{(fDTz%^b1tA@9Mglw(kpV*slH z#Oth}=U4t|bD9F=@^HaE0U9M2PMHe_qiun;Bi*}WUc?v1It%bF{>?Rjw~o!Qjtv%# zWC}@(K?HWX=EC6!$Q;VMSzL};Tm__ehiGX??=bi3wDr=s0Nx@hd%p=peiN7=c_;tI zjvv49V-g`bwlY+;2>U`ojl!o&FcRN zko$p(q6Hz)g3v`q@%LX8zhWgPn-gaNxie618A`eg1qK<2*K&4};)oIS{r{4JiXsA+ zHUbyy&=d4jq<1ae6#LV&P;3OG0A)4?P8|d1L7M#x9YMC<4?41Z>l7-0mx9s@!et4< z1!qb%dML7JrD+F-LGW5^d_ksOhU$Kz#npDw z2JmW8DQUxIY{R~Tti%v30?B(eH-l9#W{igqNe6=nKw}I*lRklicx|@*Ew*Y4j{_b8 z@)=dw8f?lMY;ZA7Lt~LSvAO-W^JPpDAeRY7F@VKAfTei?2k~0&zG5U=I_T1CN!2uTqL$N;Kfr#Nh< zIC98f%_@=3QTP4a;!m>!)INaD9bnQQU;-%y@mh_i3;6w!WoJOG66M?#4($~V*zA2U zU!-%>16?Hc-lG7o31t=dB2s`22B8V=WO#48h|yNZ0a_p`b>VQDa5#A0Nr$E*y?c>& z1eN0Z3GjwcW-sx%F7d$$unG%8?mjmfqcizU2=Nh+dek7Bz`Z+xYlRGU1g#6X7B76q zFZ6i_0NxOaHv?y#fy*L!t7v&hy_ve~FVf?`@$n&TV2&3tSQjyXK?dS=R!+~4qP!+f z0l7|;+%%kO`d`Ur(LN!~CiWe^5!<670Cpb~?+Tyi3jYz(Y%W?llE=N78%6e%4&e2n zc)#(ue&d1R{zMx=y666Tdhm=ujsPE$0=k!gNs)jF90WkTcJHyh%CEc#4M46KmE~AC zZ7dutS|@ZAnPrdiUy_*aF95l1Q0^BR{Vz122SB`*OU<>M2rGF9$j$%1Wrj^RgDr%N z;t0J3>79Cy@lzeLj{xrj%4`S$YX||DRQT~2LNphq+LR9T|)Uz;2>VpK5+IIw>6mDz=yPhK@3C5hM{aHa1gI)1(*ad zTKQKu5RgVxii`2si}4;HgBU|=KsrZ2kmP^=nJ2(oK=J(XB>eI0ki28GK_t&s74no| zCK=!zqB`^ooc;@31j!3SYd|K2zoKcUF#Z_eRiTpCk6`RafU~C`eFWJ8Hx^C4+;gBO zL_nrd3n&YZE(;G#vcoa5WFm6n%b8yv65<0F0puo7fyGjR1)OI=ypEP&!Y1AImOG&K z9_3vcoHp%W2V}s!kaB!VJF9Pp`T*Vns${KrBCUAf27e4~NAkWM=0{VSt^j6#gSCjp zWR1oI)&+>yX756JX&gc^ZvwX?aDdKZvd?1zhj$RKY3?ka-o8v^0%#Xt$yT7`EB{@h zK)j}@@`b4M4Lk&B?Vwr-jHCp{bOHzQnpSNs*nvr8c@qISLse`aUu+-W3|Wa7^iE{O za-?2X<|-5ba(!S78E7;aXu$pn;Fc!Rk%@opC@e1*}*wCQUFVkYW(8fQJE~Q%e(G?Tmq_}E^v7+aKWKnhPH+D?%>xn>@d*_;C(>x zj^Ir-i{nQ zXBZO`m?fux+z4p(97c5x0}d4+Udu5e&g>H^Kat`CCoNmK`So)mV0opl8tHxxm#*_mtsz|)1;V~pR6Jv=0v`FwMn+~H% z|L=+q;x)}lHazm!PLcwcI-qZtFvd$5P*xDHX(mh+5<9i>6u@NxmAN83jv_oT)X%U= zWT<@hepYOD#sIGawGsmfSON*a)BOx|8`I&jDExX3RW=;l;61YL3>J%SNpnUu;faZ1C}>5v>o|CISkT_Y>Lt$pjrrDTatj|a2v#f;N}CA<3Jc;uGmT#c_j?k?LfRXJ2z6B&(fCx$aSO4{=(z<^{)edKU_Udu@mkcSOE+5%`lK&#UjT+{zOw}5y}6aVo^tdbvt z8UYDGm0|~1a0eIMPqNYck)`l@5Ndnam~bl|ge;DhZ{kA8@Z zwd%!uCCfH54e%5UT8)8{#6W>_2Z+~lObqv=qnt$nxzDK7MGy!^{M(AB(c+LxHdZBI z7MH>u;C(?Y?NVHpQe4pNJS-i_yH8i5A##C9iw|i5*Wxrf)igRVib1?Kdxfv5`J5dK zpyh+X`e9M{VF61P#B17?AewVugefhsJE6KW7n?Ts-#Z$A8?u{lNJ-bqf13en9bl}( z=;XubKy!k4t!5@2rvK*X9H4oDMpI#AsW9NQ1L8FeBa6&y`J9Fh0X(Ar@0{R+&EbO$ zPO)ONZe)fX9GeB}Vub+SPgF{p;grq)p0iG2xkz51GRvr;pbj1I4ghXDp%_%5|GkcZ zcx^UQhRUb4R}Y{~f?d~(d8-!_*kM7urdczqLPA300ITz;u4};)Y{3J2{RsLQnG=VS zCTaZ>0(t~w7Zt@1IPVX*2J*D?9{nfMyZypUy!I#ofESG7jl#)C;Y>(g7Fr6DXLklG z(JB`Lc*7{(0RiU$0XXi~(DIQyuc?sloN#l1caHM!GlKdv0(>xvg>@tSGbk8aei)HX z54gc0 zfgoOcM?d+xN9JQXBLXsklB>h!sQcG>+y5rYQC0_}&V3BvWurPT2hN)F?;)TOwu5v} zc=yh!mf;*=_A6+%2%W149T=Y=UYl(j5HF=DUIA!jU@7{bWPSf#K0&;u(M9^E1oFQ4a7KV&huOK*=l4qz|?QyW|uc^Le87zQVB5U<@^87O~pgP)rj0U1ZR zmxWK4g%2LvwqQ}n2F-5^nXU`rWC30v!Dttu6pK(`^niFRr#Cjj*~;F=0(?0@dG`&M z}kffXO}5sWqp#ufzwj)5RvyGKp2U*@*p0Lay#IVp zekSrPyR97pv~X~hRlrCqV8G)th}SfI%)3LsX2s9O+)5Y#eM-ie-v*C!Loz^_p(&jC9-4zMR~AZJ{7uy9YDClKE>+$c}*YqvQf`*aH5&RsMo)AWPBw!FyJoKmp*Dqt-<*LNpj* zip)w6+90w6GIlbZ>5aSrUJ|PFh6vb)2*8f2gl;0;V+nj#A9$Px@FG#XNH}fezvqzx zXcm%(9lrqWs^R89K$21E%g5!&|JTWa2T0m+u^;sA4`B}A!vR>^Zy2=S{(DLX@jBWj zYV?!{kGp_cAWH2oKJQ%R|WI;z@ViBkTfC7@cM0>M}D@9I{D9){!v$wbJ~ z4aWey6jV|^;0b>Cx1Cj^ts!|tT*2MBzYYNJjzI6)V6<)jU3);h_OAB4HTIa{9w$Cf zTI3b)2!rnEzmLKoUeng-e(~26hH?Un8|B?LoNoKy6>lB=1F~ckmh^gVLy>@78E7>X zi$3+gCwvgErK0_Kl_<_ly3Ry4sjpkE_(m_Bi6*O9aNmYOe94kP)RwMZR z?rwy`B0!4;LrBD8N&G*it~@Nq?EUxdTkrcChP3aCRQ8>*jjP*rKj(hV_x!rLuFpT8>vNxTpZlEWEcZDtx7UV9 zX`g@pDg4IUu8wM*`^;#KYEX>|2km8X#OU46j|Gk62Rp*{Jyzkg!2GlT9D+#IJZ^7o zov)Y)v=|&tDrF9pGWhTyQkun+i)&xL-v_jF_cosMU%$rx%5&(pU$aHaDIml@eta22w^lqq-uXzs6TI!yMbCAYx_{Oai|(6 zC@vBg5^-H?-$_2Fdjc4(VFd{A8rD8+#K6k4?t%D6F_vJ?Sd8HuYnEs&FHta`dMOdVOGOj81tXFnGd+CL%T4ZrHJN;j^J2cb8Q* zTQfLYvy4=tOq5G1d)_p4+`*|wfLFwd=dat#pWlFP5}qKuQs=I|`B|P|)(>lYN@#eB z+b%++W(~iNG*DiB400Ek?}D{9!CD-Ne{3ec`?BfhF$=#l=?nXctaP4=zMhJ?q;$u{ zHN@)9g_oA=#aICECTrRgO1l&MNL(cTM0kap8Y;@K1_JLBb6%{*CRT%HJ%vrgu8+&; zZGRb`1>QZTcVA&}pFblj7gvzRSd~6~`sl{JDVtj3vxIe7j znOj^HgIyKaXwKq#Qe8*W=I^r1z6ah-rdOsmF5@G+^7l4L-VWEmJGNNFb{r=|4?>@`GJr;T}dk7AFJj-pE!rH90a z1gBfEc5aW_nIM*fjekPe>jby)iAcq|&bFIB(PqaGh+!u7K|TJ1`d>1jM2qi}SbDwa z*Mae&=Ybc%hS_9Yi)4Oc+#k15R(u@0M{x8$(5^7Kc$ICu3XeV?it|Y6$}2}TJHN;R zUL$L+J948t{5R@|=qiS~Ky+!~3a6oPoX(oRM%S@McLpimGf^ffp8k%IaSM`G122OW z&rR3HO&7cDuNO#9OpkNDSrOU{v~#R@4^+J$sBi}&P5giqZ+lMS^OIA)4%O&nU~sw% zt=zdJNJQ$qRYKcO>sA{LgDog**N+0Tj{=w~B2rr595>I+T9aY0x5&DVzq-FapLY{3 zNF}8Uw3{Kh>pTMFZex?Z7xZ|~<=r4sIiC%~?0(iC3$$V^T)x0KUjW;;h?F+&-=m!g z`)&j69;zjaoRUSqK$;9eO1m-nh|bI4TqxWZ))Hw-t28BUL7os@CN2?Vw+vgHyJ|RW z@-WvuRu6it#@XE&aT;lfw$|c=ZN^)H7tHi-Xv}W#Yv`-O8lpElwGe4GF)prr4gS*3=fS?nOgv~(~Q=pwrW#j@diF1G?&nqIb-^l0WF@nI7@4jrN#EY z&yP+~{l-T8?Q1v^CV1FkQ-#*4Lij!*Qm1sBNSNe&WcEl{@32aQXv{-2xYE8Xd_^j8 zbA$cbz^`5)mxckbUu?Et3;}>h!#))s2$Gj$ZCU9h$&QX3cXU*i&SDQSz^IgTG%Uv&LJt<%JK zXB0+f_(NA$$!Wq1xNI`{pAi;6XmnC=;0Y3R58_5UL|Tn=i(D4hH#>pcdDa$gT3a`M z!Z^l{w8e|=OP$Xp{|sscsCGus?F^TVhDg<-o<;unA$K{bb+WGgN!Rfc-?jb3bz}&5 zb^m;E#>Vv^cZDB{g~mx-MpY6)D(Cnpd5-aevp|c(aKA5fx-W!qN2D~_qHg^M-;V*S zb*w3F$}Mj4sdp}-SYow$(2~v7mDhmh%IexCH*Mor#^w>kbAs!s+dXy`gVkJq1Q3~j zR$R;ZCJswj4*4=YsE_bE)Bo)NI{eL(Ok}m1AJ~gtR=7X&$%!K%_KP*s*(^(JG*& zVS@#VtOG^R#}Fw^`Rwc1Gb@ITQS0nuKJnHy^X5d55KY`o_wAv)< z*~HCa5UJd@QtvfmQWk(*HfylYO0&;O98Z2vAZj-De+AV2vl-MP_)r&H28rPqGKe5m z`|iVwfgTzUP&>nV&Moz*Tk4JEyOYcx8}F6s7!I0eHWtneG3x3C=JniS7Lm%G)yaIi zZrp&eFrF~E3u@B~{GRb;u`6kaE_yRM2K4+Hu`d zIP4`e`!zG#N~Z5GgGy`n&SU z7HL4ciL@q(UK5u#f=Fqa-*4!x+nx`!eQ5NSu*WT~Lm*O`p~uGTkcc** za9ecFH(BzKA0b6RM)`s1lntCD^c-73TzrwTP409bs>bu|}L)+TdP61jY2Ge7S z{$nol0g=+|UrhTv|K!hLHI5bTh_1;Iep7j3B&q9`-sKass{RGFL~O7^QLjSob^{_+ zd#D-H@_e}~(BjbOTanXSZVrP;X^RfoNe;JO1EX(QRecnWJ_?*A?w6b*RUNP6{om1J z1;D$-3Ky*~j8@na4=;=*g}e6PpY&Z_nt^ti4V%G=e!+@g2rXOmj)aN904wbu|MZ)v z(ecGfgbJ-gxyb|~EnRHWC-onW%O}DQ**mGVHCGWr6o7^|8gHOt^mmJunfFmg_o>xT)G z)H<(O;ZhVYHiyX_c5iw z^JIF@mFCZtI5#~ftR$n;NA1P%Rl$~1)H=@0DG%lSAIdS?-(OTk>|W9yIuZVLHSi8F zyRi!MSOw~x79^22GgY?!SXlcWcwVeAI@JR@)p%+gC4NBkUWeD;>^doQDhzjwcS+s% z62EO9FFs0m+5rzo-Am4y3R~IeOi;@kUF@9FUA#DEGBnS^kOnTS;GYlP^lS^KaVaL)`q z;wJ3r#^uoaia1cupJ0x+Z&xllQz)Z6$1KsH} zVJgWAmm_z|k>h;$l&G4>mA~u$`roeGf#=D1&f4M5+BIZgKPS3HTEC>*(fk&>1$^l4b?b+$yjgIW^dIvzwQtR5;fnF;TQg!7!JGx%qNk$PLaCU7NxzEh( zN8JG*`8`oDet&=Cm6G$5Ulf4cDfID8f&NYI=pK>Eef_=hXa8MqL9T$wm21q)`3-;u zQ3~<#Z|`$kRt|U%a^CnMYZcnIatB6;RBr$38B4ZqX$84Utg^3EeO{^Xqdld746*mS z3_X3?UoZ#86Rc~y#JpVspG8EfCXYIDHnZ!>IpDhgS2+2=a`_?>#_r;)#KptqA97=M zTn4!pSl0r9MS%c5cZgKZszi|6weSka9c6LSp)u{y^e1xR!b}nYC&bzFk3?ky&xi58 zDviG?aaJ57JWqI6bw4P&Xq9uJ)8eRDBekpHvQQDJ*}PoO<=Ih-fEIzYFo|851Zaqq z=J+y6_r}@axv+=9D(kN3>&_qCxQeStOB@~Fm^N3C3Ud3gB|HTtp4_c4L@M{=FxMOX zGEzaVowY=}+M->JC!^kiN2DoEmhV68ayAQix7hdOfY#vvf6=&D@R9KJ{V#a@aAMp% z_{y?waaKM4EWdy1FMdyox9HJ|y{j($G!L>B`L+-l6o{a27Z9Yy8+Au{e2@#g9;Q>y zisz^3<)^@d`yDv9_p04*Z5i4QG*3o*uj&0>gZr2^$)qKI{H@W%r$+J%T#`VeC4!zM z-2DPXYV_Uh@ahY(K|p(kv{O=pQ&K1!BBh<|x_WTLumYfkqlcp;CQ%abFe0Vp%`+NW zFt-?JDX8{ZYWZ3UD{MqctIM016K^A#533#KxDRrR5Bx^VK}i8|+_Kar^He(u%ivD^U*SSBvsx=RujOyG7`|(pZ&Q2GV`$#F%s(-2~1EBDb3d@ zt9R1`mj$q~$GSwVvR5siMCBsD+-&on(!wl-bcb2w?wkM}x8F@p0$5+R0kp}js$SUxzME@?g z_C%z#+tM*NW=Xd$g79QM?vR;v@O$>t1Mo98aQc5`M@oTK#tK)Zv8>XdOFoGo5*J_7 z`NP3E+i)@LC$phFR%sc_yDmc%M|e+@qGIQ+3tbHNRnT=eMRqqu@TEYcr7K7^>$Hk& z2f3R}u0`FiMUBh$B2g#_;~)Bd_87W%z!J#aV#V`PjPO#dBj39qaRc#P{i5DF4~H*a zqJf+B#3`o)-=E@kMG>i4{kSb}a+bOR?K;w4N^M?BA=(isEv3=z)O4KIK6clgr7+53xE~i-j60_Fp)6pdDi?W@Ey;5sEiO4}oj{FFa&EzmNs3ZDgf zpSfHnL`wUz<-I0c`8Sm9J!`Iya>I}OWc;w?0%_KB%a+!fcNT%%R}A$^X^%?o`3*!W zr|qtp?H=EKIn2e;YKde>iDV6Y6A4n9!!Gl-?mk0-7RwKvLc4oh4&^<9lxFeKxiRei z*yXTjXT3jOZW7PWVxI|pNa5y|23ng;eGYP0(drj*&o5khIU87$guWZEMG@^Ibx|fYlsk)lb*Pk3a2vCA>g7j#-1jrK8nbS7>y+&?N~1y#&s~ zh*YllX05)s^y~`AjbU+i7j z#5b78qJ$ebO9)b$i~YKd1*3QT4k`C6Ok&hFF?!P)idASQdr&Mcf1aW;)(AZ zua28!(f$$4o@wGuJ~FaRzJjW2SCU5J!+a+cFXq3bZ9 z9Yk88uxBB+1%OCt>jyZ`5(bCqR3KYPnMTTrP_N zkN~iv?wD1uQ-GeU6q!|uU|S54($uRTY8Pyt4YYi0FK1ylXCWN_AyV3?L`m`M zAxD9>AJr1Y0}{o{KFbj)?cMdwi!9|WtJIK@_rK5ZH@V|CIWBfT2m^_KQxAPQchUU= z@Y2`__DyE-O@{OOUtGxucKn}fi7vt(tKps}cI!Gp*E)g8XE`FZ8vNfZ@4rs@0qs5( z&O>VKA%$i@q_lmLmufZ~ys;W$ju~x{8Mer9pTGgLXZNR{Dva!225NcO4Cll)=eYS0 zB2_aQu%W~;xdzmlSXDday*uSNsdp76lB(K@3paYFwSe3mtixxi-Dhrng-GRgo;4{7 zOVs-Vj>MT&j4@Cz9vD!cf(ey11)ylmESZYr&B@;Uic;!A|*KHqJU!-Z9U zz^yTik`}3X3%6&BNX_P@IdxH9{14<3m`|!zL#p|`*vW;YA*S_DIPJ0B8)!w$s-M!< zkKgTV64w)}3k(HQ{y9Bv4dj8d&t{3-yo8^KzZ1P7-P)(xVSKA%2JljtUX5I@Ms7hq z>QBW9MDM4x*$#`3?Op>P9durs)UizpT>_ETVz5PaNcW*}AeX_M_f>BHmA`TMTmE`t`4%1Ku)aI(mG3v!QGE#B+)f3J&sv9CpWq-nP}^y+=+ zuyQTb;{Tb|^!lp7<=<0bI;n+Arq8$DrNh_4+8HD2p3M3lcix3a%^DbA_~nIQFVIr3 zE4B*0YZVy5?gT+fTdV7l_H~CJ&_1DBsMIW!+uTQ_w7Kq8|0r_rL*WjxN<^rqMyTEJ zg`*%*F>%V6Uf+JyWR`+n0qUKQ*`DCGyAY{f(c(!(+kPIn4mK!Rlhvtv)v2+vnc};o zvVE-kO|Rpzy2Nh*+~L1GgMM<{Fr)1E^^hQqW7Iu?{ylCd3z2%+WYOF&X*GL*b_W~duGID} z_mxGYv?XC9T4q}91*=ue$x*6aQ7UYVU3J9C*G6ou`}KJ?(2g-D7bz`@_{Xp!g;k^y zy}L&%V_d)A0QV?ZWsj))AK_D;(nZzLcM5Lt~ly;yb z_0?gJ^J1m@q%{4+zrF(RvT~~(-v@8ZHuOyZ+7U);l9@Eg`jSDwER@jVUyAfRyR?GQ zd~`{Q&?<$??n0zS?N3b_>C$Ps5wa&(ptR}!(5CwrQ48kR36{sl+zJX=xDj#yQ0}VG z=qh)09FfYMYMQ^m$=VxeF-Y^0nR#&+))6Uf_t3{9zFXo8R?C@(ljR1<{9!?>FqC*s zd}QQVj%(h@mYfja4QtCmmZp ze8-QE`}W@qlW}xOxxl!b%dJ%n*eA08F=sEFT$FDzW&P-Ub6`pZGIrzPZ zbnd1sB9(JF67c&I`)we1h+xC%I|B)C(s^rLVyYpj9F*LZ}zP6%LWo76spF__@Y#8{7!R z_zV;F4inCVEoy?47V_@b>%S@cY=dMW7VV#8W}oN*4v=7+p5jS0S2Gnk%+97H0 zLsC#fq-x#ABt^Q|v;pli*7U2$;w!h_L8LTO;qv)`V;BDoM+z)XE-Nf9^I0Gd1dYUT zC!TEDqVUK9-Z@sdyBhnu8qC}N;VYq;?T{4ooB!)}$OFK_H3{sR1Q4Bw)T&R%>$;SM z?|_z%g?l13e!@+R5h-oEdA}5A9sM0}fW``!uXV`hch6r6T8Y)bx!0pzTIcQ1=+tA7 zTo;&J=QiyTshqD<%~KuyKY(@(3+FA=^X8fhki2&4rZ1Kzogie^RJ-l2b#Zv}b?3SZ~qC4QL0kZ0{u|@44-6L`u8UXZWbio1%eM zjvfgRS_W|MS|C!|&*44%=3lu0v{EeFd9l@bF3Sdy(sFi4^ByUm0WA{M-i!3#b17Sh zl=g6A;s|Sb>P|Qm$DyfOU|7v<#2`}ItkcuqO9In@R*Y&90&Rq#CxkgcN_#3wm=}^U zjfgrn|0KU%jMH7LPehBySZ`k%a?isJwfgf%K1wi{JHla5UJcf zk3E909mjzdjvl@(Fu%=h>?2ZIXTSZoj~zS>w8uz$Bei+MZT=xrT7u2zoZma*f#!;_ zbXfww`2@ofBBedExixOCBn#@$!s<}1JGh#E59p{kpEQKirSF<2c#hcx&q1JEqSP*t zTLB_cxeW*UcGz9m0JMwP4%Nb*)!Z%tBBfc@zJGi^^)H~gV3c?WOgy+wh)8M62hOq{ z>720(l5yBbeMN3~h2Mv(5xNrxhk8Vnes|1dH*6B3OG?F-rCf$3B9*f^cPnJf`Jubv zCKIdcK1J_+{Egf|{+zgZ-ns58+97kfPTi ze%Abwe>raU@{!w9f6oCq_?=4b@yL3yc|CUp2$9Mi%`JY~F#ZY1l`}aP-Qg~}m?_XI zdPrKLmtIo5>YK%XFl|ELJr)@}=8p9cshqP;=*Y*@U;U>BtN+`@xU6e?Sr^ZLN`=ux z?(LAnKbQxb>{08yVLj}e*7h8qVcp5+PYulMQd4($;vRUo7|lMD+CJkZ^@!B$yL%@x z+_wY)%^!XDP-6d(^Bp3kUF&y!cDYL|Sgm2D%a9vq$njv;SrkWFc3sQov0v`Y*$a=K zVc2=dEIefJWkRHKj)m7&u4(ucXzA$U$Aa#UxomPoN)vRLJTEX?wii-B*aYT@de9U8 zLcv*aI`PR*oBvDx)#V1rUBUVXFy%y~a$XZYEdM8}0py-Bxkh=ms8=($vpQ7TslM5@*~wrBB(40BMs&l;jb zZP~%U8Brk&BQ7b4+qwPNn9;zy$av9m{b+s$dso;>>S|`x`=^<~eBb~X`J92<`mYHpp8Mn$Xibvsc{kFBUp`WA!Dp7@%=nz?U zaLv-(T?qh|R7W+vM;r|gF$Ky2BgGV`O{^=?Ee zwmvmIH$?3I-p5vu;+^SY3x$ z{?W~R(GAiNp|ORv6R#`=xno$>Mu~nScSwjx<&uLG!_M|h0J$s{oL|(AU(~o``%-v@ z$jy87LZ`Io3CML~LtGH_xWFZ`BT~6I&1Q$n8M_X`dl5)0l3EvWd8~+(_Ht9d%2sn9 zIMMTEl?YPw4&py27x~BP9!WoEg;<9IFNpC1bbANr;wEdN2#-$OR|VN=98C^EgQ3|c zVzVdQ%^pN*RzLk)=ojrU*dRz@4fa)L_?6!Qp5sRP|1_V7pdTvd0nLS3bynCoEAU)D zK=g|E*3O*8^ zSKf@}A5!#=KvooMi?3>{ulzdPOO!!I_E`4;Zl%^g9f6S@Ynvr9$>NR)5ov8-{yRHi zb6g|HU1ny(wANu-OrFf<*Bq8M<~<^pwE)kH@x0{5Ui=3oN)SZca@5iHfrj$_A&7wE@xD`!M1S2cF1!eL4!(2~)~m&JW9i+_blAVEqSo>Y=8 z2&e+9S6E23%gozlm>N0lIWfAS=IQ5=D<`?ZlQuX8)Qha@MX;biq-v8_SJrNt@Eg$L z&}fdhXAXDph)8LJOpLUF(JS0w@MNB=(mGaYF`!cUU5C4(Z5cDuINoXIl2DClD4#2K zMfjQw#;fL;o~_+{1k8R$mxPKm8t7Z-Bue9^$Z%}*@zaRlH zuQp~t<_znjaLUGtcS>$`ihpCvegv&ibq-J|BvTa^- z{b!)1qf;&jjW2MgY>1RLc1(-$^MAI1)i_oJFRrhn{^BwMr9+Ki2?%H)}fSJF$u1+h{Tc%iM%H?;8zQ|h% zOI8-sC{HEF3~!a@QXVSesMSLPRE_NsU|RId&jJn%56*ehocUPUlBdi61({I z$zC3CgoY8|Dl>4E!OR$uI%V-FFTdLvz98qq%ri!u``F*i zYt0f%IL1h44eO({_fg{3PNBG&RAO{ts#D(!p2y(PI`mzH*dc<;qDG{0-&Ov8%De75 z$OW>}C2DOG`NhX&KIQSn+s&2ZdsYF@nT-KKa>F2gA?PoBMQR~fV7gg6t>1ANwV9r~ zwy(PuKW0aT4TN`Ygpo9%b1?AonO>^KGL@f0zY}_sYV?VJ;bss6>{_Ef3@F ztDxss?hpWx(lU>1%O5=U1JE*&mMt~O=H~Z^lor3(x^c=~sW;5XS*JLx=yO)D2_(a(+N2RDG+ zOLW~+S>LC!g`ee!R4%t>NxCRujt?9vFu9v*`1nHpENZl&j;Qa7R5DM z#~S|2m@KFvwLLPw%YxVA&--X}+%f8Ii2L3UgIPpsRxb1YGsIZ~IVw@i>^ZsqIezJO zQ`k(*4qLM3#IXyu!1G~dJ#@`Iba56C!@n(}YpeUEYsWy~U0`kYP22yQ76+)?qWeVe zzE9t0cUMk00h4!(&r2esOWX<+krwYy)8Gjv{eK0y0%kT&>lnw^wt=4qm@R&P=EkhQ zfS1EUV4vK2pB!g`t3ruWl9tV@U9;2~XwRA4S*_h!El%(n`Ha!fmWLl)CmWoEc>wdu z1C8SYKBB$|qe!S6x?V3^?l}y22~4j@+qZ}xQnL8s4V8@f^~>%bPr}A0KmCwdhjQoR zp#*7d$4`(uJoZ}&a$lL;XN}Qk{@1Ba3Oz_QzPi|?ZJ1I9yc4W=-;~zh_!Nl?!YJb8 zg_##MC(IST@Ks?EAEK}cQDDU1;!m_^ZJ7IP{LAsc%Vgd=s%vtTza;q^-b2*uJz;=q zZKN;EE3g)A;=XNMNFmZ%IPcm0X3o~1PxNnVvMUlv|h+7~dQrhIW{wuEyTIdg>F|&GF-RHC# z2a8<69bz^1p{>yr&xIgYiM=~gpcl#IpdnH@_uAunsU>GY?kaQfMY+XAJ{|m#(3AA; zB~9h4y@oadFOKnILYiCq)5-H}(C@@YF)OWX8)K`seBcT{S2 zluIr_q;m4lfB9zYNClb?7A{Y0m?wr44Ma*?ZZ>7nz(cuUbw7(b54o8KzsnyeXeA!D z8)Mq9`QH9O_-ZrP1?%<=*2THUM?P(A=ictFgL;Pt!Y?Ob>HGyo{@i&sA~jn$|Go0a z>v)j!XYG1i*Xp<~rd#(-CstP{PEOrE#_2RPKl8~uUB`F2xQdG9pZ$$WZ|##7+vhZ# zXJKWpNqSwAOoLxVBS@`oUv<27bkh@{HDgOuO6)4R8#0KL*0L{s)*Z<+phctF6`}DJ zZZ(5QX}!C2+piP2iazlJ_~X-V~- z%vHeiWyNb!3}{l|_0o%c@s8<*blGbAFYr=X%LXb;1Nrxy^Z8qC@nbh+COwygz;~PR z!sQm>{D6`yd`AWVy>7?*)E7hn?&b1*BT|}s&hYGeR+oWx5^2}PHrKg~KtxL0(WPCc z<1-=*z9+0~Z8D2CKB;jWKD*hJGxK(_>=&SAvZlDBYj{W3o^12J5Pcw39cR6M*4_{5fs*U0p!#F|(Siu}tRY<9}Qs0kC(je`<{JF`&I=R^1fC z-4u94vS0j;e6y?m4hno&9S6K1CKsq47N}lLN;l{%ky{#i_P%wJ>l0JnBKjBR)d}^73h_6pA$q%Q_lbXSmFJ_Ky$}FQ7-FUE`zgnL`r)&zw5-r zN0ZJ#`Zf#B5Vc{58a?++ypN2qZD7aUyhVRAU^@-Yv`sO(}?ILq=9V4R%FdwF)w78l^b&K&;1d8TskZ6$7_ zg^E*2@q#<`JC0oQ0A4-gh3NJT;RnwH5`0b}rEQz@j^+MPyhrF{Z&^=o8LU1KY4Jp} z9<|wSQ$)b{gk7gr+`Co`ZGlK>vAwM|_Bu8|%f*(>6Linxj*Sp0&C+V;9pj+>5%2|I zg=^Iv+N!&fgi|2@zT9<@``@0w`-9wj^xZ+J{z2~NYY?g2#U$tSf3^mKoD1{aJDJrx z8IJ4*6{PDZ`hMB|`r}og9b>dswQVcEShUS2v{tRlf)O!C&cd!SR`r^+?=>mR*bu4F zbwvx-#%~&W4#q^L)-E@0=O;0n@HdWHtH+BcZhZ{27&e5Rmz$pF&*d|PABokJkq+Ia zttdGM31}FdZzaZWxu1tYq*nhMW2C(_zZ&FjGP!$d$9w$7d=a11I^wXgiT~gx;9Y0D zIJt2gU%Ds4<0MQj*)QDPymSIA?qb*g@KxLYE5G!}6vdO`P5-n0o>j|y;N4=pLb*{P zzk)6n;mMr-fUfR!b}qn6XI_a?SVr;ZM)UCvx4%tJNBkLb3~0WrcwaQVzwmb-FA9r@ z*)FQM%{C3*k#KJeqw}oT>MWOWfk?d)>}r&s)4dVoPO!dsS?h3_-}kz}e|J8A{l%%; zx664L?pW#GYV6)>u#@fQ@0X>s zcE1EC0__T_WlGF5x$DY^l;*uz_uTcMdr*aB*8XpF?ceY}=#eM7PO93q=R8|eyE5Q4 zF_*MzEn2lhNzZl_R*)}6ds(Pa)8+2b@au%w$vUNWom}<z(7XI+tzq2jCBkdf>wd0`Z zE;VxJ4n7d6+=|8?zh%2r0_`mhD852lUm?t55Gk$qWl5*Q)mos{BduC&U(MYILZq}i zE4{B;XKc9uXA7*ZO{$Sis`aFBsiI60!wC*ge{jukzW{d?v2g7o`*sn?AyT=2Yi9TE zke>s&8dlc|m1BhpC)v)DVj>rJD|2-Jf-;bMh^4zHvAM_n;xQtX`{_i*M)fPbSa=hP z4JYYx<8-+bDcx6LIguL`?;Sb0_B`-DFy3jE$!QgKSa;Di!V`8&nf;>cMc{=no~zQ{ zmEV+262_Bg4|gy5Z*6vFEZjrG+WHC%e7XC|h_rZPAJ3Ou`nKyLEZQ&tUI@N_!JRE2 zQreG3KSa;^^BB+qF+2lg<^kL>G$N%9Y93SY-<0Wb@V*?Xc?zvPxeQrEN>e)?xvL1; z1*aZwSrkWV%_8|5T>FL3NKc%0ZF%DA%;Uhj&zj<;+W4g!eOJLhZk)cxx6RGo3(Qty z>B3}&VKNvK5vkdl=IdJaynIoAD2TmWVFN78qu8sdR{xHvNQZ;}z?w zc*x9QskjFUjNx zkC2%AZ}ORFlhxyZ*D3$s1TbH1p3nb)gx+KF<+NG6JYtzu7Fg}XUnsAU7}iL@C5Y5& zuGx*K{=Mpe7Jzk4mG()M{t8Jg1S##7dVZ0~$XAyjQ61Z%Mqp6G1v(<772W@IB{$*S zCAh!MTyjHUd_#e;hNU8lTtpd4bX7z|%{|LXU>?Q~&;c4|M z%sR8bED?5!aL_p+FgU@r1tOK}pMGm$m!Lk0aFvG1CFxox>EiD09$XVQZQMC-)3Ojy z+mF#1D=>)V)DWqfLgn7mVJ}DXM_QcJAdY)p0+G_@EM7MJs#7!=4P_1HrL_0r2hMB4 z1QNs_uZ+qctTMd{nOPWi)gt|B?j#VA%Ki96`C!m%3!tT=)kHzJL@wh7kDg?e#pNZ_uI;Y1BLpLz`MkF&*Zkx_(RkOq9D@zJ>Ko>`1QirB-l>CN+io{lDUZ( zA~icyv1*`X(gvVi#L{I6x@B>7MWi&nVDHrOf1{J&t!LJ(-xQAD6!_(|KTm?>oz*56 z&-yB$J!cK!t?cd1_nbyyDJfj|>0VdNZZ`n0l8ydh3ga-o$F}n?0&TZYj+(gB@EYX3 zW6*^Nz7OF(VTiPJEip3}UeX%D*DD52Q{3l?sXlmmovF$wP`c| zH0NPK4$%v~Jk51a;}5`dWli=(ZuW%Vc?=W9l5r>2LNjia*VOBf{)$bOB(qAA!Au;H z)>dQe8~6FjevpHcC0aaZ%@AkJ3Nr185oeL&`JD)>2siZxxki+WkoAg?!B!z6m7BV0 zneO=3YLLrgat(5m27bG{P8>!`w=ZSW)U!*@C&MK)=HvpcT>*a}Q^kLH_7BTw*k*Y* z84l5~Tf7qKzv4O>A~jp+=VbTpNg>EZF{d0*I2};TB(=CH!ejJL^DaCYlQK93HZrgl zkus}D&MAmgZvUavEsFN3K=VVZ*%JF~E?*Uq(yA{#-F9Tp%@nv3%qp9wG|l6;6hDXy zNq`Pj8ZFWpT?ukG(5j!v+>d*l7m>=Hc1Q}@sjLIJ4(5||wRO50M>`+kJrV)G%~~Gi zGNm2l&SD(<3H1E9ttUh(SDfJb{qAHt*ifotK6$G7;i(3%jk$=ENXxD>v1;nS)Z+#m z$DrJOk=}g~$RSd>z^mU~nK{`k74|z>>5`R>$x7_B`Fx9Q9NpFLLGB^oJz!4BP+Dd1 z?|4^=n@9up}sGJDQVpAaccuf4I`mWBYJHQ;zxA?R7bE$t8~?c&&XjV4>-fp!|z%EXRk z+zW$3s7KJUn3JWclz6`0W6!Yq{9EoO{L(k;(-h2wppF!;c{MfQ{`3 zmAww~cWEnywIoW8maSOuI^kE4^Fj|_78zaUj*Ahg-1#APo=;+rf!sM3>zRr^nS91T z8h_0*-fQm6z_|$^7mspRr8ZZ&YZ8c5uKnAE(A|lqH{t3li>4rzRgeldk~K#U-RC+T~~e{>Si4 zkjux?MN5sNxs?JUm8{Qx8#<$je{Xk8S~48G0l~YMjHh79yq9H%#gGzAE`P zd?Z-MNmleu<`aTH@EvFPN5h}fblQMd%er-)#<7l1J=n+pdad)uty4Y(j)F*XWvcRfWR<- zTX!PTYHaw`qkuoJBMuv&O3?L7HJ%HH@3g1Z-H zIS1_Tau;Z22Utl1FLI`(yE33(j5|%l9uR}Ss;siX>bol6IO}a3a8ur$jci|e5(yaDJ$@CZf0<Fjv`o1IuVcbS1Z zm#B(J<>YJ2mKN)$0IduiTqx*X$c_Gpls3w7#VL!j0-(8K4v*acyjRR>xx%`f zzm)wSuK$ zjYT^D`%;%hXGo~;lj;i&v}kf*jm>ypRJLFEx3r@9dnhk+Eu{m0>=5hyyumqTsvoA z5YUR5T#B}L3jd4F&4Po3XIlr95H_gHN9n7d7bNK1EgzQ>gD4Mjk^j%}MG z(97Y%7?IM-gin{Q7*GMUQjC<7GP{#9H~>PVv`Kwtsb=HH9zCC(y|@?z=JVokTS$B-<*>=w(Niup&Yt3~ID-QdI#5sz}* zbK&@e>D^ZMzs*0Tk}9qv5pd8v_)YvI(G$qCVC75ESfua~nkalj?C!X9ch&TJr-65i z@h-`YFY%wTV&N&GS9EBqL28vF>?h=~w)K@8`|`Y3f~TZy&sP-e{CH_H@J=({Rc)`U z{BO-(5!4gjgi`^%$Cw8LuYuXkl$&JoTNH;x38cQax9QI9<5iIdcU!S_uL;bqaX+es zNSke9ewHh9BLQ4uNaL+GZUsPz+`LN+7Pq69soxTPW@rTr6s+xeGO zOW`y2hB?_qZRWx!7k7$}k-}}iGWYlADVuX2=D&a-G)1|Zh_es z_>I~p?6Oa205cwf)a;9w8>eqP`V!<`Gqc_bV{d*y{>Gc#v#_CMd;F+p5PQtWfpVij z{s`So_@4N9Sd{Tgr(u5pFPO#D8Kub?B_2{;72PMrD;b#cWk%nE4P3~q_rqs5Z5I7upMY;u-j$|j54`-8zQA8U7x<@ z^fF(d#iClG&@xd7D;`8jyFOywyk+C>0__{pZV2>laDxCMr5(LJr~Zynw*sgtt3t4H zXs{CZ?FWA$QEb#;uY5aGA85Ck=k98U+|}YtwuF!35naN+T1Acq-aaN5uC@;6H-YqD zkj~TGKeg!if3phU$P>eDp3D;C)~|Dc4$;^XW(K!bZ~k&(iw3RjQu?uare+ zrN*q1|KXaC;#8vd>B@S2Sw{u%4l%v^8mIdj+yx94Bon=Kk00OX?Q8^|8!KOh+OmSb z-h75%LV5LfE7j5c=Q$i^qLV+1tUhyB+Yo7WzqdIZ7W{M%(8{rol?n|?xtAOfDQ%9e zS-I(yThHM#BC}eov?%7Etcns=6DOC3d_Q;2_@}_T#v0?G!uTNH7js3ANo_OY7CY_x z=^5~jGrf9M|9bvP(0SoGqPJoFadGP4W+>i1jDbS2K_NFlA=2XA*|9`8f8(kmI74M- zi{y4i{2j;&Q3NrY?d|Z}l*a#nSHgH%D#t7p?gM@hiM=kAm|`%_J>{3;;!2ZKrRcN+#%@R!A-XisoeJQ zcanUpD?tvvd-S7MuIgXTJLR=Fg_LgSh~)UegOtVa)F=y;D6L%-zyExkpO)zvR8{0G z*#x{VthSxn0i9aR20bCpAltz@aS3V=Ks&vEsEU zhPUy#kDvJGA@YAdAG>vmJIEctj$0+|UnN}vS)&B0+=0}6oo7=2fZgm$CReLFtd{?g z({|BgQrpS@I#2o<|V0vEw`(B7tF8o|$<{z6q&o3@AlIzb-?K=>7PZ{r=#_}AW!TVNJOI*BrK-|34 zbu)oi&3JXX1M74bk?+qJ(G$XRyscL;=h;f&b+W<2U2f#gPe1$whe*@Dx)9KFmcvEh zwXhJlD>u2zPw|e5l89Gw9>pf*K8}A0Cn@OURPo?c@oHGZ5v0|vnYpy}UFQpsYhq@P zDQ%7^@kG#-|A~-2b8oC0_=|ZZY%ieqvV=XdxQ#!va&ZY86$DghzORHef zh2Hxlu=*r`uPq`q``?TolzR=kSHmNr*c}VRMg?3hG9slFn>}oPc*wLG4v(2tU!|Qd zzY*CeeoF@A@=)Q`CrIU*{2d+a1J8gQBJ#+bg)d%$m$YW$VGO&JXghHQ(_| zJ+dow7VzL`gPN_A8`tq`pXcH-(zKt)^#13%dc`Y9ZARan5t^M5!deB9npL-yq?^8u z0NM>~*++t&kGN3)kFe7$R7=c%tg{ zM1`BUBk^Zg6Q{**=q3IhXqOqSRcX=6&wHkpkT4n1e^KI8=`NssWn;igd9RoJmhee& zE$J0U9+kZ?>h|I_9BW}6?g+Zw;c^TRY2o~$A3e#LSqXAQEIRYF_Idozgf|KH6SDzc zF*ifbYhktZjF~;B9D0tQzSoP|NW=CWU18rR(c%pxk6^>*i%s&y;1fh@_N0-{AFsOD z1I-glmmo4s;C|--k4-X}zD@L$r+NbTakN?TO z8~l%n4(Xp1bLd&G8d&*aG`R@7xo|IMBhu1soM!rJvUC*Cg0T{(Wu~WPFbhDWw9zMW z;&=>8&HP)!ox&JWiLCrLJuh{80laT4Da@sdg-aLASn5$s8X_X|Wkuv) zbv3#=?W_`$6qEG0R|LezB;JJOvtvo;*sKmHTL@O+gTU|ucdCI%%T_!tr>Vx>vle!G zu?pETn`|zx1(DMBC)_rRK6J7cCaEk)p6iZ!uDg+ZpAPU>Og=k$ZrW^D2y*8zNG?mQ zE=yquL!@#$EjLwrn%UIBF$7xmlv#Lk*>s4Mws+6?UzQ#Z2HF|?Ivo{RALV|(7Ln3E z-8@&a!}fd~?0qr+Cdn<6__MTT;Ys4(sulL%q(cfpE&@aIsLe*; zCTaYaxI-9D3il{)U-jlQ=RmF*U6LZ`k;0uDB2qbx<(4AXvOJJ`z&b>=(xRFlODY9l zNU#2PLAu}X{~FZ8r7&hTQEr;ZPseM8Zp5tTRr3ci$1H&7&v>D7lTf~R@xpQvPHST? zWrut80Upfo={S5yWp{|b+0rZyCVC}Kk@2EFhw9;k5yS3=MDK=#+cO|YtFiEe>cj7b zfgqR5s&P@<>mt8ZRl|RY&$lGZ?fxVWc#*7M2dGQ~_}?5Folk13J3{>3zF$hfY7IIs zS7wvTMKL0^+UgJP?E41P-moan(H)+{za`hnAEte4JeVu+lDvZ{C$cOf)_-tEUbNjM%V(pXl6D+VVS^R&$z`qZ(W}_sq%U&n0{$sm{8mM!^VYaGc92u^NB^k0?C5! zxM4>~e~8iLzgVA(-+pg^cVBP-$PgK3aKk?$wYll@vgu#H+Xgm^n9coi^Zop6Vl!@g z8N8b?T;-MkYWJ`P&SEoXZnqkdstuZ1ZE9oF32NcYAx+xeP5kC}ilBu|62gxDFh%^^ z`1i19$gCbvS|8wVkFUcsH|w06eRtJP2HHg?7pip#<&T8!^8SrTzU#HV@9g(*yok+u zN@jkFOZ7&irTe?R;_c`^{sLMS4ir8@OCN5x4Uy8Wo|te{y8mCGwP4Rl6ZcKy?wBJ| z+PB%k?*Ev*hQhsPg$s~d1;{Zo<1qgKkAu=pUlr2?a;LB%&I|R=a~Dhysa#--W^rMg zyb*>c)+OR}hsN>OA38+2#C6jq{xh_+&IEWB%ynf7hcX2o&F;j5sP89z4*Wl=t~xBL zrHwONb9VQfT@|If8vz45u6gbL?DpDu%epFJfS@SYlqeEX2BH!QCZQsVqM!(fU?36_ zg7D1@J9~8gInVRXJHMHEXU;hjZ}h%D>MQzocGMaQ3_|e%WXF@%I*r@@UU%mr#Fo>+ zmMb*N#h!ILo|Nsx*+08fJN=8;4s}TGuyEjE;or#Ek31>c8dtIR!(D}l&A(%oJQZp` z#rj(vPs(O1u?;XfUyj(`b_h6Ipp}g`Cmm19CVIG0bp84d#P+nKZ5|R`4{Wa1@uY06 zZ(r-p*gUib9X#+K&g+c+b=Cb%gWwMNAi5s!Ix#6@6ym$jk5`h^FiCZoRji7^;-c@b z(;T!K@!jF^PRor>tE?J|1W{xlu6NU&RIGIp@g3#|tX^teuktYvrP_I}Q-t=G8$=+! zdj56YEw|mRTATWa;|Si11rJI(U+7wkLXLFE*jMQ4i`^@BJZX0)eY|1n{45HwJ?xNi zk;tS7zlj}B%I1;u!NTX1W*u5McgXmb*!Y$hed#)$lx@|2-#5My468${e||RA$_#5| zPGmW;2fQI)x|A2v$)Up%kPp^&z7wm&t8_J_nhGiX=J*}`Q`Wvx6ZJEO7hM!%8Kp9S zzavN^|FF%YB$9)>x*@(yzSb)Whbt<3>V-SWhUjtw7BhL7d>LT)>@C|9-nxTK4Fq)iJk0PgZe0K80#(7xA9ZxFb3h%%nf1kJg zhMb&rj98)Aq7WORbv!BC7%#mnG)V<(u0O+xL#P<%>=7vAROXld|>rueeefb-e+dcXdpCXXHe&Pcs5D({5QiC|vVcdCej0m`Lz@{e#DM5(O zo9Da6Szc3JdxU{z(ur|HGBZ+7haFVxdvOzrU(&%=DblaR zHsKvl%BJ^e-#nn{(}YZd`Nn;L7GG30y!u~AE(Qns!Y=Xa$Y z#-Vk?`S&Y`bq-=XP6|^6UrFoU{Nr>qf1LWzE-6E4lde7-#2C%Nk<)%8QTAe?lghvw(Yi9?PczBInOajZ+6$|&Huu#j|P zP|S{Q?>eO-KD6|p`;J7;I8pUc+%L{0curGU^Xj2R-;oku$5inUnR;NwwvH$5?yV19 z4qdSLir5l6=J7sgvJY!NcRVSZ&!tPPo|U~@kQFCCv=8NG4^_59n>v=nFZ06NX>$fRRNg^TUO#S@V|HF?rbB(|gtd$si$LOaIS zn!pW6;5zo~aUyRb;!?NNN1fMyM0}_CAq!(H!c;5DOM+A)VlRUaFK3Jx^#i$1?x^;- z*y=bodOFIti)$yckT#wp;-9CRsf(znXcVB97U-ex~ zR7I2B-gG3@z}dD9`9$yd%+`u^Yq77}jwgki*0Ul+gm)Z#$61ON9Vi~} zUS@OV_<)T|gF9G1iP%pf3)OtnK&+Qo_7bZm%=#<5I4RMKErPkLLkL8v z)Tk7NBvGY*Mt0s{TyT!$&>|4 zjf}Swtrxd&l6aCXg=i~G4jnh>8|l*>vB@2AU)rbV88Z|XGi(8t`yFU}2fEXoJXTL+ zxuQ3~(nU^blc6cKFh_*vO88u9O>K4Nu2W{|O*=wBvLsqr5-SSweQbiu)t=W0$VUPD zQP77r34wf-vR|cYf?NEMYj;20BOvdg!F%XRLH^X5y~w|-T!7nnPH1~hIF{zv`;RsW ze;y&g{XHkMKZholDh=9Mx1;h~3@H;Mwu%vtpk=_;9D`16j|Bj=Aex2Itf|oNb^4Ew z88%z}MMD=a&lTVvKLp?*(4y*Ih!D@fc*cNwo)D*fog`mTNGdL3&5DqNR8{&id4lp- zR1pEG1)vs~&zDyjMb0b&wk8y&QaWZIcE9o8i^3M-a~HXf&NsgRDWm0gVjOf zgt*7o1V+~cy=d!~=GN>9s-7dnt=}(W_sfiFnbQI5|J!C0B*d-vkN^*f4&AXKLNy1g zIioOpwz&U@XJ+RYZ!d2v3?>zOvBqAkD+LL->EW(Bm}=J{R&N%1vzEI5gbbaTw9I{g zClqVE4rbqHMiA7R}i1bc)o)B`?Z4VJ-RS#PRw zV=MP;y(K#?!h+408s+nE8ThzlTTEsn0r@HfUxnKAR1<-mmc!F>L%QKK6Kr<8k<(R- zJKQ1!EkZNum>m(KI>OwTfxaixU zl(Awwe)pvCp45RxdlO%rA9ViOE-@bSR-s|5um=@=d#&cf!FuS`N8Q;iGuw@hG*xN! zp}N@W5qC(%M6G-wn; z36;T6nIqkG{C;8Ay{sO{*B$E56|ut=@nqVcs^4GAJtF|dj; zt~4ogx!&2wepCIYIigGx!dfi?)gnt8x*M@xW#LuUp1Q}Hk(ME^msm@1t4~UePap9c zWx43ZIs$Tt(LaPXv#ON+u(YD52l9K5D$eD)=OVpVRoXJ(^PzqPSp>cSx)#96GzY=g zDP8N(^@=J9*G~=px&V1pK_K^K&i7^NoLb?B%XMd*AePoKF*qi+qU!sIP{qP3)`5C; z>oIR{?Y@CyAqFXt^(aB6+Nx9{bSa8nvJv9JNDvz&h}9ogQN*R%^uJKZOH?MD(F;eK zSgO=_>4omT*60on<$REueL!KGRLP&)cV}}r!{D4Exm^)bWKbnZ&9&*Xf*nZC7iF(6 z$^|sX)1~K>Zax0T;1(PeSsxXRqSN8Y*pkD8-_0Rq-Z5tH&>^uZJy_BA+Um_)NKPx` z(8{R)iGQvc)cI^D6wU>q-2GNdk$^&N^EUs(-vVk7;pMVIl42)$=s564+ z-#`Cpy$6LXMcqi_T+@)csw$NQ41At?0-4_<_;R3C4%9!iwFCQePjKjRROXP}_z*g8 zSEaOFx^)FbK#Ftx8AE@xHdQ5EkHrg0oLDJt(plK&EL3*`5z>@>()fS<8x!q_Re=Ts z#7l1F#s3mU?aN!bqHZ_=LFXrjp)n2Dhd^SKFh*%vY3_aHU3t^MJRgwPIDao;jTC`M zkvWa+gjkE@ut;u8N6`6y!0lDlal#rQ0TB`dstb=;Zz$jmg&B3Wc6Q~p3SwK!sn9_>lp@~VT`D*Q(SQUTrVa`ip6&W7~KHs-`Br?+LiatPbMJc0F;An zRMZIM0RtZ}`j=kmn>ybsbK2vo-99_zJrPB|%qm|tn94Kec>C1Y8!ZI>rr7wVSiNSc z?mgGBUdL31C5?21PfB#F?woxW6B{%g5yBM6cboD1oP?R3TpTS29a->7Z=y3{y$rx* zphZm-5UW1}{TTx~qEDu3%*!?$AY)KYg_x}n8_<>@LJ9o61kU=2P!1i-;ecNVW6PoX zO+-bWaqKhhXN*?m@2yB}OqCGFu1=1-I{l2XtJBzBozxg->$L_8=Z=u!sTL{k9*OiN zR4F+5xc|h_Q%SFoo#Y3xuG6Z0Rpk#7CL+}59DL3hQ!m8^>l^>oF_Gbo-nl_jhwY}b27}Q_vcez;X(SLfIk#AG|KG% z7F=3ZzRyLD+t``utWRsxDjfE7bvHLc-w#dq!~V2|gI7(;G5l+|9E+xqH7?|rHm3rP z8s2T+M5_G`z<1DvhCD&leq!J!Mu)~-tgmpHIm3JxVMRvnnG(G-Lkv7_X3jD}ti=jg ztT6ep`0IodTR%ESAVq0G1kwEFP>p1)HZ(e~=cxE`-dxhqEg@dX1 zGPX#^r#Y=>vG}e^^{z_Qe}mP1w%pm^e~OgJfsQ$FDlPMF!=+ivkkJY5=rswrCUK@t z<`5xU0kai7zw5XBJL{Rd^YBF{R+`NSA@DJbK@8H)Ql-GdT}+JUJtiQ{LeMNUr#iL> zBti}&o2k>w|0ZSHWybBuc3+izhI0{r zJoe&nkMD`VJ&`qiWf0+q9R83y_8t;8t*zg-ZUs4ayRF-mM?fN_29anNuS)EeyIqA9 z&q&Te#^@lrSXQOw=NrMG?xiHBP2tdnj^R~l_r+mT^=vCh#pvp;PGUl1UZaY=mC##h zb$*V=+|RX617{2bkJeZy6}XM{B2X{Vr6#k8kj%nl)_6cm#=Pn8&uex{K61uoL1zUP zPQJWfzI-LEFY(pzK8bzx2}mFWfzXJmb|8=>B}`IUP*0H~y@JBQ$ng5-1r+_gi;3rw-lC}LGujQ0jCWQbL0!`|?3K3En zn9As`-atCJAKm!$9-AS#rPZPdMN;!>e~R^2Kv+n0};+L@GN6W{okIn z&yGpIv7i%{MJ~|J1va$4Ww#v?vJ2M`5MKySSQlb$98^eyE9aSn%?HRuKKa#VD9k7R8OLNwY*I1}5JWX=$Gp6^^9lI*ITuiBs zKGsBXvN`i?r2el;ok|wm|82c#XIxJhYZAt)MQtn9ocJkfKxh29JtBigWOmd|O|N17 z%g;O--5GcABDA^))n^e)dTy1>l5Opbr4%gy(Soit)BqwxNMVFjk7{r!^H+y#*55`d z4rYvl`FZgr(4kAnbzcJV5`dRLlZJaiAju3&W^}3Dh2Qli{ToC532VLxYB~P|juOei_c7A|Ql);4(a~-WDR#Bc=Nz<5LVCC}4+ zOm)SU$62HUH`!h{QN&19qI*^>pCc0MBNFvn-%lgcXuXf53(hH)*cMCFCqLV)61QwP zVL@`<1D*H$Dc$()2gYUPj3zm;f^M+_3p$;yta7_8(Ol65k6oR@y-u-&w&2{L4VS`4 zMUpb1LZeWjdcm69z4Mm$3yZtpQEg;lBimc;-qqpBvS&-1yWp`a5P<@b{&17n$-RC^ zj1j?~h5oFm3lrOox&3%jdXB~Vcagf9xEt>zy6^b6z3=`ycfrTrB&S}iT`yM8ecb_` zD}%O;(Zr3-lp196KaMq1ibjoCGhP!{ybpqX(3qayBSM%GhAAzrdV6juUFr0{;sX(3 z$p>c<)fLt+UK5gBVI=6OK=fqbBuF?~0|BD78pL>g}r3 z`QKDY=CM2ik}Lqp0&^P79f4ev!fR4n)8~Ia7!nm?zBRI<%k1iBq$S=GBX5a%)(x0$ z1U}6YcEj*3Y_AsnUGceDurmEypKe$Lk-F(T_2e(OzFDY%{=a;G11^OtU&PX#7=_ND79%sh8e03e=0a^}?rP#ypy!h3As56#7bgTwUO?D{OZU3v;ez-SJC_T39*< z#JUH>>fdUhN$h6}?uQnhWe3FIfLMp}B0?bx3t1z&br>ITEz{+pNE-_!g)vD%hLft~ zKlR$;zgCO2F~}1Ucp@^ULHH2ChlM_@B@G$Owl{fHE}uz2s-ZzORImH!DI12bVC*Uw z_2Efh*Slv5LSJj+*853p{3PmqSJ0AmiQ8CK2j~0{82=EcC;pxELaVI5ZAs1*iRBf^ zF#5G)%})K@74{-IAp)Zi{tUUQ{cF`~?`1l;8`l8128^jiDmmTFhy1KUa(Zvlo3cyidKpiU}!Z!%NL9@?Z z6JmU`2Mqmd=s~}xwF7l=&)Wp%Z36XHWr4-T0o6$(b+H^Tuyz-auU=K^zRq){&D!}S zXP?q)pK>tGaqD$_n$L+Nq~c5=$P^mVxPqwSIvK2!nN!~#SsV83a(QrpRD4fhdrzSL z^}Jw`>zm})bn)~%DuYL5cJ#7KKDx3(Vfy%jWi0lh zD@0X#Jb#{Y_FPkv^O`e$&A+zQ^DqCHy6zv6vs-Gv8@Yv1B|177?F3_Wf*DM6P82ox zzr3?RAGhEPFg^p+6J>9u{j)Ix|Ix?)b3fr6Kj9i$1`*y!*f$cxp9oR1IZ?8oIld{_ zZwfWX6a$kg!TuP1EP`wXW;6EP&Al~_=H7H28q%ZF<|}XX@z4f|*&wm{egYA8aqKQm zP1r4E_iR5_OxhYCGz{Pmyy>SJ_peaiy^ufO7#B6Lf3~(yZt2$OyfnQ?%6t_We-)`kK2WLS`*7lC zl2agdDBw@P`@czazvLn|#4V_nyHz81PpY(Y#KAc^RR)H*1#czRZ~3*5^1A8qIRWDg z@c=#(yFU|8p=A)^g_wOIw*HB5gLA*Z{mhZdv8kMz<4wGVmYM7iQcVRguHffMa>;`3 zxlcEerd?NBTvw`R>Zq4zzD&D!*bqzjGytc8E_LaE2rUq{pzA});FL*{O>+{KJg>Xy z8BO5*fSw<4qF<$ZIroSCb>$h!`6@B_DpCIl8d7$P62~fxF#KIHxGOfMK3)*v0tYW} zmL7I1*FGQg&*GL(BO45rosF>gZY!*AD+be^UtV5($*RiI2#e1jbn^%5#h-=QZ-Wp2 z-J6s-q;xyPFHHi*?Jbh)ZX-Eya;rH0%=Po+?TM>SN0TOf7lQ9XTj~KCH7QaKBjwKY zC~o*&`N``cse~1U$n%G0G(Z|+jZ?xnr47~XrprX!#sZN+fk^#hYgbI2`iCtv#yOr) z*AuG09|e6Hnq#9hjj?e21i(*VG4kN3&#_zY&PRmZQn*`cM?V)|5A~|j$8f1}IDcg#)6%L;__BnwBv+)9D^kA*DIfN3 zUs!Y87{7sMb4;c24Be^3jJvOmW)^MP`Mffpp?uuKlszE3cCo9>F94l0a9bep&Gt zo$tL6;Wz`2GkVnZ$=an+&aL;Ro8gMvS+jQjayi6yrOmm)*UT{dUTD0RzpbZNySPj> zXI#zyMxtsS`RbmkBNd})OvfcU^zZ>y9IAk!3L_db)%Rjx? z#2+k}44Jm^_nevL_}lhb!hV*R{{&JkVXJ>3>{hb7l|OUrRkC||Le3oP5#8S&F~>7B zhh=kEX9`9a-dIT2q;}W%d1zRZ6(rkvmh>VHf;ebQm#ny3N|>p%^aw3^IN0?~zs9iJ z8rQZxHOC<580&NV=~d6KeZNne`^5XfhrA^ql^2x-subP8QJ4;*U5U1C7oD z9cxI%yM*j6p?b4}Dn2D+Ps!ATPYYY$DsQzA*4F~?TA)k)3n11)DJ+y4(}N%rzuh5< z0BC{R*ebJZMSA?IWKgqZV|kmS1s<*(0CIpnJv2pxI0nWsrZmtQT~uQbUujQY={ySJ zbom0C>NAeOrzlNRlM$GARu8*{{#c!t z5uqBwYN$`w%=C%|R~)7=2}1_|s#I+;@6@xG&III*7`zeN)3~Mx|O{b(KaGNgR*;rQcc)>y3OEO z@)|4Lk^~keu&ybSgFS~on9^f?0+;&1)t7)I2|(VdpDb&>#eZ}4hw7!3)Gtu zL*2Ugt#45ds`wsjbPt6FR3)Ds8~+_ueSnlXD=|LH@6)SaCLXI@o=Pg-Cjk2dI#lBX zRoukDCdTMp&v_f(m0laTX3PA_FE0uRNVM24T0EXkrP32lyWDn}6zZ?3mDw}@68T7Bkk(|Nl{&SRzRV`PS=N~cbpJ)p5Hh`@&mK&Ze% z&&@*l$7Yw22tG&(gQRve;=R?r#n<;WohKlEjIkeobh&u4@znJhZ)^9WK+LaBfhj7YH-PPk*Et z*pYLQ>wb|xp-9jDFK)%Hk)%oc1z^8GoBEqWO=@OfGh_H%Pddry=*Qjh2kyPj2^ddU zp8)U#=%(B%9n>&f+ZnN5X5eMUxID=)Z?em=(cM-rwVrM6M_5mZz$uaW+_!qk3LQ&4 zVIIlhBe^Ygjzl|*8-0>>JISwiG#$L}g_qTnj;0HY)A<8~8-G-6wp`In;DZDpNT5a6 zZ3zC96rPeAP$zsRx6}8hO>jv756u^x#GjkoN$*2io*d7lC9lFq1KyrHYYB)q!wFs~D3i z{zyi5h3TxVkNxa1d=NAZ;%|b7bop}T(X}E1kHRkh5Svj?`Uw6b2S0Miam=Wfe@^JJ zvM4oqPoEpbq@#W^=qIzGA62RjgyEk7y=VMJ#@?@~r(LR@1I{@Dw2$z=^o<95M0)8> zaKMcXR+t4N?`EoGXWV>#<_>QMEVlC!_Pj*B) zvnijtIN~-|%6nEK)qGWQDcKh|U(3u9x6w-oyoA~dmMXMeqC&{Cy7I=NxDh zFtaCdD@v+&5s+gd%VQ$-(bk!E(@(3#nFQpZ02~xp(6BKGq@987jGckonj*&=mCF+Q zjhD6@{YqG`0&o?nd8pj1Al73HJjNKP-N-3V%(vGtaKbVvlRK0lvnEy2nSbe`bNd)4 z+#O$mwXZ;ZK|vL7ahXugFqGd3boo#vZoYYtpgm^{p7XbmAO0~fvAy_&i4z``XAu<2YkiO(UTk(%Bu-Ti3F5LEOp~IRy>^VH4lL_DPWVro`%+R5)LnoG?zJJ zkh4nbv;4gw{cd4dJOO%7_&0)H^5ZhuieA}@1+-t0#;HU1jM-1nPO!Eo_{+K1JG&gq z&phai(M}5VPx6P!`ak;X$*w*oWrCGf!TcefG$ngvVahjWEDL0WnJcl~7o6X;YTuJ3 zi11zk-z%JHOtRiFr5}6VXeO)|0Js3U(myF;J;1;Nj2^XCYh5>C@T|^8-Er#=O1mGF zPNl8?ch;qa+84}7PJqlNfd9qXo|}p;@c!wJTXIVbZi$VkogX5c=iqtHf|@uutl!Cs zQ@jc5B>}i3(57cih&5gc)9({LvvF^*;;;W}geX$CwSpIaw-(%39>kY$6 z&I76517160>CJKD27Qg}PF^^XZK+6oS}DKTE2@4*4XGHZL&pdV?B`h5F26qE0IE1j z3ZtZEG^}0L!i!f$rCnVlD92OCdJ5HFBSi2MvVOl1u8JM5iic4lA&wv!8zlR=NR*6? z`h{?cb2!DRi_p_xEb$NWJ|E=EC|L26USrS8Cc5As&j%^|AT_1Bi0uQ5JB^m%8Xk6Pl)1)iOdBYjL;os4{*WPBQ+jVorQhN?9 zpF{N*rq`eSuC!cocV$rv_6pd&0zHa`2+_jn(ZVG^5zfMyXW<_|5#B&gWT|=N=ljtT z_(uu+>nEOaxS|~X^An+wVH=sB>v<%b{s=i6Q6*GynsR2E^5=S*m7btk`Aa>m%0F6_ zKi9LX(~4c4)b-qInCpJBs-G*Kvi?HWU#LEgMT8g`8^bG<&zPZnJwCTTVLdBk&kEJ2 zhlur_jJ+pQ6ZU90`()}YB7zKo1|hueK3cE#yd(dUfFRrTBT$>hw+$w&86uD&(xVYq5o@y?Hp@+X$GEKZ>!h6UzjOQhntI+Otlk3PEztgPPWZ#* zd!O-$wS|E#jNyMH5~KGnyu6McOyhZx%{XN9I;5ww{wSaK*CR47j&oMW`Q`T9@|sr` zx$g-4MVyaD>u?L1;lXJ9&G3g>9L_Ihw(p!}^&v}WEc!n#`kb_?{W$sb~^ zXJ9>J`Y~_h!vrhwjJ3;q%mL2+Zg>vg6`9@@sei)rhR2N#jmUMwJ-;Zoy2zhv-i;Zv zXVQdkZutFr&A``;)!4f&cjLzx`O`isv=w9fBqvsjY7zXmyNk9slNaDw}Q_ z_HuL&EQ0F-a9yBD9VH{eRVlnG)un1>n=O8XJYF)f2ZleTbUKCnOQ{mw662f%)-8cw z%F^Wr?x2@Y$4jU_ioDZv)ugs*CrHKhLbhILOVLopA#yfEt|rWONz~{O6izB$Dqh-+ zGDekVo4q@ElR(@OfLj718iO7|q)K6`)WYiOiI4x*o*eGsYWrUwy9~k_CIDdqW9nN8 z*CvG_Qp;Jl;}ij=(qosWL><^vRYh2D2*3@27LDM6Sd*kMNoqjN-!~OH+U@E(zb6)6 zn;5i-b!m(_MEJzPPn_}fjeeD%=KMB!nk46@cEHM>SVS30yA0&7NR_5eOy2jK$!e10 zC++FSpVUN!^{Vmj_is-;(g89UAhV`_Ggg1>hRoK$o>;7}fZi+qKYk}kI0UY_-BAzQ zCj+_oQ>EoMT}0s;(q6a)XB1{<_?7MLh>Ht%EHmkayOA#h`9d9f7lsJ!GT1INqUvpg z(n1elaZVEGmc;L>Vc4oX_A%^@bG}LJzDd+)5B-s274c{V$%z&@MvK&Hkp3Qjjyt^5 zoa7{l-IBx$=&8i%>yJCHoN3V;3+J*BTo&rn;3bIgN(NuaOsNadqY-1%oKM^K#uENd z`kw)ny!R{RRILva33!0OHh{m4?2~y~v05^PfLBOBg~YPmW%r#!djhv0@HPc(Q`l2| z!Y-3-W{kA@hk(~}ztwYFDE!nt=_C5D^CKXi0QdyV`X133w|>%oKLqlWflnD58kph4 ztonz~`~nGUf(Rstx>N6qi1nQuzLWRLOLOWZi`)Lk`#JyXkq{b5>bt?3-QW*JJlB~C z&Tow+AW>ovB{n?TWm&IteV$_#U|$aUa^~&J{*0a_j9Itn+mQdjfd>R6L2jLZ{Gh24 zRW-x1y2}{cWy`(+q>L3LOFZEdD#73_|O5BUg97I(enoZ`_Z&C(MQLO2-0>Cq_N$Gw(&8GEJ?R(~<@ z7n{?tw}`cjg=MTQy-2(n@0I>Gw3f6aOKzVfSKluD{V=iM=ka|AA#}PSoKD{*@!JWP{S*%gNTvW}3c69>iU{Ps6yBHWQw!RZ(fc(@f^&&X(qtBC z$h)~JRR!Grc*Zl2fFue)qQH!Lenuddr0|l|X7$e4|AK#5uLjw5edgva?TZEbnCtnN zQ*V@;8ed)f({K~XNl}=j@Tb%hTBAOFdYsf3iyWEH6hVC&aR(6^m9SB1N+SvT7~~qP zd#~LOv!c-EsS+m|vl0<<6);!PBX?)TtGeV&kEaK8rFLN9+eOQ2oV@kWg0{Xl)^x%IdycZ^`AZn5j7^#jNiZ0Dd_V6Ea5T+mN9*( zH(>g|!|-8JgD_qT@YH3a(VP334a8Ck0^Nf6pBL2t$7Q^g*53TyebDyb2e`I555)aK zP8g!0BXx3#2zQn6uF{SA$0+PJf9Lrd69{}5&dQUqQvphjVXp%;UwpQ!0CZN{XWwbFGfpe%+h(|FM1DvbD9|ACjQhI zTu=Sm_up3p?VHf_8?Ol6?d5)lfY=7o!Adcx6zkDQv8aRj9L(oTXoOj6EQR4?lvXkP ze5Z>JEZJ~@RycpXuv-6a?=`O52H`SooKqW$tDs6-{|+7oTXzk@l5G)z7ExD0M$NTl zn{y6g$+GY+t5@@I;+bqj$n6M{0K2~`G1@V;{BE4QMKLqG1&SIq7%8;n8ngvNRN84|tX!8xaemZyd4 z_e%Ec$B?i^eFo!x9brw5@VcMovyQmkSv-QE)d+QJ_{I01ua7JroII11X_lEZ%haDE zE4%flK5pJX$^?nbgG6eTt?4y+JsrW9sM^{MXc4vB_- z&uJz@aF0I&)6e_?Ww^%erz?lL4I!h?8OQL~<(;(0x47v{AZ6-A26eo42Gw=PXr(}_ zl>g_|9xbRIyg7UbZhfN&G>RN(TmnQ$V__QWN>vlayJk(j!NwETqXKYLptXOYZDmaI zzJCzw5h*+(HJ~9Ewt3Dsd%XP>VXYQ|YN5`9rwlT9QyV ziB~+UG!R9W+tv;-LuHOb?l@HMToK_cWB8PO1_Hd*>ljGVd>)Rdbg;NY!@^??FDP&m@)4OAx- zzqH?=`8U{?em+3P$Uk#O4jYQ+{{@Nu1zshnW|`>4@Sx))Ck@)ALG?~f>*-}rzb;Xv z;)6nPQ0PqKVxo#8WH3V3)5O4j#6;`y9+}adi$|NK5s(_Ob`7tkuUMR-z4GsTl9M6O z%HaPMR8JN+=_Ry$$)7ELX8L-iOJ0^tv;2wXN5UZVV&r$o6Q zlqh)~^QLvDM@KCE4NJRB1jr;Hi90v^ zhDBH?vnrGgrXmdJYux|eKl4aVEi|l!YMrL5FUJ2X*EAT0;ctk*4UrXfpos`yiLSu~iuh=oZ=3XEHDPUm%+n@U1y}dHMSUmA8 z0SN;j40NFj7zEW=r> z=LgTPKeZl?<%!&#+yh;xH9I0iFffAAp^MfUi#5sBjZ21Od1fd~GWb0P(xkokYT+&d z5&}U8)Td{#2;_zm-cXuSZ>?`eHTA8n@gc170K|i?G)^dDJ;cC6j2?AvK+n2xeW_B* zR1}I#l~$a&GbS}4m$c-B)a(TB4B%$U*p`BMO$6RU1Uy70)aMj}Pmsd|xfR{RZwmOL z4E-C7!0@3`gHV1oyYSVhIaj|KjleR=5rZ7DZPopNTRt0u@cq^$4qoD%-=F=Q-epwi z7rPc><`i1byrF;G7j9!uEf&4lfzlro{84EO8svnKKdaKg4tj#yKcB&=t!BUrC>2#Vy)iiLELIswg z{Pv2fT41zRXx$3cM=fpZOvK5t2S?)J`7E*dEKy&LBEoK^&2HX7bzyD7vP#KIufi8`FSkvDerxM^oc;tydebWi<7hbk5B0;%xS&mY19rF#LdR)q=` zD0hJZCC?K%tlyGLhf+r3uC1G90a90ZNO5GX^hMJ!I^kl7$$AZt*e^wIsa*1U*Z)>q_m*rQDEW1&-;?rV- z)4Z~(@8Q?-@>|_W&Mk?_EneUGMZ}a$ulnJmaFddy7RkH`5&Saezy4Q8kTPBpQ!m~m zdCrZ$1|5%FN6LiCtwZ_4u)DYF4Ff8DN8u5wlz>W!^;NcB+DR$HCgkl3*sgG-CaqPa zntfl7OeU;91mK6jh?jFdz_m+J?~2|lH(~h@#IxOs0}+V6D-yX z7OVBesa6bb!DYteGOzhjoR52V4uOSNlt;xv;c*jP^E9m zxU)-x&40&bav0YfM*Um8q}&^D_Qx`k6Du{1MbRr&>3I2XJC??8{~b%#3j!~wYg}ZL zu=>VfUqm>kgy)pTV?C|TwRBHjRI;$s^Q1w3g!LT&?|@#;+QfdAOzOXg^*#gdGbYsL z^mtp_d}&6&?^u3$9L(dKX&gn3EeF1DJ{V0vUI@Sof%ef!P9p_geem^mz7*z54XJ;l zPO-h-toXfzu=)ysufU3GDj?Q22DUK{Ro6GnF=y(Q8p|4oz4%Z~`q$36wR5v*|N8$G zIM_3C#)z+%U)|#mv=0~DZjZM- zKz|o_s4sEdFY#(yO1GQ=_isELgC|?E$T68e!nYe(T=MGqV*-ytWStjxr~0f2-kXEo zTra9BH)zGVhJA~l5s-9fmCkG7bed|qcf|VF1O)jdJP1vxSAPT&rG!yRYZ_yU8ZYBE zMk;M0QHUv33K?V8rIT0Zv3PpK%h-6C`jX_x?!J|&pX|rtGM5#Wmlf(?w9AH55#zti zB{>_#8#O7`;HehbS_^{5;{JLIoxJ&fkk|dK>ozuPj>A|Xj9v(@eShLnyI1klh2!v~ zxG7<8O4RiuLcW5{SEvai^v~p%?OZ`v0|g*ZphIt55bH@PJSjDzu9?ow>(^GDdSo1K zNr=!Wgg3M0_E+s%B|k<$UJ1Y}fssQe>&$%;hf@foKne?_79RUgEq-G6=O*W*go+E_ z5(sOk5QGX1sr4>my(NRUWaeuJbvd4uyEk=Xo0G<&#w&!i8h~ohjh`qWv# z%!a@Rw~}rW)*J|OpaXSyfmkb)utModja%teGm)o2$D212`h7*GJ8oO&kH_*nFEu~U zI|CWC{U5i}!CD!V^FLNgpWPAMkgti06TFCwQRb zd9w6}CZvw7BJF(utsg+O#uL4Q$9-&;n>NeUAIhYuqUr08YE8gBc_U=s2-Q;r5%$a3 z{c<%y7O|@~GvEthJt}68ihtg8e`eXwteW8dw)yPhCmItmYJ-Sv5UI8K5h0ajQ+f4% z$N3t9KZ1J`)>I)#73!bqA9*0+q|5`czLUXsGE*8RBEM``#ln?+CgSl(5;-LCs`_pd zP0IAxaT9U>9x_f3QLIx{Dz57GeoL3XNzP7X-<`_kG>0z7aGB4H{%77dnCR@+C9!{> zA!Tw|gIwNMN$YZ*V>Xu2qy-P5^+R65)MF2B+Xu1U2mWW+U>Ev+>CJ_cuq+;lz$1~4 z)o+dB@S=xUrPqsvUaS#y?YC{6c>A#j>n7n3NSy-KDSA`u_btriwM@%p0#Xb>F)(2I z{xj@xch?PwHG_c}j9Fk?%zmo{Uoz;n4)^Pe-0_QCy+);$?KsDuHS_1M_K^DZ-l0Rw z34FO2lq1s}mB})K&*oq@XGXm%OubyL6M49Su%?SZx=5cownnT?a@Zs{P5oB9v-x4t zmANMVX^jm{lW=|i%KtT?lBaj$nEr_~`GkU@s#1O6ju7VByvbNrr9x0DG@kvkd#~>7 zU~G=+BZoe6OX@Os`qBwWH|MS=tdU|i@)w&$ACC3mZ5EFe^^Ms+^6X?>Uj*wE!K!D+ zp4@4g=eM0BIbMvB7w^tx&GY{~o#hce8IPx@pqnS}kYLb+>j|&i@=2L*tlKwU>4Cb| z!e!EdZaQyxKphX^78EN?i}^c{+10sqOWdkSJ&gkEMqW#C_pebm3Pk@XIj|HT=2515L3jC^AT3k}!loa#I5p(Q>> zNt3}enK`{u)U-R|<$dWl!kQxlIYOO?+4qK~TNU9oVS@}d$c%cXc^0LX?^~0XdvIZP z)SRifzB6K*Gh(& zKpjP4_=CXcAn$5zSRbzxms_a>{yS&(ol~nJA1&U!&m%mO0cC-&neh*yn?dHl}>Ye?p031gWk%*RKfj=&_JuV$VC(PjwyDiN_ zzmuG5g?%-OB%@04?&tTc?EHhkf0KZ35?iVaf?DsRgg#1VFw=O0XsnChu-RXY{42%N zaT|+(P7&{eWZ0(g{t1ER)3FGiOTcr9E)C0z2%i=3v%;8$H>D2hFvv-X-bsnNqZKnQ zO<7`kj)0VkL8;jK@1=f?gKC}xB9I3he84#_P(}Ri(aE~~q1$7^dO`wDNc1;=nQwO* z-0?!JXB6;^!j$UnP(=$|UzFG=O00G|LA|iyoc9v*_Y(EJjq^LJ@}4Ol3496wDL{*c zKt}L^3=CupPA*BVn>u;IeX5p*N3@z&(KtwZ=zfZ%g5AsZ(Tcn}bhc58YajTEoqCTrmUJcTjG8koU)U za*)G~)xJS9aCf4CUNo=Zl(4Oh_04!e;CG9_Zjmv)K1T2tjS1!rA>VmYc3_4t8f5bnW<|BApyPGrQkpK<^a=>q$LXO!q8C ztq`p_Fe`a`r!6Gsy43PIe@b}$U~E`csXr0Q10i@I)LLm|QR6z_HwAUQRt9Tj1~kM8 z_2Y`0R4uZq7O9;gIs1&RT=Gvi0r@5b--IUg!WV&r%3-M7ik^cU3aV&$ocorv{*_q! z6@M_V)X(kd)YyL}mU%o2CSiJ1DO*q787H-KM#`q3@_q}RBQ7_FH za{_Wt4DN|_ba%8mAJedzgFs?97{eLSyUoa$=KoT&ED3A27-WlWckd0XlYSqQj94#n z@FM3-ZJV^7@631cwk51J0#G9`HMhCf!>-8H6R|c*VIzv8qjCsCf9<$w-ZFDXFd+g|HS&K(WMz`hY^L4GP$xaH6JK%hp*hst&IutYs2VCUK#`j1lX11^lk)-L-M@ zz*lyUyzFl*b`|aIHXDzUj{x`x%&C$UBK%*hLEduIF`JaI}H zSjxB!_P;XXcyymM`~7L=Ny{G0!TZ`g=$`kBqt!g<4f0^j&->acrF#|c*Z~!)QhI|b zCC@`0RpCaIDa^}IxM5Ygrtycv&TRd;I44$S9?Q?`$ICY!4jX)WE*4b07{rTBXe1m& z@aLdEXEn^fY{hct!+uYbqGG4644sPwl`gbN7pfO~yI1EovWX1@NuA=PJMJQs0=u0IL2unZi$BwcnJ0A@FK!#89XgBp|N)dc5n7|-{Ce7 zgB+IjJ1kvE;Xlu@*U0HRcOGuzNznBquVq55fpAWV#HvK1{v?aMPZavEk0BLb6@jZF zbDI}Kl5&S8+(#9El*5m5Tl$Ig7_dT9FyR$pZ4`h;ffe1CAl5c1Y?C@@2Omi--V&YU zJ$m!_f;OG`xH}a9Q~)FDmIM(VGw?BE5vBR(ik=}WPSMLkA`{lFj^8O+$K<}f-)T1i zDUh%QzpRzNDA+Fw^;(&_%)tHg6IlB3Hy^=ux>tf`Kb$YYEfKO6!k@hhBtZ8BIDv`} zaU{SU3Gmp@^X{;mJuLsZOt#WJTlsUDY~_w@C0}NK*x=P0S{~2GP5CU={>=Zl=$-@T z1PhIV`7^R0&U@Ov)iw*T4BDYyJMYH)n4SIQ^j!M|Sdce`>`kG%%ZTt^#=e)S36(?M z4gCvFTY%xu0{yeR4+eT?jC0CFc4Z>DH=lp{CA57a{l9rT8v`YCv6UuIi$vz3)7UI?yiF+4`ms5>Vhpnbf z-Td82&V7N+ecpiG?Ay+-XC4e$hy|L!7$xvq_F1M^uYSxqzYq)bpadM0SamzIt+z{V zC{}E2*=`Q^x5v0YQ%4n|i=YO}^+|I`D4OA}ouGK>H#;UUli`yG7nTvnE=IDkV z&#an1EW+Igle1y`9?j|LguIpqO{B3&66+-1;n9~#o9>6TcUeq^RA^Sj|6U;zp?l+M zkHxqf`4W&XF{J`Tgii|iNnzdmW4hD3W1S+Y%OKpO3!K#jPA#QP+iGS!T;xySZwvHp z3)CAgs2LiYcruK@-w?Am#Ok*P!3T3}Fu(o2(fdS;&EhMIG5i&&`4#@e_nYu^P3)H6wAkA$4X_^0M4boCSd{GEssv2h}`*$U!_lDS67eZv!%RYOM_-| zi(mBH#aOTzEX-itsriQE)uE%zzJDYAyDZSU%&VqMO8&<+d4|OjEZ8`KVH~fgS#V2h zK-3ae^&N#cK4*~HJ z10S(I_2q*=idb00n$qwoOO}czI*Nu9)>IKl74>}hC^jkgaY6}Vt&zhTdB3|$j$F08 zwJ}Cx`SgFYqI?Kzr3h4t^l0og#Cn#6XIT^bPMW(lMA|{C-F8kAoH|PS_etRRNuajT zpoXHj(YX@4T;8sO9uDH1QlUwyP_01BEz42P+%S15&eYm#eZCSyUx`{#zsi4}j?LW51SCoXqC^(-`T~Kxmc!R_JLiN#N5hC| ze$<{Ecc%{O*Fm)c!tR?VUqp0!L*UPgtj_cMJ8GJZ%d|80?Tq^Dg<2-yCN)cJn)$Q2 zf5VMlo=MdH1DDBEIAroJv1bfeW@a|k<_|2KAP9n>33Um82-lSGn$qgb_(=)(H~gQf z>kf$G`2Nnd*}L1l(7+(-1HQ;6>gL6RZXBA2#Mpz}KTpk|#{Ii6l2fUJM|TVfboeBu zKaoI(!nb<@w8gv~=;!;Cf=?+!>>i9OMxl-?Dcwq98o(J_5vPSxZlUz>iywbq&@g&L zmOUcHgRh}&dGo9BjU8x%QzxM71eUlBOQ?oSHF5Xi2)=CiYUZ~)oSQU#lm50*ubkI~ddjJ6N2BWZRJgd}K2){x#)rB|dpLEiU4G1|vQL4gBNVNncF=ddOoZ;`oVl{D=d`a_5F4;%AR`B9#J} zg@Md@#f0I!v8d5iXnd7i?%X1?@3C;WG=&Rd#kV z>Pu`I*S$V1-4(Eb@OYV%NR`H;9K;dD2M^HgT_lw?U6e(z7`v5N~i zv|7rlmLyo)pf`P7(dcSU**8M)MyQc8&0^%DO_f2cvacBUiqXS)$4sZ3Z%_%Yx?uX*4nVPWZ`k_s`Xv zcX7)PRCCFtis{Rkg2MJ49K16hIP)!W?MxQ_wgldmjKjKVtzG^lz2yW4@=#>_knC4* zH7GPWAyU&2sbXmprygkg?bYL5h*KufDD|4w@liNM*NmX zza8=t=@!%7~Z@@cy~o_63sXnlGd{>`wMqo`3T_+NZi zfwcG}(f&j#9k}Pe*4b?xwHt9dp;jlf#%-FHESo(4r+f}N7=U0fI{b;T#sZf$*IDqh z6g*35;IfTta!kJabAJOT>c9gIyixc{j1~Pz!H<+0K1kqP$!I7kJcASx`{o_cGO_yO zXAZ=k2kd#@O|nS{J5y}h!h&>Du$xlBA^)JtO;O)C+ z4}y>9X~dJ9!k{0{H??Kc3Py`-|jPOFUgi0B#lv#vHr2`*(I?mv_I|#%T>bR0* z#>Z?6r;hl;o$EN6QcEWO6oPZpZc9G09MlG=xRR@awV{iPe@l+#I?mvM4BqJe-+q$a znzISr>86Tds#v4!(WlsFQE#Ki^}D9Tyh-FlF9UEHsNy`EtY`;>9Z(xr!No3XXbN(~ z<8#D{i#Nwvb(-Cx9uDNGkbXMkvNM*UW65RbZ5!tgk_-BKkk`^J?EcUohoBHZjizTsX6n% zG%h#+0D|wQwED^EPHCZ^z4hj54kSQ82MCPt^TC3YO6gMKs4%Z)BVW+zybleuOa#hA z1`)FT7WF?Lbz}+eW$?Ys3|}Q3{UM>o_Fun!XtsKIhCRf(FsXdTKbyY0#eu{D5C^`S zT_nk$(5x5Ff?T8EHA)5NwZIi-7|wjixg`_ z60cu3UBjBDu|qBXlk!CRC*t`D28dGD{$ zb=jO~x&WjLtm3cExga|I-+NZHOA5QB6NDFe6-v5mzJ{G9Pl5ZDv;gs>PdA}Y&i`6F9Y9lEd@QVbs$>1qx zkc6x|PmZm6r@Qw6nh0+Z@D^#5G#NqL46{g<5F~>^GQG^?_S*}s$Jb%ED+K>aZ1{>? z_NYhaRwi^DI)GNZfQAM1dHx3=}`_7xV%*@q6F+iY?hmQfM2eM z6xHXi^~rhnON;tH2*jUf?$29-A1dFsza{-HvHAzC)F-I(i5S?`KwL(-#dr?BhYx!A zS~$}b3m+tfK~jU<)iN;U?VKCfV;;eu5t*M6Dd26+u2^bP7R`Zl0MG$+=UFY4E*aZX zz=D)fu#7U!IrIl_)t;?oIJ6gmeC6wXC6=4y=eIq2zSI5}4b)WxTt((@Bx%JH>et?4 z33f7QCmV}j3fjDltGs5e`xgx-MywY@A`Z-!^*V@(ojH&|5eO6+mP}2k_{-#BBMTBM zgRwI6BlAQe7dxd7vqq!0)^OTo|PPM)| zh-S+Z0#B$jTjPh&rGH(z#}eY?FivhXqJHxYx3;89SNfaVQr5gZh(L-&nnmO^KkGO5 z-J2H;JA^>4iNH0HdPJJ7-Lm~6+z6fCBUe&6Ca zo~Ifdk4r@%kWyZWaiWpuITj0o$7yPIdzL^V-aH~OSza*Eyc4rRkc;Im72pZrO3A`dP{b-}(y5kT1 zOLO-(Pdo{7ICy)=*b{pUE9xz0yyXf5jecX_lgOrz9Q+%(#T&BN>svEc?pUb$AJV=} zWZFiY9&mLFq>?jjZ zu1U8_h=! zedLBXyPcl%&L{6b@Q(6XP93zYgHv&TETMp*3mC<e0G`={INgT13 z&6=_FH?6%Zj-pA*<$+wDF}?(03E5(pEw&t)RRV+S)N*$_SofRU-F*~+H1bp$i5XYF z^?l);w0#`sBG9@>e)5awf3;Zrr^8Y7_&uTE6KX7utN7Di(x+5;k?Yt&L^}-03);!h zog^=4`BTpOWT6-hy?b3r9>?d844&=fe*(2<^o%Z!_3L)zLF0r`MUTXgC(4i!ZT9io#D}s11?&%Z|MxvNRu4pMBfX+d!Z(Nr=hkC ztYov`l+CJan4AMSwhBP2z>L52X^-(g;y_k3kcNS@^^Nn}n+H_axjYx2 zPM)2X%MIs@*z^psfBd>}+QGEV1>A7j1-k7dRoI1RIyA)ryR2xL9G1zg&Lp+NjV_a~#q0@jUd=>s zqTT}FEzrY>23XM|DJ+tj)LMM}+rmrOd}^5Aw8F(r$I*($(8e*e!bR%d%)!Tj-w$w_ zB*pFvd7BYSxm5?a1e>MG9M_<8fkF>~(>LlSyf2l4`_1h1nlH z=oQU@eBgl(JRQdsjh(M=F?lRVzZmw5jZU37_UWI#zdinpT@m4ZT6_|XFBX7Upn{Jf zEa55zuTomLTp=#Zhm^hu^)5n%t-!K0am$W(zn$bB3!qd;5|w~{tyd*F8%`n}i)?~#20u1k+Nj(i(OzG64ychcr@c->(;ZbC&MR%C$dH?bY( z%3!X{tZv@A2Y1GXy#3qra_aI%drmYPf^4W&@?e$BV3jG-u|^JS6gxq zU3O&T34uM*@wH6a}nVi9yD&YEk zr5Z|15I7tj^%E?a7%WkI9DhhIwcDh!${zJ|Nu+g&R5I&iIt(J!c5s|KVB{T=M+RS6 zA+#{Dei*qreH~M);k#)Ur$MAdJCc|qai%KN=CXXuWfH?;pccAz?ty*wXlCnpppK`d z(`eSc>3-WMG+km?BQ|)v!mt1A%-xUqPlqjUJXFBJe}Ftb;Tc*qu+5LSwfH{ z)Mw4)4k*1f-#x^&;whHa~R z`D#X|(a7y+6FXv(vD*LXzHj-s)9AUVrC=>J2HWCaj$4v!(v@-=t;z+cdx7M}#+8o| zrw@$mBddZFmZN@pDXm^&H7nR$^!xkCgPgK?0OW!1Y{gs03+L@lU^@<_U?`=6A03<@ z4Z)`fl~RNTc>9ap3K6H1r`JhLik96D6YqhE4yfaB+9I6PO7#mbZJist&H=UQ5UO?% z@3UJL8G}OJMhA409?7R8`HGt`mhcKPui&>nIi(W1RPwDvx18xFWkVXI{_~~wn;g($ zw245Q$OP|$S%RG$+OfA?w~9w>zbI+Jz8^@}FO20EMsdA+CHa+d!e9Xh(hWd2P{;O9 z7Nn7ajg&4fcfaCk#jvWfB5qiB!RWgros7wzw56@$3J%0y0Q?2xpRQTw=D>_b)|r=5 z_)W4gDMu$CEbqq!m}a zcClvV{{&__BEQBhz9T#s(&TtDID&MIH%Z8 zrg#R%{fn_=rj^}4gE)mU>q42raoVD4|7iC~56_@SERqKzd1|nzWz z{z<*`EP4jYcyt+02kXcZJ_)Tq4awhJDyB=t-%2zv)(wop^y~Wk=Zzmyl+STf!5Dlc z=K5b(cy8MmpnVQ$bY29`i!^a%OP0{Xz#c{)=bgjl_R!+y@wM`Zcboaz*uw7&4{;!U zJi3pkh~i*D>?E`uIbW@O*B|U zhh3TKziC3{^~VREbD||Yx`d~wL(hs9i|Jyqf`9{+(IolH4gKXm;2GC9(j6VVp_`jN zS4z{BY{)KHx?jsR;ya->iIhnqrI_-=#f_%XuD?5>iER>rCXoS7LBJBe$lw>5*}qyh z1OM{96g+*zDE^rGJx*w#=fv=wc%n(CY3TxQ<%ihzjNrSZ6T74vuu55DKJIlexy^Na zmC?IOB7|`C2wJA=B8}_hhW<;4Kr=U77OY*PK>}RM(`W{P)tdvd<;ArINkc7ZXZ;HneJ|R z(K~I+yz>aHoo~?2SLFY|+dIT55U3OoSG);Y8gj4DP3Mtr4RXr{x#HJ^OJ5<5pG@12 zM4N40p?$k`|2eK7KY_L%sq1H&^ULxxj`cLm_CzzElpWeK%XSSz*oujNPU zg07SXemmgQ5@?Z}pqdj@I3=$8-qj;J)BPfX?~oaGkhp&wdWhOQ;j2C&2N7@E<9kHM zp$mE(>nK=9nM}1=8@Et&w7HOfXhGG;2`)&bN+GBePQ(thEa3qIA28E!vcBhgOHaOC zJl_R%Tp}|nA>|0L#Q|{w>9K*d!cl#FXyUP`HT$?WucS7wq>76@T(1i$TP?JxCN{r_ z;yT?saVcDzKKa-_xdQF)oRdnexj8QAyZ=r|zY{8cRV?9?kp47;(92#W4Ixx8bOrNm zlLm%v7(!@f=w?E|nH!Oc{Q{+afnxU>G-)m0e#P)hXx7q&bh_}{y~HhszQrgA*dUAM z=8DAZiew&s>o(<8omo0(CkOI^4_@#`@3s-1FR-gXHmk1^_*J5D^?!k`@3*F0@ZGqV z6aB&mU-%lrsP*1KiFW9=EK~|ZrFttSynZrrTIIm>(zG$wcU>+ar2_>Pfh3?E-w+^8 z;^fA0#IS*5L=eZDQuiiywP9m-ZNB~|>tVq5(@E2vv?2yK_2#%?q2a>IXbNrva2qJ; zrjH4>pE?UAYILNaBc=Y~M%(jFwTR1AYXhS?`WQ|$QV1f2>UI3Fm7!byhOFpS23}=! z@fS#vJ?1knS^`Cz%oaUGUBAk^wY?B7`rVZPL>{#$n%bwKs-L@CbNNfgS@lDRh$>c84ul6D`+Bw2xd=8o28+2hvO# zHB&S2h-Z!5Ry;)*?}o+|%2Ns@f$_SU=XaUCD&^XgFghh9tbfgRlVLiyo4Ahq1azN3 z@rz?S&Zg;XT0uCW@k_^@iSIc07ts0zT!S|kX19)4wKS_jzZr8Z~R?r*NmV#cC`iV8~x(`NvUyQvB(3Ib2%Y(G@iJ#=N7PH^ypLNF*)!8xp0_;v=iGg@`e^hM)mn-`p0 zHGFmRtUFiGY^4gUQw56QI2`P3pXAiTfyDAbEMNP&e=~dF~oWQ*tpcc zd{4=)B29uQ)gWTrnf%9&tC#K?bDWE`?nPR$;le4LQJVp|%>c`9-Ld`@*XFj|peKpE+Oa=svPfeOVhA2 z9biRk7+Aw-E&h0?W2EQ@rH&Q3`~Eo@dlj9ILZEF3T#X-0j^G4BVCc852ibDlZ27k` z*>We4EhjR5-A_I^%~!jIhL$h0%qKq=Y{N#JGP!jbc~{|^WHg0-(9n;#q|TZ{SrmF~ z;m~?z8oe?LtlpB}-+Y`h=kPW3cD~KP+suzRj{d8?QTpx$)m)naiQxe8aK@!6kOsbD zBVTe{o!RsKxp6B^-O=QBh(L!(8`o%N31{W-tlSV6RA0J#dGV~eS?&nr0N%?7q&s2GEoKzQu_;iGY(x2j@Fu`}@qm&y11S`pd!Q zkMV)Z=5xkLcLuwo$>|YT_mD@vt8jngnQqDhDSe*@?(?*8#XFXeE{5r1gOtWIZ`*oajXnxG2)Vp6{$^KLh(2J!}l!tLIkzexxxc8Y%>#LQ`CXk`;AfpbKLa zI(bZZPO47+neGj{Cs5X$=qmtTfsr_SGb@@*!DLDmdjmQKnl;b(G?Np(CIHt2Mh;^q zEK?f)8^wy|NnxJU;<1~>?+ZGPmtn&@n&5u1alcq`aUoi|`QZoo4Gtt#1X4w&3*S#m zxv|>-y>JUwJh`1)nU5;;G@W=rCPHP=zc zHyNv&jN)^Fy*g2wV4xgKf{A81`7hXLewK}9WJk;g$$U$kwx1=`Nno91-1tAvbe;WS z5yT~B5&S($?;gnt&=)q12I-|7NTG->9Fl0NNk%u3L{m688i9mSnqlMwjmrcgP9CL| zM~s$ul92YPBC}Ky@bTyMYpTBQr`|x?`wM};&=_axUCjLQo|rp7TNjd5}R{PKixpT(MTDLlud1$>vXbC6jy~4TO+hUp+z9s z0sl8(8(nL3=>|fJqST|veypJSXhZTW_Zz59zHm}LiEf+Tc0~5FFoGLKDFCHl6t%N@ zeYt8%KHG5;1(R6c+S)3$RY!j8#9wFx(!$qiA&yBn*B0WG^R&y!6O&})yW6XFj3=6< zMgeFPn3~532QA`Tk-LH~4Si`VpF!3BbBPxNPEE-62-!c06AcrAFp(P0%FBwT%V4@p z=jro-?}Zvx^+7ia4~;v$%@a*dn;5oJ{6Z`4_^ zcKn%Vo~YxCl+H!srGQiCB90Gj?L#Z}nb=^2(5?fG>%_ISbk%#wx#mbO)aEj6aG6$w zZf}d5pLTG@n45_6mbQFLvge0ne6~stx4MbY5{0ITLPZHToc058>V+otB!x`3UB=QJ zVKLVxmZup@EChGE42SJ+6MLhDs}h1Lp((!1W(ipg%wnvJ3Z~s_d0wxC!={i*0km!a z`SPzB{hOum>TC`qnh&D+>iCP#f|N;MnM4=6w&TKq2&6}5(L(}s-ju3$#pUhgK<)r= z2YiR`gjkSs6g)?%?6}tvvg*5rSht_rW`;SQ_D0XeM;d;lZE%Jp?Ss$HpWXh10|^s= zFo8KPw#|yxNMVh1?7>+%30Dm3%ieU#-{x=D@IfO##|P*5V^m=1-{Hjv(T>eu0{tc9 zOXg;-PG2-3rss;*c88_9oahS(UO+3nXJkd+$>BTs1W{+=$~8U~)ZG^*dl&9B@j>Ie z#F$@V6ekBFX;SPh@T7;5)_R zI>l@7$NrMC@9+c0|8X2Iv9_03aTJT3xjQT2mC%=4H%c#nyi^wji4JTqKkJL8U_e9< zh!kG?Ea8Tnz9Cl-lt*5+YA*@lK;mhOcyjIHcHr8yqC>SDNHHH2^R-6Y&Jf(84MP#C z1P(|H@OGno{j2j{b)CLw-CV`cRXhcUpFiko-}-cTF9+fefj?Bs*x}xD-pnK0FI4fR{gBdEc;E`_Q{GqmX8QJh$c^@l7@iTEVAqD)fTp|SzSrkO10e{6 zCLUM)YXh|1l339UIn0oc!3kd%jNIYju<;Nlnh8KAP@eZx(3=Rop>AYV8Uzq?tGNCrkS)8hKmXO|x>{(?h_5r~V(*oF8NI2TNLoNEx{hd4q%n-*T##P?8R)u99*pr_47cW{(!t(?*^VT zJY}wZ3oUKA2$YMoMvU0JZ`+?MQvO~7Oo;i9ojj`(TQNoO-`i>0jLn@uD$c3Y5pZ& zT~;M`8oJZQ*b9F3m_1F$TyEY%@ZpR_IHO2@yn6K9v>9i7ZlUMCl%`9GnWQ>6t5K!ju{+Z3f-x0$* zV)a)~a+LPkJ!f2XK-3J&F#%{?Hv}3tNRf(xxXBwP9Gl68HL$OFXqyEUmTr!4A`4#q z_DoYPx2+~;GghQpexh1VL~u> z{fN|dgDP%NQOYmVy>Qdc+M)pTgyaH{3si8h7E8E8!8?@J=f+urxd#e^{*dvOrbbnB zqRkLAL-lbHGZS;(JVdKtKOEW**UfwRS|xg9MGN+kLjAQ0%v%MDM<(3dP-ZsFC=hX; z1EuH0DMjDlaE_+8O(4?#Gj02s-hxNmxZ%ry$>%eHNJkGo@Zjs%N;K9En_iFd=_E*C zg2V{tBu?Tx|Ld~(ItTA4Qgsw5yu9-_3k}{v#m)d9dbo}ms|WBnd3;^L;srO6lmY(M4$7)bG{a?Ov8$Pk-#q!1D(sKePvgn0bW_u z-yJp;5OH+Hk%DMAZ-km}go+qtTqGK`$z?2a$rb$6vzl7hCw6k$M^Ks(M0>Lvt4e-2 zYZHv7z(EKcgxWYGHA^U>VG(Wk(=Q7;Jx2TV{uqBOQh(|X!RVQqH*6jim{nIBysxb# z+HB3SuJv<*QMX^^HeZP?Wbt3^xdz>$aZ<`gxl7gXXXB z*v24tP`4^I~?&PwY=i66a=HP=N z2!{HfR@^O`kuniImJj6cf!q{VjvOwH*l+aCEf`JbZGpjU;^cg>a_po1QFl3z8v<}c zppIWLEJ%SA7D#oSqocn#v`p&vang8Mpwz()G)8U_LvBED&IP378=1))nc`)EeFjm- z?jm(}QlA4Ce?@J=*Dg#Na<#obu+o)$L0Re zK(7c5t_T&Eld|pkn|?N57=k!HJXIf3hB9%&sE=V`UYg`$22MJ9v9=@{>; z5GNC;WD+Yi-ZvsnF<-lwl%yJNwvHdrygn37aT^WW=yAu!MXX5sb4lGWX3257%Z}?O z1?r^`e?sgMi?ps}3@V8qg5qX?<$R!RKaf}V;nmlaj_$SKeIfO`Km zVd!~C1|S(|-t+me&WWwk$`U*&=t=1h{OaR;M5p<^`1AdgKT7`QM2keANTh*-E?Cjm zGWc4ihduFjUy1P<^};I*>DWb$?xJ+?%;J12NE2s~hBLWY$8i9NA1(|uwE0q4;`Ds@VybxL<((I|@-KR9JN z-$n3!e6v2jBJmHt+eK|^rIxi)#bzSnLDqDel4`C^uFxn~sJP%zEBY;<(4dD?$qNE6 zsAp{9^z7}h$Oo)S@p2f?<~gq|Is7qt{_~%%*6v}Nl*18x2Bn!n-qa(Y` zn0RO#KP3H9JUkgcH2qSM+_q>)wxuHZ$)azvEq$0*Aqa*4g`;Wu3cy#OjIUH!!aEAS zqcj)p-9XR!+?e#2X{Bc7TW?PEivWBPDB}b_tms1;KBP4@Rl6gO*6uU@KGH3Ce(N1h z{hLy)n^MK0iZ?Rl*NX4%aU3U!nG?DEYe_mNSTH<=gZJbCPo5qQNnznV#Lz=*f|Hg8 zI9DFu`8X?_dmezAJIP;pXM-#$E4-HjN#ub0jctZ(^3J7np! z`=e$St`bHM9KDA?D!`};a_Jv8i9VBX&*>hTle+-i1xnZlR=lMS z2OS|0PpIoj{0R4ty2`xX_J{*v?dVsBoNymW=|@s>8?(J4CNS9`0`-^42bp{|`{UtP z?hR~y#}Zyk;A@G_;;YgYXR=+s8WQ-a#uSMxi$0 zY&-=?M1o5xBF;-j_a&paGRid!IWk$iH3})4D+IYhL;S*E3E>P3XUy@b$|=&-?eHh3 zDD(+vp;uAae2oF%&Ic2X-&}_JWy*P zzdRg?k4Bs-)=U*Eo-%AwLL7g|1b>O*Uia3rccBLBw#T538~LD-uktbW`Yz(|P}c7+ap+mjN$#w!NFUyztzkSNSg=O3&&*cIT< zalGWVUUJ2G#bQ*avvA4X7&MG`VDvj;wq8`_OjF$tIhDEvpj)7hs~)l)C(|&Q*2T4( zaRPSKagS85N2=H(ga!TcG*>e{7I6Y3;{!;tj>i>~%cpM3j71%{3Dw)k@?b+BYIBZK zJx8t|4VwH7vut1-YI7B+T_tmlD{P@Q3AAAXt++6hm{l=ae=LqeGusP5FBpw8)v<&& z3bwIDp*_;h9rat<)Vb&Ik9#-xa`53mEu6rMrOqlAC;y)lAySJV`Inci+b|`5OlTbH z*i~ZYO5WnvzCJzo`>p6Wv`&Q#EM&&xGQoIh(J&hMs*S`K2Z#J4P8=8+M_jH~t2Mjo z1Jigkj8p+g6vbtg-G|=Z|AqIscSA_ z;X7rpQ)cXYw_uawv)~+q@4gr;-aNpGwh2L-P!0d&SaUB@?jnU&1IJynGF~_}2@R){4?6kExY#U9xG9ACV$Bqy< zLPLD4VnzMr&`)k|Sa9F^XzTp(0bOHOwW3G|Q{;bdcYn~J9M9i{b-+;lFsaok1eo=Ha2`B_AN7JcjS<|3zENTkEf z!JRhOvI02KyFzeRXgK{;_sN!z+@Ae111~e?)rZDhOMOT6+NJDWc>9k#$*8{)xk(A} z$*}oFWrvdF9tRT7r{jmDOs$g8RmArd*S$s)(8O15A}>XJFNfeA#A6)9OYjt68wP3< z3=M+G!r~;>i1S!z_Lzjn1)fcL9+#S#f~MfK5S$h&;Y2bl;Ta8|(dxJ!3$8wjI({zE zeoiiJuuTuad+>EVNO9?#774zGC2c9FO$K9_!6-gNzc1YwV!xm-g=+(pJ&4iz%7ZC6 zYja1WqNi|B4hQ8n%4!X@1%K=V%Hm9M^X_9=&qx?HBE03e;i1UiC^NQ?xaV-$UsRb&v#4>Sj=GzXfzazOe)dI_E zQVQ(t47%8_p8DV_7=W%Md8VA|=@%^7HBQ+Xhjr_~U=VPEjH zk#AW-k{Bk54RGpwTxu4n7X5597v9c&Jlf^=$QHUsJ}IE!7=vV6T?nW*Mb+N zTX~sIV*hmjwhmwhPG)oVcIF))r_q^cUTPqyfoAwFhb8pMVV~R@JOBRnquas#13H;# za((66zH-HF=lHYZrp>3OWTFw&2n=gT1udLu3bk>Rm^zXacNeNRt!|F`jcXIb*N7qh zY2MXi7w)n<$f;BUKnc*r1!-875-FHS8RM`FTxkNqXNj${h$khseIu3PMMm)?!9d@@ zUB6>-+V@c#Z>fQ|RPn>{eYvgn&<|toqc;A0ZGU15!`Yiqn`-H}YN_H1_=2%?rBBw? z`)K~`g|xj;Vboy>g*07AD+q_??Uw2V`*Wi8B2X_Bw z(QqPwQUtk2$<6EjtL@CM4-lskYE(i+TrRc;A&wW+_act2KVF7mpgx0Z^987VA-nWp z3zqGqvR`qWOJMXRa+blhs?fAOp!6S5Q}D~t{jgd4wkNZ*&@gfZAXi|Ci(RmU0Vy1i zj>Fs9Qt3|%YId#9LMnX_sC^J9-0pE?5#rSHjB0rbLs!Q(lUXed54kqgK%<)cq|V0G znVq{<$#tA007(Kve4)#B{8kFzO3fVxd0#hs8GLwaZlR?U@+J#?A9`rmLw}Ex*Ttk9 z3tZSfJsWBN4uE$+37_X!LMa7HDRq41u;gfr&Jx`@oM@B)Lwe|{5GiU^266l9DJ<^)QXHw{p7Ot$y*Ig7XGsger8j*{?DhlZ(vGrN}U%c znkWE?0&{GnWkuViuw6PfGh*EsyPt|*;fD<^akAJvnZy_N*saN2Kk|7tvJ<-VX?MQj zhrkkU@aY>v2=Nj+Uh=I(l7vnogv8fAopU~PaT@svX}=*S3kQaFAm@fDAJuQH>>+?SnPQzxu_CKxWZ&u5FeEHNy>fr1XysMbUJb*528 zWAHv3fpp4DI>|+fQR9+r%;UQ`XzD$Ix+k$5?f&iJ{gpqKahwp5S_nDEVpn>E<{~k1 zA>m5L*5sY?JU#p&;=G_tUr>sSl{k0|an6Y+of9i|vytz`O}ajo5799EdBC4%hBH#J z1RpW<5nJQh!x{m9Zn?T`0Vmqd2km@)9J|GedPt#%)bz`?X(l7?w^!jj254Nd0;^aO z2!@MuBaWRw*N!Bu!1ZgW{6TKt? zmqeO4Af6Td%D}ITK8~K8==rYF>_9#z+QS1qJOi9JkQE&i!$Gl`)lW8+!vzi|pDM;K z{k-`#CmJCF5h7J=t6)VR$lwE+Hm(njv$>$<$`+5wCj02i{b`dEON?^Say=5!kA{>- zsFu;yq&xzS8bu)e5{rI`!p{L0cS4+-l;%xhsIN&6HLm3##7z!V^uIoB_q%X=~)k;spzd6x2Jn)8Rfirls zqMc&cDYn7>QBP8A4GkK-x&EAZ%1&fk7Rq;R6&~>AI4^+e3le15GSYhKf3t%)_!Za$0_5g7HBx}0)u#RL8^K3>qYek8M)|1-AKbmdIC-rgzs+A^|ir7?p3YUjuL_B+h)~g(Va-d49FWR5? z<>myIkV?T+O8e)liFsq$Y!*Gvbky;DVIC6A7lC|{(z?C!e6M^#)ty+;Oa^8$8mCGxl$%pqb+HpRnm!lC+J)5ibr_I_m!<5? zNA{ysKAk$`{htJFlZMn7Phx9~Ck?GJ-XNhH$R~kCDVEz74=Flc%oZIl{-)^oh8L6b zl!u+jM>FUQ)tpJl-nrU|d(@hI^3ehf^69}LN91rR9ZpJyV5eOKQVNtx$v*0P%S|`* zs!KTdR-o2O_Cz=%Aex0{xkWR%h>z>=jj|Zo%XOSAqLW46CjKpu(FHOEVY=Q0r!TS0 zV+7(Z)^#U!ulN7F<%s^Jp%vz8Cmtq5<)rbvi zNV3R{CVPMV!{#uDc8;fVj^vpdCNr6(bJ(8a1dB9-$!_B3*Y_?2W}JVF=I<#VJmu@+ zic2h^M*@2!#yz1012B2g?1Y`sm%9FX@EEQ8XFmOT$hrqX+n^!q9>lJD(9m_iEv0Wu zzug<$m)qVSvhMfUb-(}3y4&yAC7ymFhtqCQ1O`RM_->rl!BY-B<(5YbzWNq!Su!5p z_x)9r@tzxE5^q!zPY)l{aor{~$!S0-jjTHkUqDltE;dXjQ`zO(@}>9uf+uKNJo&(r zZvYqZ-LVI6E!XJkR+5zrG$bdl=^qAZ-u=XE@QA;0s{FWHU2_sgv=YfGd(?A&VfAO zfd@P#+2&N`KM%hAkp;;R!wj+d%JZIW7c=fv;rJ#5(#~kN6AK}(h>keV8SCfdXMR+( z^x4&0a|+P3c>y&q@}uB)9h`PCe>vB&p8)s?v~V0Z+i|fJ7E29QozK4QA{x{STt7>Y zv-xxZg7=`*J&3zK&KZU{rzpKs#IqLXc|@EPxp|6QaSzk-yVZrqyl}4L^FsQ(&;(C4 z+i^Kfm(vP@VY~HM$})!2zEGf3NHScwX|<;PJn9Jt(jf#LLKU3+gaxT$Aba0Yxc(3M z@9T8cF08t^VAi`b4x|7m7m%N4HSfjxP2*}g&NXOq4Jz_U;)I=O+B$$<2PwMx&~&3) z?;PP%wCL<^I$NMnmL>E{>3*q#pzj|o);54o(O2f8guX~>3*mSPG&zpU7)M57&2t~~ z@Xz*@mpFJ206f5GY%*g(K128!YT&FqxTGZldBlu)L_mI87W=#O-}_GyCqrV8K|ZuI zUaUx2(dSi&w7(3Q%TN()#1g{fOqg6js967^TsAVj5P_6aW6DW+XZfA4Q3uuxe}+K1 z_@IlgiH%$=!CMNwrTX|-e_&p4?G)Y`PP9`9I)$V2LPylh*GorFcQpg684XMAt`T`U zqilTQrUpJOaDImR%Vun{NrHDP!y1jdDtDftc?_pvIBkwo;MDgewZ6UH&%qD!z#vZ- zXX0i>&q&}Ii7{_2ocU?%2vb~w7s02<^is&r*!zurt8EC!t|7VJJgHgIMHMX zlA$5KiDLVEDTgoR<~Y%5>GG9xH-=PoDoeUT|$skU=+%#UUIGf>0#)y+eX=RZ+fb4(h&(ZHY5$Gg>Dfl}x z_hp1mif1p!c`ZDEl|ZhvxHkTyhUpduiKD2YoG3jM_-SrAIf|10(ET1 z8*ax_!^&3F?OD9%LLCS1Cf0N#`wJXui&XNWG`xtD<>U~}x-pZt7bDIsiPkNN!Wk$0 zcda`6z7xerr33*;5SZiQ?=0bs6uyy;4Q*7pEzUpES2@1m%g@#)IgqDPv!|rYXH~-P zmzEWd#pow;K>{yG%BsRD zSHQTiFG5QN%BkeDav^(JdydA)5;Vg;5?ddM!qFgL{J+NS70M-OTsH;uO~JSOsAp38 z8QECjOxpEI>T?ww{FNS=Vs8}bD@MboFq_Y=Rj5J&)H7(gy!aFu1G zeThh|gcw2a^dZh$kTuA=0ZLRoUJ>-+1}N zDR!IAYLG&yrI11oI42+C)W{8MhvFpP<4jGlAN61M6og}8%O$=W#qc-(2oqCzV zW^}Rlll#ip;!^I}6@mnz%@zB97Q3F(M@Dup26{2JI3A;5*d~)uNeL%f$OnadRh*BL z744P4UWxWURcLmDIOPsxRa&yjabs|2=6OmK;3wR6zxLW7*Tto}P^J z1W(WOGXRq)_BX|TjUaGZ?vzVyNZCaUum{BL!=5$N2b)&@#jt8Dd3x1{~;(IFYhxkurs+H7=%z`UwJBLBzib z+rd#A52=|4*?HW{8@}{;)TnB-2>BGur>t;c_74stkNfzTa-2^z{6t%X6bH}#@NDA` z7u?3g=3choIL{dPjInxLQ2%;@{cRDH_r;E!yQCUTmlqFs@zim2AWOI|hS$ZqJN>g4 zrd@qDM>zKE%!uQ5oM@wvZXA-*A)lf1NlFLR((PeyYCm$KcX;3qPsKuVUR4^_i9$DT zi{WjtR{J>q!Td{`wD&vy>U-|?*J?Bc@jyAA>@R*y-RhB;GrI<t7v?yXcJx)5AP$c1}ZCR&^+2nt?Z74%TSI)t| z;)7RwZR{|>!rM!sz0~kw*}@%Jog)5B<>Pa9Jf~|Bd;?HvAY0;bs+zebkC)UU9oq$< zU7&+=YO{nG8phB@IQR67_puruomSMMBoEO-i)i7G_-SJaMMAnrsP_$_8cwYqVxp>s z8>->KZzS9q3wII{#$qHebOQ4&Mz4Ho?+}YsuY5zVT!9f6yRSCMYYV4)E}zcjD?Y6( zSf_;Ulqd*>_AbXVx({=rjUvz}Qn~-cU|-`M)sd`dzYO-vvWXABUi*Y(*qh;x=c4+rf>}(9TpR(_usO5#7U8urx0(+ zbsp46pX-0tqqRQ62WR+to2x2dlI?n$CHP99ufzmbGhF`fE#AHOzd2DiA?-HA*j7)| z_2jUC6E>s%LKx!^@*A?c`pdpwjfy#tb|GjNj$Z%2s@!aUWehB1G)7nV_^Dr6_`R#^ zc{dnjKwaPXbRGSYC5@gvTPce;p^MN!3PNGfFak(XBqk|xlYDK+mVi5 z5?wFi_3tgQ<=14IvK9~4S&IkUc%yN>6_(&AhK^zlyg&WBa(Ach-oF~qhoXyyU9?%H z_xQeRdtEPb@6|CiIXss=PCpMb$9pm54O{__u#lj=&1 zh*KiaC?Rpn*tH+&=+5rb$R%~n+Ls4vXX$ZmLgAQDxDr3^&$`#`ICJ$vBbuB-01Ck< zoNJyXL{Tt`Qo|`#u$Lx+Z=!UY$c3ARvBmc}S&1CTZ6UZVG`q`xwt?^U=NA^liGfax zHBM8Bs|+FS14TxGfy=#Rv2a-MSO-JeE`Y(h$3lp0?o=JgkmiTmU!s!eF*r+DBL?>k(jm?d;lu#-~x zT{N|@VXn*sd%z=*HcGRNSeSZem>)@EHZ`H)+~Von;wh{}Gj>dPm@C@M!8ZWV0F?13 zk%fOq!H1M4emzA#Um3P``3X++9su`%@|u5d*CYflK-au36m+39>Hl&@@n07+Om}65 zc5@)t-&3ISlzdj>^CfO}(?dCs8xY)pI)TX#U3XdkiuMn&au_Q&^4+r{d+)a6gKFg? zv|epX{(n)v*$uwp?8=HJNz9TY3PO5*(PgjYMO=SAP|F8S#1BrQy7O~!)MpOl8U)v% zF}9hrAdzwyDYwMGcwABfO<$4Jv`DHj!^JI%+-)it)r>fwC3>I9i4iB+M4URxw2rt{ zRL)c>KKanA8LgWq0G>b%J9MywJ_!4u4o-ZVH*bE_^GPwBXru^4inQ^CEGwENgIO}e z{x@?!Y05`v;qM$;$Q&>-hq%+@4HMGjx!mlzd>($wOYU|QO!FLQMsJG^!!}Sji>jw< z*nQtS^4F+Eki<5KSb40c*P2Agn1ycB4?6ey+>7j8kED870WUlERjE3F|;7#tf0 z72D_J;jSSEXC-s}q%cM)jNE%+RTIBIJ6nI0aaeX$P;LkvR#-`HTIB~-%C5`KeB4fQp{k%z` z?_lG;aq_97YLQ7^3h1R6iKD$Y{j9Eb(;Hult~mwOr=a%3WMX(h+9T)CT4Yj-!l8u} zEXL>rh~uqq>8-Cldc#@fh%=L(X^d|c4mwAzv;xys67^FmekeZN<57pYmnj07B2!%A zlqaOqFrBu*b<{Gej7-+FtrbLrC=f&qEA@C-WpJ^73on`=g9)-x_~zr{bEv(1Xq!(m zH+-(#H&6CGUWY7v$&P=?Zo)R+%`Y_;`4$T5BgJf_So=N1tAE03Y`b zdfNG6m(}L?N6l^P5$7nhJW8_AuIxJ&`PM(M9(6QH1d>Eb<(Bqc1C$G8$a)$k(3Z2- z>EHihkNcc|8@XJ}c8n54&j4@+^uyT}yl4-EJ&hD z%3)s7zBM5y1SRi9;JwHg*M{Piyrbbe+H6LE7#3|j(+5LfB1>)pqnjieb7=hR*OPCg z2};T+P)1o|%sF0(4+DJ|`^}2jQH(ONdDE*!-%{T_7DS66D1tWZSh2yu1r;B8(GMK_ zz&YU(WeTfpW&V}Lg3<3~mhZ_>Ydq@Vx|nxHA5qVX#h_R`5JPt9imJ2EJmP%8QpOzS0lI-E8}5VV-r2AlfBiyLz3T)ydg9QWy*u zz(F?VDV*{M(gptPK%6VI)fIw{v3|&9Jy~{p1G4^x*zSf{8-^CY)rA>;=DW~vu>=%L zRR5ad8L9d;rk`p(4?_Rdt#jFu^ zG#r3%V1m;Xc|t3Mtx)}IQEgI7&-SrSt(*RtGuKK`vPiPX01uB>XJys=t(hdKD3O2? ziJBWbQCj`ScT|P(rX1ds+u$h?=S8E=!fGW8c=b6_m?JgAZ~nlBX|7Dbl}4oAOKk2%q7QsVrg>fyWeQ4y z00;tRxCR`rqyfSPXocZx;vByx-LNVWMDz7PzMdI|BIZS_6tGHRby1HQ`_spw#U>q* ztIn5I3!;G(2&Am>sLP9vguekjX3g2a3A4nDfBb}Q;~|;NA%cO5v$_x`mKzjH z0;ceC6ZzyMv^YsJ+%X0j;)IE;!pKQkASZWyp0GnO=`;kVp&4Fq@+M_-Fq^ZwS>-Xb z=-D9OZ}fp=b2uvZCqC$7tf zY>WlUSaKGLOY9;}5oK9Kip64E5hsM25JLTqXXiiOoGKdavtMYq7=U8X9|Jn^mSjj_ zhIAk<8rV(^J`r{!R1hr^gCeo9%*d!Vt#KvVst#mfAZvzUZRagG674rVN)XMa*lbF> zV&O&K%GkFuEdfKeBh#Ky#?MIN)xGQbg$MNi-GqkAr*im|&kg=>nKK7Hhj3LwbrZTK zUu2X|c1y$s+r-(~-2%s3&%~R+MPcktsbQvJ z13F?h3Y=(06-{s{aN-rZrj@p9CGfI%Qbrsf#=(bt=Je$O!4EMt&FCJ-0T9RM!%u!c z#S_l=;0Zob=p!}9!xCOkq7DX8!-GiOvsI4oYP=u0w;(0_4(uC|_5_e8yr$XLw3hHo z{SAvD&y!k^k_xeT1sR^av+AsmMD_cOIJpvau0*@m-mt%OTcv2~XJmbg%&bMGP0Pn; zNQkpRvcUvDxtb#r{ZB7R<1thCsRV*6&}QV+an)xF-3)lb6AnJ%oOaGI*s6YczEAG9 zNhha|y!#p5&{P(tvO`Do`-)9h|HzIcsZA2WHN};R5hp`2D1!iJ;v5mgX;BQTwO@Q?C^w${8}tg7Zbl0^*gils$W?v>UxP3mPl2&CT&0} z<@|g`cPsMW3ktlTOqLY>c3{=j>GOGl7X!T*b!2qgFB{`LHblO0G<)$c!!JlQfC2&3 z;7RkW^Nl=C|I8EOWH3(l1BMd7h1-!O*3F%MfIc5etMN0R>_qZzXZoxqV^hqQ3F|8kF<~;R#x6?LmZ7* zsUdr3WqsWG1X0dV^eeN*6gWMZu}AJ>jtd7c92Df=Bpi+>C2&mcmc4Ccw4sV(zPH+g*3>wbCqSV>%PwKXCvB?!%}HwDmlBuYeB@hF1NW( zLQ7^(esuSCbi8m)6Etrk)zwoxUHsM;ToO1BxDgLH?U@0N%S9GMQFc+3c3J8+_o|aw zOsmk1NC+aK4cB;J?6ECXC>aGJkj2>vCP= z{uOyCO9Zk+%Ib#~Ugd7uIDsc*&@h9xyfG^BoBfK66*04JwOEZ>@fBH8D>khq*qj4D z_$>qdj(kNuZiLp21dwpcLi+{z*Rupp3T>7`W|-K0)(tY>+k!S9kBw^$DYlW)4k|ps8;OJZWoa>7R}A;*Tv7s8mcU&)gmNe^huV`!zPBeC_9SDjBN4-miQ#m;e35g#*b6lE zD(8B&7a@n+nZxz+vWD}}aJpW;&#k@B>3aDg=l+n>^>RM9ET7Z$av`_6kkj?@Q*PPQ zUJ}nakLSHwdC7Ua>_sT&mX~w7UasVpR&u&tuHx2Iak^fv=2lntYPOzpukU4P1Glb$ z)Ae!_x3Y=T^>Q00Z{xIHuDKT5=#lQwiC(;SoYOni7*{-suK;JDN>jsHJ$cb-j{;3>I(I#pO^}8Q#7aDA-7aBe%wLV8aq)+6mi&tl=x>3X7A`mXJ z4|v7RGpd`5!Y%!1=tn!_x=Q}ay0V4SXLh58BP6yF1ORc5iOKEQtt-3HLl-ZF@lq3U z;?)+7XDJMA+*{}rwMkGPApsE*8?1#FZDe61p91h9KE!yrIcK{6!0Qdpdj!!k2`H1O zzNQ=kV=GbdllU%qcT1JHM>pMbl`QMw@bddiG3=y0f*YWBv}ay+QvG6@zrKGp_u|i`Fo( zh8ck;a9q#~8J)~nB{SN5{ZHpz2&^|c(VJXY`kHIr8Z&zIOKsb>?G@ni_2Tj+ z*MFOC-MbH;R8(~5>Z`8$%dNNH9=rXHJJfaICXArkJ z_0&_(&&|u*P+3)V@Rr+dvoHtt>`$9AH8ytC=-BWfS?_f`;e^6K2af4}AhmOsE@zL+ z$yv9uqT-dCZ@u*kJJw#mfj{fOsA0qZ-SwoCOGyV(13frq^dO~U$BunQjv7_HtgP(k zTW-1Kb1$6v-fdg?x>Na}nK^w%EI%hVHh=Cs)enpwG2+wir*^xF@J|bL;ONzX6FPS6 zJ#6^!6^oZFsoQ+hO`ibI2EK(k<9=tXX3Nc=Y}~Z@|3Eiu#?07+@ddFhx7-#hzN}d3 zK=#N{u|B;||4wRZYADcwqelnYwQJY)yupJ@FDowIwW+4&V_?|8cVT|x=9@mgv^f0J z^Uu#LBfaT+$-;$?fM?F^*|Ea>@v&QO+p6?q`SKO9oH5xk)`2to_Wfa6YU;^>4t#w& zke;62W#FJe3l>~_@uM3yZv4O7wr=&b<8D`&qmREMcEgRE|9RdUaJtsDL#K9eIz!qaDvi-!RMa)$M&JnFz8R}f^u9I%vx}9%-P6u8M+xez{UVO==KPJo< zHHNu8e|G%mT{M^RkF{;nraSdDCttGg(#LPTUE7J9YPQ6tTsTGPz%c!Mm~WR8{QmOv zp!eyg7ogp*k6f>8c5T1k5xZ&2EiuAr*SY7NS3>=SZ#(zHXWoJZ+o4Zv_bdF{w(ZIE zG#?xqKfYkgmRoN9jMKaM*4tw9=gn6-FkKOOexnsx12Ap-)vxNPC=HM`Q{`@HLzvSXeVq?hGYnPsWx=mK_a zXyc8W$=(IlHW^4^HXYwVbeMW5X_CyR_rKaN$CQKaKB$+%kIiK7GR4Yp#6{{mzON z6|r&jY%pfz$j^KAJnc@RXO}R&fTrnnM9=n%(MH)8XS{EwXY|`>Y_6`pQa!WtJ$_C* z>x?shLho>TCtJ^R=gqqxvM;;zQiXqq_U#7?znV85GiJ<{JhyNY%@+WF_OHHrEj=6L z#)b?&|9^asC)2 zs+$WgypSHl$kxC4W;NzgeZNaQXm4#ZGlwj@Zhhok^aU5&`hv{CgWu@fsnblNx8CEB z?zGS7(PJdN54?Wuwb#B4Ed6}6ceeemy>9)x3l?1R1nJ_ZOn=pyHDm`4|CIFX)I{Y7 z;+v-fG)6zbbpfok(-e|76yM7+~MX zc-tKPU$l7fZm#Qfk%+?o{BzIytp_Gny?5x)A%pOHaLdg!Z$p@_UQN#k)EC&-eW*TN z@K$Y|+WeJQUKJ~vI59@HD}Mo9)A|5L zwcP)oclJ3iHio~{Z|a$6o;h{(l~?^q_v2fX9^6Xr_IQk_UVY`Cr%j*vz@^2-PqS>k z%F4<}zeX|sa}!|Hmd23Rn1^kWv7<-FI(O_)>fAji3!jJ*zH~R&q;P*`{)@GmbfSNPMs?t%nZrjgyR^2B2b z&q1D?xX%diZyx?_$wq&jF{)m@TH!z7to}bvfR9zzg#U$h{OOtSemk!n|I5oSSNPLB z;$?Okuis0hPh#1O|EZ^(veb(%xYxA4-lM+S(YUXk(Z2DGZz%lx_dDxH9&{Gw_PER; zL*^!n{}oqUq41|UeV#}5l!yBzix%%fzfq8%AIs?8o!K5 z_>7KG8~)E^>yq9bG>U&-e*O)>k>~Pw{O{ha+ww&6?dyivW~6rjkwiQ}eADr#_wXmt zn!bNAM&)HZK97!>r}w!l*}5eB7oh#;(%hlD|F`2`srr{u+?H1*l0R?$eCxgOg%@1F zYXrZQh$o0|I{q}rdpg2=y^>yxcZH~wdx z*>6YRGtQViVdBJFuUN6-rJJ^FQM$!gR;^m40^an|1hC976+1aZg_p(bb zi=BJ+Ilm?v_dIc+3rDC6{rmT~p5M8j<+e%B=Os2g+VS4L5hF%a&|2;vu3NwUUGx{g z2fiDByw~G3p13yE+!W*XjP)eE9xQY4`ESzurP+3Tsb5(Fxg{kfF`AQpjpEa6`3kT< zLhQLN?Q6eg&6uI^r+0a4=(&E$MRVsq#P9O#^ZLNV?XynsJPq3rz?$ZPzo2(Le_eR# zWlv7N;KFS;Y~1t_;%MGjt@&pB`J%OcpY=7(ho~=5YeTb!4qb>emMtrbWo2f*Mfq(P z;NP<2-|*qXOHt3&G(PkDwq)>T9<*=%4g6=%n*ERY3oicA@DU@waq6kvhSIy-_Q0&` zDW{yf@Y3R)wEydK)`J>aTO1CTsP~7^gCXai|8|$oo#zfZ`|O#pBQB?TiQ!p8|4MQ? zLRR3nj2?^~H_lr71wHWdf%mPqZza69(z}^kKHsqMMm2W?{OLI@ngE+NXPvth|fk2AMwh~@;)>t#%w4d|SRn@CspD|b)Y354>-r(Y0oNX$ANF`-&-3r_$@&_`18> zq-`_aydI0&<|UUbtUu@6b4xmP>eP?w(yC`Dx)V`5WyLSB($P3zk6Wc$%!!Npg`HBP7{=VS)=WETHwJ{pU zX|H~cvIlwn)j0zP{X(AC9r)+u=BoD3HUry#$+mwNj_rmH9k!b6L+T4?{Q&g^6&020 ztN`c%tuc$yx|5+Uz5wqg;_Xv^>HYf;xTtjLvVF9s=X0)qw$J&z4$RlE&w6n7z=5m> zGf0lw2ZH&1UZ;_hJpL)v|DS61?ORCp$91&F{3rCB@fYd?Xzqw@0uT1F4r^zGYsVz^{cEw9VuwLIJx%$z>mdS2&wn=#ZDX+G}fCwA;Oh4A0Z z<40roliha0$z8h+;%CYkGiQEh`O1|q(L1OA2m9V_ckud9=m7h?zKr(=)4HKK0sj7D zc?;G}TK9~MF|>E{(M_9cK4v|*`s!;)4`w>_fcoUm373EHGouavD7Wpi&N^#4z5Cfn zZTrdfk;r?=K2x$T*zHcXIoDi$%^UOPFL;vdkIWV$#oIarqbP z0nO*ozR-&t&+KDqpWa;B+r51G^4K-kUH8wLo3{A3ZMHRSm%>{5d)oi?8Dq`!MLD^7 z8~TwB(Ro9>?mL+GNsQH(sn01iKRcjk^5nZ|PuPd72iK7v%$*b0g919&g`WZJ*t&mP zSQ9_n8SBa~hee%g&&*8lugg3iJ(+uK|zWU)k0AsSpUPpUD z-lF}1u^TsSj&Z*Ltjn)h{?gQG)9=a38d^@z_#?;$>`XWY@9$cfuD7N2WAv`Ba@OoQ z(P6`euL!U{`r2yHe+HNYIuPhUpaX#p1UeAtK%fJG4g@+7=s=(Yfer*Z5a>Xl1Az_% zIuPhUpaX#p1UeAtK%fJG4g@+7=s=(Yfer*Z5a>Xl1Az_%IuPhUpaX#p1UeAtK%fJG z4g@+7=s=(Yfer*Z5a>Xl1Az_%IuPhUpaX#p1UeAtK%fJG4g@+7=s=(Yfer*Z5a>Xl z1Az_%IuPhUpaX#p1UeAtK%fJG4g@+7=s=(Yfer*Z5a>W_(}77-rjMV|#xTrzmv)G) zH;f+`hLJ*-X^t2%-pEQhk$$r?ZyNB>tCi$ePuQ+}F{AA7LQ&sDvQH`3u+*!f#bIZ=1nz>N83{WQFXfNN7kq5ekR!(nKyM_`O2v!E0d*9 zM3-im772BKE#{&{y;S-9d@8nbp>Oq^!u@B>nCmW7{^eD(+21x+`vaeS>U;M7^@~_6 z)|^a+zOh90*P)f-OL)KQ{?7IrRr&+_IDMp9RJlUM?>wiyDj)8rQGvP)CA$DO0zasM ze4FQXrd2!;rXZOepmeP#X20B zc!x@VMxpkj3-+mS-F)rCuH~{;o=C;+oDoVf(x^W}PpvaFsDokr+g2ep>4#PSJZk#V z(e{2C*hlGOua_|7#Qpc%@o(^f4t(YUCq8k$ z?wk#a4|L!Q3m@cxFV@M)2RiUYg%9%Le9nA=4t({(7Zx9U@U8MmJorq}H^>9MVf0l$ z!3R3?W82#PLfH;!RfeK0-i)(6zex#gXD zD&6ROjuOL`Y2NaCRlgcWza{l#h;P37GZk(yd^7J(LpLR^M zlNgZJZ-vG@=#ggKp9(cszwyf7I(330tOLG(4{~P6aT_xpok6_&^DSS-bdE|j($eVp zqD=?m_ZDb^4ObV^fTHRGg3V8te&m$6kl^R z|8LRn#P)bKIXri)Emt;2Q&Y@{IiF!IJOw}C3AxU8T1WM-T%PtSrF|L_)_48>VjSc) zVjP}Jyo4cq&-Q6_CKceT(e{Zx@Ndf6aiy-f^*xm?{T$F9zsTnM^#cz5c5yd5xL{tTaI0e3n)bg)zUKt9GKj8Q6*{$T9F80Q{q-D4i} z0e6#rck4!fI`^eN8q>ORJw|PnG3wC*ZOgznUzagTe8rf=A0W)-;b8&tF-Dz2ZJRH4 zRvVBs+-*P+;ZJ0n^kHqNKVpYAfUv&n_t#z*TKBi*TfR>lpxRP3*4P;3kn2ud>YHz4 zR%k%`Q!J{`uXG#8xK{c!@hyU8{@8I)>wekqlWPN^kF%RKMs3RuudqMwp$$NX&P=im zuxhfKH`_3ZNHxs2_4C@6B>6Qr^XIQDUr|zBUa``k|1Ij;wxBfA(9fapyGYxE-;|{C zggBl2j19WpxOLGKX#>lO@tIF?NVmqPexv`?atceAmaJZ0Eh#kJgFc{KH~XX)7P}ky z;{3CNK1APXu09l0tf;J5RbE}ba+xtWGjoWR+Y*1dzFW#gMIYeTjn{oK{N|)Xk0nM% zBsqORy1x4GY0K&Z%9ThTkT17Bn1`CH50gqOR+Ls(U2RUQSW>#GIrZwt<7EQ*(I&cY z$~+bPO3?@SiTJ(Z+tbaA>yy(5r0c5>*X#G+E!97uT#57n`Eu(6=J%RDUeIi7#p-Hf zSsqul65GLAAMBx0lq2v0N>3Q8lm5#?Jv~xqwaWwGkWN8h|_Ouyg<5s`ha=k zmeL25E0I1RU#LGGZ+1{wQ2xy_-ni>w(dzQ$G$agORl2-%adq*ciZ!T_B>f}h7yGxS z{eUrQ{(H>=^-PKUBfrQ$a)ojo4f*dvnJU|=^(#~r&wmZwN4dDq0X8TD`#g5en_665 zQL%j0)vKyYR}@#3E-kLCs;DfjsxB{GRb0Aq)#|F!;_@YYFL5^=I!V{#p?z;S*5UB; ztKD(^a{fS%{JHr!e;#$bql|22f2xS9eMCB{HN^gP{E~eB=-PqC0;`lksiI{%_#z8dvy8$FF|@U1Rxc$^LJ@^?AB~ zfbYZB=xx^5j9$s<1Jd`?hv;1`s}CqwB7H!<-1-pxd-L^S_3|%AALNyNqnIQc85KQ% zzg6@BezJ0y4mK(K8NHL!2c+w#59YR()d!R-kvlx;!9eO^}?6Qx@ggd`mTC^lzEH3Uw5DOeFaej&#g4o@w|;mOvne;6`Pm%rPo#A z06x%xFD!hJhy1K>=i~z&_@crGdEhgQ)498|)`f!(eD%T?cI9_{J0~CLz-KO$`gZYU zrE@-0tn@$!zOe8?9`c!$&gsSZKnK34@IhXjuS~tEL4AS_eD%T?79V`;+c96fUO)#v z^HM3Fi_b9fRWm|*paWl6_#hAIWu-Hpt$acUzNmyD4}69(Sp7tLpaWmM@P)+(UsgKv z#nS^F_)M$4IQ4XFS|3MxpaWl6_#hAIjZOQdqh3G*qka8I^XA_1%Xh40==a)U9`1ugnj6@aJ8o#}&wp3cu;n2k?U){9A<|a*?iE55W(5 z@Yf4J zKo9=I!VkG7`bi&t(1Slu><7q&{-J+zrw@AYZxw#Xh5q^RgC6{_UmzF#pC3Qy!H@hw zF7yvLBA)Ay_qV`K!rI~RHs`nizOb}|DB6RxOUN5aM$~$mpyidS%V>mcL!UNc&Lo^z#aQ^G`q1`&-6qdmM6;^;LN&G04HXU^CB4-tq!1_dve#BM&*l2@;*6 zMm)n%I}IW?5-;s_fZt$Ua7Ws^GBu^Ay1eUxlSBPYqq^hzb32w0cUT!5zth@&S$GP5 z7M`LXz*(335Y_)*_r~jh!_)S0_++|zOT=i8dgZ8IjmPIwbY6sXJH=?1M)KOElf*G; zd^L{ebZQga9;SMvtGOj%qdo1GZONx2$US;|3s1ohcp`_+cDk*=$m^`|)A1A*JO@$R z%ary9Jlje5BoTJLD;{ZxukhTU@nroVJTp_qr19mC=L}o_=7p!`cBRycgN*0LR6g|I zA*ws6_C`xh!@^PS(x@%+{&lB|tw=MX&RdR<(r>zh@e|`|-aUFe#W;&`)UpeF>}!k% zv)#Pxht=B%YeyEnnjj@QbhrBMHHE*~$2wnxt@`@uaeB9v z&tY?tGv1v1Ela+N@22~E)qhhW7=JJx8Q<395yq){d7g7W=i<3j6+`|Ep|XxACFA}y zh5W44*HtZfjYsA>nTLm8B;(Pe{jKuaWheTr%0p?IA_x7Kd4bpXc7CCjhw-SRGn^nX zE+;$nd>6ixcxgO>@A-U}mD-bPgP+j6>Ou*lKP=a8Jks2tbR&=Zt;e_U6#Rf^%}lM| zz*(1gae?+*0ME!Z8c+BQ@Z_^5lE?F})S-HfM@~FDQp=4y`|tSOWBV8I1b)CXbdlic z*-nquGIt`|r8S}$#{+l*Kj3Lj(fuuOj&mH#zd9Qmj^*qpk$2z;{46}DX*_|m78M?E z82`ODE|$Y*@8d8p5g8>B->316-Tm4#F#j@A!ibk3Joo$yy?-BN>`3LS@$r)du>J9_ zhY{->G21$x;9Fx$weS@DEIh@&0?xY9hZPv%`xSmVe5MXZZrl4H-FQOZG=3-bx?RG+ z6Jf+lG(3f82dZS6A64_M=_K*hv$+b4Wq&V(_&g*KA)(5TYf?|kxR4#$7g zH)YqRU#}iFqQ|i3D45Iq%tODkIG$ylPW;S6VT0uslC1kQzj4B~@8xxk{!iUikKCWf z{PQkullSh)lXj<`F<>{*?*2zv$Gn>#%Fg}6mfpFPhg>S((8V?F^Hx{9RZDrO{q*5I zyKcC)YtFj-A^DVt{7e2_mJ{lKRnG5sRzLCUM|(eBpVOhX%Z${V8{6zw`F*5a*FBeq zJB&a1jOU+w?bQeBUU{m^_?20qJx~05PyXJ4(Y)`!ensx#AEo8(@7Z_UpNre=F8up% za)v(kQti(BXR7>9ysA9+_6sKFoO$lks=l_*S+MJ&zNNWmk9&*dTdwcR6{BMLdKIMqXWrUMlvc%hKodzb3Z(v zrin0r@eOIWc3ED(YghIUx z-px@op3}s6#SYnYfNxq_+b&%U-enWdPbxk6u;a6q@4cr-4$t78G=!`#x_zW)IVi!$ z@So$o{FzHi7H2NX99~j7Y}k^_C8I`+7`ixf*yzPemXwapT$HuAbjirlrNf7hE*?@m zJTr3*U4t*LTy_it-Z1Od+2zzeY<-?;Ken)9lrYDAb?29x@1z)cxm~FJzDP%v*XPBH zp!1>IdY;NtyxnvDUgYr;d6nNB*7rmXhnwV--OC&@FO7NXdI@9x8tcd~UyXHL?sa7> zY6!inKcDi9ZRH;Kd$XPb7%AIK6Qq!$blViKH(v+?mU`{ zWGC~QdCxn;I@0$qmy?~Er>^@I@zOjseAQ=16dv(2SF_S-o|-QI=Q5VZatBb2as9cS z=Ehy;Q#bCmCf|B46Z|YZpABuWMC{K zxgm)@t^Cqp^S$GP5 zz!Rl51!wxo&U1#bf$&57L|EgwnJVfwy4WfPp0`TnVc#>ti03rwW9ui!V(Zsy8@g!x z1v=qW`kWiR>btECT9?<@!=(G^x`;6KYoBomT5xOs%XsqsHH^o7slXIW=zvi|!mpVC z>jS>5Y+%mTA%8|%T53$+L0zhLL$_dO!S1s5%ZdGk_2aO=U~gevxIG>2mw3JFd30&J zi!BZBzRdh2U9AJK{AXl5t@E&xl$6H*Sp9&=K|f&b@{m_Ix=>3y`s^;$pS!fjt=%Q~ zS$Nipo(Rsmp4iR`!S144jO#*M*jbzSLIBP`LU@FeOhR6ES5IRkeKlhwQTX-i5Y?4_ zFs5$jw4B-QLd$ehwvMpXJ_SDuPigO-?euFXx*wk0xAB!6p3e7GUqdmz^dv1j1wY`4 zgiXOYZg&+*dxYI()G_TjRHs>V@%W7v!1c@yV>-f!=eWl0uB`M_CL-sDC&x90XIuhP zwtmNWD!WU@<-Sy8dwu)W05I~4KU(g8Rd$!7&Ss?h+Fh`-V0WROV2@$_CHDPczX0aF zKtGfeo%PthE;E+K>oUD{-`ptcGU4x$@$?rSb`lb4#JBnZk%NA~+~*~4j??a{Tki}v zk`70Gk){cfUzZt6>uK=iT^B43_2;)PQ*-NdxkWhe#qCz)TX+h7z_Vt)tm6}$b&*}` zGLHn$ythdgX#Ntu=lo^zc>1l&bmDm&t;-brfM?Ogf~RLYJysR^_*s_;{D7zVyzXz& zPLEXto%ueF)@2HQ7M}Yxp1@hx%h9(klgCfKf_0f1A#3~;{46}hzCt_I)JNgEOtK|m zPX_BU^*K@MeXigKJkd{?f-}Njm+$wS3;Wh@U1lup7+sh7Td(Jf#I?`uQ?1Jseyq#v z^}=K7UAbLC>_sEb9_HUW)SArry38wIdvF)uKhkxG6KNe|5v^T(oYpInxX5r?)3}w^ zG47zXiyVH2);IQ}HI3)$b&QeR;==r)YyP{g`oQnVZ_Nui z-Oh`zW4v$cJGpanoa-1Xudh(+7=Jq8?wtPJ2jV1%zr|9|d%pTg0+^^&FexiN9 zWO!a!UR(K*c;4r6&7>IQ;{|$_W?5m$7yWcO*LlD~2fnC;ArH*%bA&+$zIxO{gLLEP zt(qhZbl@`$U2l*VPtVB*I`D;s5AwjL&k@$oU7Q~1z!w!h$cyti(*qs&>V+>Xew@#B z9x%k}Y2!jrMVc|o%NY5}{RX>3{=)e~hKF9;#Rq0MX(1EXB_`>3Y&wY+C z=)h-+-a{Vv^f|(D96$%Yu<$`1`1IGf<9wh4ALt+t;jDE2inr$DvsBOyajs=V+9l{s z;OWv=u2<#DC~lI8At17Tm`xqPs?d7 zS5oKNexb+HsQ6};9!JgUBOFgf4%(}yyx?40z;@L7R{tyd06dXl=!*^q=h~uPj#i)X zROi}Cdv8zuguBo3%DXHL^@=(w@72isiSZSkYm4+Y%lsnFwcRTD;CknCZ4o}&&$Y$) zgYhW(xE_x%PMI?P;5ix3o2F|#V&&lVF4%XKaNpYJ@_YyXN*Rx?J;L!wjD*LcVa z_N^V2ZP}hFYdjMCEIikWo(RsGC)l@Uue+o1w}q$RXW=RB-Lsv34F&OA|HXY9U%|dL z4T^=Q;0HVbnJG92`_@n+N4L*-s(ouR9`~i}CSxxK`_?qBc$e;|vqtufA3)ri6JOfDW3YeW1T1>(qO%R&qLQ7Ix1lZPdT*e{rslPdu;J&9i-9jMjy`Zu~TrFK^vu**MpS+k@BnxtbffIF8Qs z5&SGXrM)}bDesF(+l$(vRU_kp=McI%N+NvlQs+1@iBG%-o}&m$zWni&Bvt?Bg{N~T z>2Y*kx!?ypQBS7eOf&EKbg~w;&qIXi{(_v2)s%FG|Ip|&x9v|;`Dz?bmh0C(H8+bp zJ{*sqWz<(;Jnl_ZNx_7Uzp->QJNG#OT2GK;_ZoQIkNa5lSWz{tVoB+os?ySU+>!kH zmy221FLV#~7wj>R!A`^c7wj_FVVDmCJ^O5r@i}CZ2uM8-)(A|uTYF6Kv+#UI^h9vh^~CdFIG2xyV-Bz1 z>#jqDaZV=sdxXCLfy3ARe~9kc&&`aHl^3IXUb+o-mdowoJP$lIHz<#zb29}$3r}h9 z&UWf`Zl=c5er~2WUh#7?ed4t}Mwr;o&5V(a-58#*ul?}U+`#iVvaba{;E5VA1?TuY z7}_V!Z_;?$&&>p0I42WxEC?f>(`fwsrrX*umhAl6CqJX%+)S_b8JD2MwE90heyVve z8ISu?i9Ncl2K4}p8blmrH{5nvOJ@)BU=Fz+=Vn^_Vy@TYSkw2#Oe(EdQCeMfwaEtJ zDo1)p7@%*Nx0tJ!_rhj+JGE{#BKu+L`sj7Y=9Bt;YqZX}uGXCa)yFYB{O zH;@nPA28E&e(bdRH^1SC?>hh;_`<>md76i;jCM{w(19;1e2@n|!*K045jybIOBnLN zx4xZ|4|L!&+vxH|g&%ypFNPiJ1$5vG3m@dg`JCy24t!DJgFNsVMt&;KQ6N3gfv;Zp z!s3H3E8WQlI`ElorF<^FJ5rr|paWl6_#hAI-I3}lpU{CXDq+Y2Usn3R=xIM*pP&O@ zz3_#_2jBX3PCn3q5A6l=z-Jgs)KAnW=)ea$$U`_QJ4Lo(Y{U5J3oHVgTGSvAvY@grVC%B4|?#|2|wh9(QbVBK@a{er|JAdF4B$q z@Pi)wVd00|u<-fugC6{k3P0q6zuqT((1YLTrSlKDNY{@a^x&T%{E&T{W!gD48B=AK<^XUqkYIt*5~?E`$$9%p5M$m zFL_7rz92rSlrnj*_l`n7IW^vperqjjk;g25aYHmr`Xislmj`lYJ z$ZPX_3s1ohctV<^oks4a`Od9-5chfP?t;%^LlmBas6)w=2YKMxPQrL*J>L}%IpXX2 zGL0we2jQ8SvOkqCe>`Upmg|Yf4^PeQN~r~c<_)_4if;7k$66qPW{O}ud51dk_Y&0| zSG!+6r%sq%RD98t;wzU`RjjUDRou*ETZAn}js?MQ5uq(C1oeyhMExQe)H}kc-&JKL zm8Hd3lwSQcR`JnXuc$AsU*6XS`=j;jYwJK?DD9xyBFf5svisU#|G^#%|5V$9uoGcV z!Y=l+Z#hkVf6A|W^Qdpo>)BpcJ+s#wcKt1SJsW&;pYHF>Uo@}Zv&I{dgYm|E-a}qq zsF3s9BHtaX{~5`mbGzZ&&H?22ZEa}0>7c$jsNaBk)rHQBuD=0PcU*sNcig@|75pqb z_luqg&bppx{hIN_o_L*c_%FWiPvdm@7uK^qCw7D@-fcgq^=x}mUN`m-y&s<37SJC0 zd*JD`=#QiCr3ii&p3>f(?bKsEo8W0*&*p=dwVur<-UH83ge718ctR(*e@l$#akQRI z@B^NxCsT0N-;dC`rJHE#bVI7~$dbX_e zJt=%Oj%Q=-Q*-O|kB7fJe)4)YjK{5IJ)0Xl=lkBo>)G(O(pt^-C9h|L{RMl>d{*8w z$~;{?<`>(x)AMH%hFym8-brH)>-Tu_d4E9)VH-_T_E?J7m}PF!^SGA(qKvao=yV`A zS>Lk9L=O4~^H*A~Q;zGqI7XS%K64T`2tAOdZ|U#YU83zV13vl-z5YqtV^R7h4qyKL z1M1Ije}U#kR-B_b?zSf1!c*`Ao}q>E9TmY@m)Z4QoFlA!8yyBz+l3frHTzE3uQm*elavh#`ed|ui?&r3TRv9Edj{5f50e|UXIMRV(P4TGPT z;?5_Vae`y;moV&E3DI@8VD% zc6aW(J-6bs6LU|c@8VD%a-K!gL;l)9 zcjxTsH8A(%Z}i$Vrti;(mVLi-?U~nZ8Fx*aXwI0vlYeqmpHchFhqf2=UAuqpQ|U!} zV(*PCe0a_Fz1_#}FWA+tXgudZUtHS29WOQD{w|LB{#MdQ?M5yBWy0^`Fy%EbJ(@GV zzNmI|k3+TFi>B=M>X5kIs_){+{6*8hizAt`d9+71hq^_FGi&wxD#y*QB#>gzoJD9` z;_u>cZj$;gPM0Ch(fTVXMvzEQ14myCH1=H_cW%}Ajq!~4(c{8Bul~s`q{-oW{w@x< z>htgmX=5QVP5)W>L|I~^|gAQ z8uQm!FA$dXz^2SsWB%GMek;jfJCx_CyLUH?J5v4E4dlHsL(O}`H=oqc!)C4a?Q)a( zZ9SKX9N6*ZPA_@iD$p_y=&>xXAI(j&ldul>{^fGAQ}fh$nR;FmejGm|zHZ=-)Sgru zjm=ZDJeKP>Pp!F`ie2mP_bogHKMT)iL{9`~RUHw=fZfrDC%1=7L|=3s^zoJsy36!s zq;KIV_yJF(WeUzZd;zt`5w`aE|E%`;9<`wl>EbmXXrKR}FgvURK^XBm7Js$Uy_V_8^&#%9*F@F!P7#4&wU>|6!r$cwP&FFf+5dRaUGjuP(1x zY5eK_AA$xl8_>|gXXF#*S>=;Y=CwP_Con(n%Yyw3dm8)7VP}Wk>pjG-j>>(=#=fk7 zQ^m%=FEK#1B(FX#x_N-AKlo;yjH`RJ54p+usyvh$JLaiXdN!^s?s~Fyy}9>i2C!hr;poxS%M$%ggyXg9lo3}cw-OZj0j;7*|e9#6GIK9&b)$!&eXS_N2 zTiajdfnNB{Zo0qc?_zNNksjMq_OR;T=^n->j8}9^IgDfOec0HKjQz!+XP@6I^FF)9 zbm?>VX7Fv#we{vE8Lw{B^YP|B8LxhEgyWUSLH}kx?;)=xzdvCB%lWez__WJF+oeAw0rGr-ORD zVjEPCSE)2!okn{T`100myBKZ{lkGR;x3g`NWYs!72wTZ5)!$GDWD0&3p3>f(?UeT! zJfiFkUHqLEp4>;{8O0kf!{{b;<`b{ye<&~UbN9LvmVEi+iFGr{;dva*7YKg9Q>xy= z+5g>zD=wO@JIDM#`C)(feS zORR#%e|Xs`&U3a>`_POiOFU)0VqO8`t0}nibHlOZoOTuKQseHc+TV(E zgUy=;sClcVpBp@-nrGizq_6|T{VX-^x&T%VaSCZ z`0;}t{86MYz6pJD;fwS^5B|f#2f5({{GbPa-Wj_5kc)hquKGv%pa=g};fLIMN#Bnj z^x&@-e#k}s!#?SQ9{ibS>ik14>feta^x%&OKjb3ce*B;Z{{i8LTuXm^@(+6Oo6=uG zt||2w_2CCS_$!4Ua#8>FKK!5uKl*FPjY5BX_(2bTv>V7pzP-}t_x~6d@IIyfMd$c{ z^i4@WEIx2WddtWFh;=fIzI3CUdK7+-eNXCS>XUdLn%u&Bt@l10jE>dkvcflitNVNN zp!S)@?hfX?sQ8O=wU0QK&yL%JI+s=Cz-~5Q^^*4-&DXN)H(q&p{ppvj!`b|P4L*39>ks_H9p7bnMSs+OA~^%ID(B$+ULsn1{QTVT3Ok>Lr{HJdDf$6@ z(K1GjHzGZ3&#@FXzN^A~ChK5HsEle2 z^GH{7>x5I}T-IKOnI>5=LeU2_o|&^$eP)PX_Q4E=FMQA^>xW*~I1c=uUtI$k?EG6 zwxMtv(bF*H=?c2oK~KpLR1R~sm%HM1to7GD|3dHmu1Gc8@32qHeJ%U5*3*H9wC_li0?{o4)bp5mi}u~|heSU^e@;$ML$Abf zcFSrkynkH7ey5HYkbmfDQN6Pqp+D$yL$*^-7gwyTE?rZN$Rzx-o4Z^6=JT|^k9A=T z@xf;i&S&f-gc8ZY4yUR1#ClpsVUz>q8u%CKPXw=;lR}R6H+}pC6AI>V+mkXCS1h`` zbaB!}+i5G;EeXoS>r#?++d4YSg>s-=hu+ZTLU}`{`jxA?s$}J=r4>~xl1*CC9g=>L zF?oUdm6?Rsz0^S1uabrWyx=oqNb&_=3A6o~-^p1n(4ky5QuVVY!>?RRN|#rc6gRbW z1J~>R-d-+Fh|8s28{n3g>s-=HE-*3p}aMH{K~bsV)=4vSTwdZrDQv7<@!c~ za*=tSwA;4VSuT_Vm|JCI}d25FGmFvp#RppDymzP&xophxV;SO85PD@ZOjQh#RKe*pnE|df13cah# zh4O}m_?2r(`Krq0B~2<6Ww7QAJ^pgPariUcA9B9E+83RLpS0Wd3un1d4y#;ey)arm zp8&UPzj7^Jy{fumMR8?SMP(E8F!cL)xqjwTF7$`|4l7xg@hexkq#P*Mp?6DExlrEF z{C19Una6V;l~+_ODPLM%TGfOisVo*f_1gFFyyZ78qb$4@Gg&vkU+ol@3ws0QK)FJ_ z7U*(Gd22qq&$7=sjj7WMxma_m%1c%*TTMNEX|gG6x{uGxS9TQZ;g7!1diXf~W<6xz z?oPD_68z41Xqe58-=#a)SMG9*E@t4mxmpE zweDH&iKht?{M~kkvs@?#%C+NkT`rWjrp%{Ym1S42Dqq~x=4E8q@cOAoxiWY?w-m^F z=uc24j?Q?{SuT_V<*MnU%Z2iWmiv@zRrv~TUcBX~DX!+X532Uf?Q5$?x$JmR36pji z5oftj4wS3R)a62XYbt!oMW3W7U%9M!RcUpTFe{rH*Td2Tm zzPel}Z)mMgxk^?qDX;KN#y8^Qe(`dRNl-3X#^@`{9x;ECv!6#fP_BVz=yIXFHP`u+ ztEzli`I6#A6-%xzzN(^X`4W2~jr=aE)#DfIVdn(p!g_~B#92`XFL0I%l?F5U~0tlKuyi5JR&a$)`nxP{L2vk%Kx zuBt9sxtKbSWYcE4f!8^oQ-BkEJ6^n_Cs{ZDXHNSNs5Z@j0n-9cyhl1}qB~i&l!geaxFX|KZ z3vSdq!l>WiyH2Q)#(l0|{;m_=UxvttOUCYQicQpb4!*3|G+JXB|E|-a=k>TyT%KmM zw-xZ<`IoBao6xNnsrPnVA8J3jygz=KY+EL;dr7}f#(QSGN5*^cu)N>Kdu5ag??bO3 zW9xkyz|*LH?@8T2I}%0iMb7uh=I!!60sbHKdtdW)?L#j7o#XX;My?~^Sb>oWCzt~N%~-Y4&)y5stDyM*tPAJp}maj-(ae-z&>r>Wun z@;;}gKDqZgJN~}H@f@9Odi%#We6OR{2Z%lZPslR`XC3DC0a<$yXYl%fJiSi(Hqz^R z=;Aq%`SSdR^#GVBK^XCt9Q8xvc`soS-%ox|>Vj0hoRs#Ey*=#p#52x~W_njj1%4W# z?Skib52^ah5Z|6gbAA{7MdKKHBS}18ZQgsjodpf?D zRjE09@#^K3O*P~MRK1_vLYQ6!&`3o4$#)h!rRD*-|K@pgq_>%Lk(DPyt}c=fri)Mn z!kdVWY1CRg;=sNoul?k_Y6bhdiG@NGZe%}snyWoK*K1 zic^_Fz~e8*Ta3R^S?`N=UGDX8^|GD|>%2G}xKet!?n{BE>EiwI zOFQ19^2B=->x!M@Z}ipy`aBo$%|B_qHQ&e% zjKATD6C7bA>f>`c*(n{a-%YY;{Dt3OJr|$nvb5tMb@{LVVRRx{G4hP{ z)^wh-t@h?Msi=df5_Der~=*{H%>qBo8Hf#KyU)Zp}R^zYWXW{vV#uNIY zi^conpV$*GB8P|B!togX^@0R3Z-DT*PBIDU@t5aL;``(G(;6_oe3!{slDIu=wLU|EuhMDx4|!S$Il&_im?OeYySiQ*+xy|CSif_&!1l&;M$r zcnW^N6Nmxl`2P5P(jL1!XBZo(eb%e+B~-6V>EbydOjo0S#XJ$hi03pK-ygrUBfV18 z&SxDPM>dA1O@iucnSN_K88+y5=;1F1&z4&mDih z_(D8KHYL@A6TR^Se!#P+MDX-#r%g%lE1*UH#(kXIIqhim{lqp*!&oXjA9nVCZvJB} z3_BY50Z;R_c7A zGw*oAcwL33)6I(|;?J@$1LwrUHd)|`kIPZNa_O)A+<4j#axv}4=8Iw)v7=cow@sd- zc01u#w^Iy#Zakd{VYSiFL(<<4lYL9#ht^)K`d|2NxrxF9-`D+Y*}p22nvdA=zs;Ms z2n}!8-_}v)kZEFbDl>SHa{ff@h{dg~A7{=RVCulu= zfI8(zxML;zZkNZeC#t|<)wR7{AtXM(2r`YuL zd!nbW%lj?y_fM1cJvBL6UrT2wd%5iI>m2wt$dKGPbXe036o3BJpUmQM|KMuvyZLG| zz3sdVN!Lw}@IjeBMVQBtItn2F(9<2$b$LKnb5(aoIpTV%HME%36~sySWwC5kPyF1t zoYtjoCG5-TH+;JYdX<~B%gAu8>ydJxTxBzLxlrDkxAndmBoHr`Zhv~$QgKCPk{c|F zw&?c9<$8eDmvMQv({J|O&aS34GjzV-yJ_bie$25x8gwX^n?CezQ_7WiyU(GY#Mfb= zTx%&&E*JaFbr~Nz%LRN;uFz(^o*nua`c7|$9?qLS>%!SH@(a`&=@ljAOp{qw)# zdRQ%ZaarPcQ4b*frpK`6TkLITIQ0msgdxZ zC*yjEa%~`bE*JaFbq5DH%LRN;t{od>Uxnyn&5a4lwRm;as*0qx8mlaq{WxB(@6x(C zF3$yY!MB%7)X{#+_xH9z&T^p~DA)e$b-7U9&`o~jYTiDxfi}FZmU3}sxm?=C+gBQH zCE?FM$5}3v1LZ1`aTw(d85xd#Bi_$1t12y7Ra#tGN^3ZiAHjA!8^>!}f^sP%H;FPa zNyd53a-ke37sQ~vHBbD9V;@YsTut4Iv_;)bEy}{ zz2^9wjKA<+S5Ld$9UEeG4kzh z-;fj9?QGwCszv^s>9crCSXJo~`>QapH0p1YeQr$jPio(+3vbeI_@7a#iWmoqOF%703&w;DK4 z`<%Yr?fAespFHpn^Bv=z`MUEv^E_9>&%*2)dtPQdqiKYj@Z&r49S>j5-)MEX_sW0K z;9kYaX_&5!5V2g5-0yasmV5X|H`eYf3Gs8h@!r1fxmkBUm~;QQal1Kua_pJ0FJ_kK zPW#)-O6HV~_tc&oexUZYfhn~lCikbrzxZd}bHj(98B64m%*Sr5eUrC-z8_?N7T%dt_{BUGUsrw0v+LS@_UvinH;!ih*|&!CqT#dho_qY44_Z?&fx@zB4cHd7J*7dDc(6GxODV=BY1$WuCv`9S;j2xdj8IQ7lXbVPwCX>S@*4R zmGhHFm(G`tk5IW9erKNIxC}z8C^_6Dhx4MNTlIU8=cylQO1Wpxnp#08FEw!&X>wUS zB{{OBsKT z{GbPaRQMqm`L1{2hV(%X{=>o#xk%TKAN1hQ8=%V%xu&EGe1Qwn2R-<=3P0qc{{8qt z5B_@Lhg{^_k012lNB;x4&>!IF&OhkEk9f!pH-aDUmq9;=b({6CIQu*ByUT%kL3;S! zEBDb$sfDzm-eYRYY-(fL)(0K@=tw#LSbXzCdGG!&?Sn2^pYc@liXsQ|HReZN@|G89 zxd-x<&)>^cPQp6i`xoEK?ZFf{_ZYryoaX%%dcSJ&-#?y8b;tGRcE|1aj|D#qPtgzP zi>{|bbpG_gz41DuaNe|i9OkpudP~G8k9y^(UX91I4V4$s^TcT+kLHPc8>7?s^44t@ z$L(P{UECg~c%-YjnUY<5BhBcdf40)UR{IqEfG3LUXs4083+T<-9>m3BcNct-tMME} z9ZIGcq`=dCPVo7zc*qf7;pv?B&rBJe#+N^yGYHG|qyv6azUT8De=%2o zHP>rwMsh6IZ#;5-bJvYLB(xsi!c*`Ao}tCEk4tb?C5}Akv@ebXPlI(~f4*V)Zu^4w zAU80zcrne~IAf)lQGuN>Kaqj@jHNlX2h){D7xv z=>8ixCnM|Vr&0Uzj{a;OK@Nc@@U!qt)p!DDodkU+yDo1Zl2hNwj;vPUA#`(;MEs8W zK6dwO&%nMsd?y?65)IEi|6-(*#8=b!^0p;1kSn+)EKuL^&#()Rro==`GiCOuk8{Bo^d|UUkcAH^gJi)XbDfYU$hP5XAiM0 z1bfm8Pp1VGKYxaE(5mqi{46}hUIxw@fm;d;WAWZN8XR7t!vCf{Xt+;5t>3uEes`(B zH|;m$zZIU&`O%D&w+z1g+b8!4eEGFcC!V}68ng1P98bXyc%qt3!5QJN%lFn0og1`I zZ~OLe+SirmBT3h+cdj2!Ru+$+YW=YAWBqXN7arsG;&5;L@q|3b4GQcqBD<;IrAxaj zUwd#D|0?|ZZ*qn{_EPQ6`)4XS6R#@Iz5RlTIcJ{xv|10jea?bi5A`k0J$u|+YQ3zm z8`r#b`*YlnPbB^#;(46tC@wObWNjt>9mK=oXGm^8l69`;k3_zAMgAWK9jL3HY~(Zl zQ$If`_xau7-M@Tb=5ETv?oNHT=ic-1!JKaAJXR$B~u?_1oJ0WY-FN(b@58UOh!} z3ojdy`_{{UseQowX6=pt`9Uq~5@hvBTdCHrBg{U}H`!fd{g$1M+pStZEb~xb+WO(z zULNyWtlRA1>(G(@byh_SuOFsfJy<`i5en812kVDv{jar#INokmdzQ9s-LT*CH*xl=gK?lCD@HI@=wGLS5z!#M;uc`bl@`uC&-KQ zInx6j_<%3ufsa;Bsu>QX2RiV94)PGrN{{pLJ45IV)^8pVJpw)I)tzrnm-#^teshrM zAJ(x;J-PD_e$a!zQurYk>H6`59{hE}54p&H*o7C;2R-<^oUQW@xn_d=gC6{0;fGx4 zGs=N_Mf#uz|D(bWx!`x>2!7Cm-;nkXxyZjCKj^_fL---LUdj*sa_1lP;79)hxhS_E zKj^^^`H+izd-3liV)S!(zg!=3_IF6%DhKd%^;1~i!ShG_ewp8sPEGNDzZ^ZGx9WG{ zn<1I^?5KT|BUzvERO^RD4(4mj4qoz(-u1(N@0X9;^-hAHg{SBT^hN6juOI&F-ngz% z*tk}`@35~Q{=3hN7uFB^#Is%4VEyo6T0hJ)XnxPJ_VvSF^KY6PIX;fo4-0<46E)y$ zr`rmQNDqadj;FBTXqo__Q*$GJ>)`HlUy##51l_G%vXkXJXlP|Iq8^;-CD4Sd`DvR*&jwDmm` zDPvrJZs%Oz<9xg2rfm&j3s1q%!t+Corztq=vhw$8w(W_R`z!o@O)Q?O*Dtm&CwN3S z5hP7%m2a8j1-I}N{46}By?eIPIQv)j%d4;A#O><`665JyKk$`h{wqw#!c*`Ap2&hJ zILE(VQz`8e->)fB;d5v*DvK^Y_m$WBu;aRB$M@JFjCj7+`1fnF(zBR|oFASXhw;h_ z&$tApZ2gY$oIyqe#^b(JWVcP>RSUq#EAD8z_qBY#W`_UwYhY)=?m}H*J_GY0Cgv}E z=4F_V^|*w5p2tX6?de(dIXy)`T_G}4|)7uSLIs3 z?yCD0$)a`D@Q={$@|(vvZtX6?4|s;w$hvC5xe=B4N}{}U8wqoJ;AdX1=U%7X#p~p+ z2U}A8oke_%{6PBze!z48)qp@U!szOydchHF|mUTzu%cIFbtsjJmRY9Oj$*C1Sg>79?Qv z{@8fDdtQ>>E5B|$n8ugC-95K$++OkOpEWl!p}!-7PzrvuA|LJ36#Oha#U4XD)kUU# z62^bYa}?Kc@%Y$ojVC`F@p@=>et2S^gipK&o}&m$zWnjjDQf%E3s2{G%ljnaD8_$5 z%CYZTcnW^N6D0%AI(%5{;mw7A|Jy^gyMFp`?t2TLuFam;@T^tv+kJj+X2}S5Nv)~5J9_9D&&VLdz;jb~s_SI*R-iG=9eo_@G-9ja|5D2VzMKl9JeRs7v$ zT}ItA`q`1^7$1$iuftzQa=2jfV9NXWQA<;5Mqf}qXUv;F*fZvoe{|0NsAzKbUC&I) zp1pfY_P|LKvIlmboW1WadD*}IexbU0&zz9`r(OBk-S5oHj@B1s@B8`q>~q)UWp{jW zZ1%M;6lHgOYfN^=+VR<6Y%0jUw{LEC?HOaU|8GZr_6I51*$eKPo;@qFG<*AFmDxkS zzcxGa(WdMV9^9Bc(v;Wk!fA(J=J}>+2?Sr$seK;z6&O5`h7hQ68_WRKu*}wiyyX;5% zzcc2kZ~l19>`}|dEN=UE40o+qbAzI_L=6P(KWP6gp+|v?W3vW=_8+wWV^ed3T(?9G z1noa)|1F_MfsCLAf*J^F;Ml1F>;u=|lJTE+Ok5(&C;s8gTXTXdsDnTU0v!l+Akcw8 z2Lc@kbRf`yKnDUH2y`IOfj|dZw+`^SncxcQAkcy1SqJodVH4*Qd3~|v97_}V&=cxc zmM&eYzIn}Y9pvH|c5Spr>xZ?oz8ig)K1ZPG=f2HeTvc9K&F8+^_mztHBl5Z(5x0{$ zD(8rWf1}HJSk7xQhw5{y!X4%OqkiH$*{u-QDb}P~Ht9WxLq2fcSiR({p$ONx zXhH`*vr{{Lj*^SN!MSfj2fna`8>Z_#PYrb7iwYmoMSk7ijRqa~>V+>XKKL4(`zG<= zGdoN98t^&u2|DnFg%9b*)0?GK9yovwd{N13&1&UoZTS3qI4OPv8eV_%qMb`G;Jj@771~gC6`5 z;fGwL>&Fjz@E;I<$c;*SMLFE{2YT?EgLVEP7yMD5@`E1ymBJ6X$iE*y=)qqn{E&-s z*ZZUodhmBSU*{ikp+A27pa*|g_#qeh_TvXV_#YL1$c@T)V7l5X@CQBkjZB??$VL78 z@q-@xGlU;@Ubgx&8P-4}Qo;K9FxO{+&b|?x@FIoEK4dx(-7Q^xxeM(XK&fKlgGK zjZFI7%P@T@(tD3zeMf!nC4AG6?<}^_KIA6rGoI?)OOb>9ab~KQyrcKr%ZsV*xc=Pk zxIOn$@U!p~{eZscdh$5;GIG*BjEtGPLKcE{1Vmx3Sg)a9prkoI;Oxx2uA?qw`?cR_5u#?yZ8CGhe)_Y(0)Lwtqj z2HGz!-}$2NHfN?xOy$cTPy4x-UU+J5S4yoo$o=idR6g|I!MT?-{!o2j{KR+~?W4z2 zjI*ZL(Ux5Y`_N@yV?3QrPw9OAQX2VFQ+}!T2z&K8(R<|iS$s2H#?z43_?K8Qt?^Xk zpuL)2@*=ktXo+(ElKRd%-;r>FdFy_E{fc+89)8}DvFpZvuOxyRh_zV;CoAGlTI9xwHPVYS9gryVxBxk%i z`CHa`Pb$7!J2elk`fo}T;}6E8h^fa3j8pFCdpvKNuIF{meyPWs`uHFs$aiARp=4UKO~5hsQ|Tua5%>X56a)IA!yPq# zb!pnq<8WQRVH6acO?5hiE_RP-ufbogcXr|3#?F&_ncIt;to$Btf2z|Lgbfv%}3 z|D#g#vK`HD>vSx?lk{8B7p#7^DM(s+E^?sfrl&mq{xQ22u%jb4kSyxA;CuF4$=lJR zsNT5#-0rxwqXj?UiFz>wXC0nE^B529MVzvuBa0~f*>iR~dXEsmZbq9x81a@I^+Wet zTgarb+tGVTyE#EG-DWn}(cB(Jdg2++u1cnr|Kaw@b~M_1Yqg{I!;U^eJj3eAFR>ks z^k7FnLFw{!z-CA1oe&SR9nJCVlpSk3-;Bf23+#3;%{#g5WV2WNyR~dN)fYMGopv_i z?XaUMEOxU_pTZ8in!*md-JL#j#T}<_+U$N6cG&q8-)Q<|=V$BpBP>-b>E(FK~r- z9B{_Zam8Qe3YWRURj%;0uJ9&TIPX`^^e%9P=eWX`xx!_xaFr{3tt-6A72fI!-|Gs0 z*A@PuE492K#H@d>N zxx(9A;q9*Qf4Ra>y28@1I3U6_Rt0yRYc-58aFC@&*NA~alSD1nnZ#2DsmHwHI&~(VR!h;+s^z%-*JWC zb%xFNoZ;yE&anA6XE^$IXW0CQGaUWU88$z1g{$k5@3fa1)(`BPeunAI@2X$q2lCm6Q}rYG z_A~lA(c{jTuiI^56b=gIHXSK@c*tABZQ`V{Zdzb4y{R(a2I zqJLXj|zZ;f$Uc8UQdLJ$ayDYNH@jWAc580k4yW4b7 z(=}etm}Ylvhg<%3J#S*}IKuf8k%Q-L^Sd7MBCi*yOk@7Uh!i@*I^_2^CeOoq-Zz== zz!+_MQf<)X`@D_%^Lr1exsfB*^w#aH`7puH!t)`~6Tw;6LL^`Nn8!Z?JoDZrU7-0e z_@47&$>Yg(&3Rr>DQ!T+2{bp;x}Alm;Ai0}?cKATehpRP)mL#gxNqZYF#Yy=uk37* z$IpU;&(QXs)tX!?}gzMm`=N;t5^Ov$7&+4zF@l1a!7>SiN z@C1IqbIvlxGxI&&SxfZJa}Hrq4FgZ$2Ry}Hy1oU@MfvYobff58LB=n}^KOkNaMrk# zXGYQAZb>kb{>s;s-#mxz9InW2;fVXNi*xAkJmI76>~$Y<--2n;xoz|ty8R?->VN!) ztaI%v-C~ZSbLbeq7*FnB;XT!Qn%onWZ+SS;*#zycd}7P(G7#hau>Wr3h`*JZW_eXO3DHuQC30(`u8UEMG&FbqTExLuU*-QZQvfAV{hUum|apCf7Rk@4j7$`r|KgQUMx(vbf_$@gf<>o`qc`Qp-r zGd5rF@TPzK=N<-JjU%c_5 zFaO7XmCosUen6&S;9n1#Id%HqRW{9d=#w*K8fGj#=5t+dIOv$tZhv{$L+i_9@BP3( z%pBOV?K{&C_|r38>&xrEdFXY=KRjvi#-9J({dZg5_wwnRZ<>4jtZz)+xaHUDxk~AS^7FQ(D+c?&lH}(b|Mst4Uzn2QXY1G6yPuh(^TXuP zkzZReL-LS186DSk_q%77<_$CPDyFSUY1V~POV2;~vw>U1rw2at#a|6vEz1&keQ??_ zr~L7sdrBYr(L)1YIN>Miow2`&4OMS}54rT-o+qc(;rqq=_`NmbWZk#LfmA-tOU!tD z+nHTsBp+PVPMYJ82v786^GIS8xl7VU)NN5mGppTLYbodlz+H)H$WMZ}Q~Dor|YUojPez z=j4+XEneJrQs+ez7xgWETi=o?Q%+iV;=(DNoma@;@fWXNI-+z|lJ7$|ckCHo%VO~42J_-uar^=K|l_^v6({`9yyyGB2W z)+KbPal8EAsjrZ_BmLP|`nzV@>bB#K1b>Vtmj~d9ya8wJ=k;;XAsW|s+~K*@`nYeU z8)UFj_Aq&}pX+yzATig+Jq+8sAp z&jIdyn|5p5w$>&lb8UU%92Y6kNBW}iRr-?`t#8vKIprr>imN*((R*1 ze9&)TdhiweUR1631NuXJ(AStAd?xA>=)E(*1zDDg@b;d8olgo!-oVA`_kb4FEa!aB!>F+*Ujonc{@o;`X z`v7EJ*hA6toa}RF55<$(506fg{Ibe6$)NY)o$^QT!x+dnlgN-q|)3p5`8kS$Jw{ zOVSATQu{%BD5CxzIeRGX712N2=GlH}dsH0)Pqe2Ix1VTFVLzaqMLSwXd&=!7+Es`^ zd%8^49myVw_p5Ye)j8r8-EPP30Jo=`vf4jL@w+NNQVD|j;C&TY{K6iJensvePhvb7 zKj4Yv0cY*c+C%ZME%eOX`{l2h7<;|gd%xg0M`K5Sle70w{Jh-zuQ)w^OhWZ97v~_A1etYiVh&z{FjLSWEkJUdsS?Gq*?EhS?r5AJkvs;S7G8-aJd^ z6Jtb=r2VlHvTrD#bmd>mFmcyIq&DiUWgx!3wG5hC%cY*F-T%Ook>bYf5#t9u$6m~H z-x+6>-|CHN{JMvMXGFAeZA$Fw_K4&pV}AMZ^sQw`<2icPG5|l|x%CppGwVIwX$$k{ zS<3+YfTwsw*SElVr-d+;@KLmuf$@v++^X>e&RQ)O%#1cY`7l(pefvVWmSLUpn`;>` zxBV85h`H}r%Yf(L!><1RBIgSxbKj@QdGGY|_y38O_|o^Zthw)+nqtvKYZ;PjB(|k- z`^oslc>X}+iT6}%LA7y%iAECt?J}_5T84x!^G@M;eeyj`u4SP9i|)ht>^bTSp+A|0 zXSxS8iqlsg1cwQ@C zpyc~#H9AOs{W?g}Wpj+=Y9#&hy`O13$I98f_$p6wM&1>_pNt>yL{0_c4F7B8`tYRm z;Qh?{-ZF95mt+aq`7ZIoFxRFqJ=Ufip0PIN=g+>b+t!S4ZOSwE{(0$z4|WXva`ykI zwI}kG?fUNz^>zK`jpqy!kKdQhz4t$7Utapp4_>F9f4+0~iGTRl{U)vW`oC|Yxurx; zH1hsl8NKrFk{8hv4f%=hOOn@%n!fVUM;CTqzU_OPKKsmXmN3je*S|E11HS+ zA4*xi3!o{_g+Y zEz{8bX!Xd_*!d51J#)pI?_GG%Uw!i@8~VR{)jm&7U-ZtOcYW@KgTM0YA0NNvZ#l$6)cLth=BIpR)P3*uIzP9{{M_^Pjg!9huAiy=ARQy!&ASdIE1rp0F+EjE zpLy%7QhEIWgU6mbe&Ag_^9HCaf!C4kD^JEf++nXx;qOo*b8X6=W5?!On=&rX+LUqf zPzqX`GOKS%@46Lh0l}zqM|=J+>Rq#Z(ZUm>%U2%XcSV0RD%Ymyx-R)fs9yrrUd3$| zzZlAY^Tv%O`e9uPx!}=?e(ubF+p*TBVE!=n2re*xcv#OUkLdK>A<|zD#ptkvh@66!~A(-<_WWVQ1D*IMoj>pcT@mpiu32tCG&uVWQKU4D>EJVL7q8(nCK7>bXBGUKhQc;EFVgL! zM|{vnC+qZsZ<*^UyWAoDh!6U?Ob@h5osG zqg;X>@j>5x3b!ZF1L|oHeuxkH4NMQdNPpQwkNBXkF+KPq-}ZZm^dmm#J5SZ=2Vdyj zN00cRuP{CMBHccE#0UKoOb@=$f7PRZ#0NdvbMQs^@zEnb=z%ZzBHdZ^56H;X$-2G4 ze2vPf>HY)c*HDfaN4%GihxtnzJH1* zviODh_<8mETE&a;Wc*@0xqJX;oxU(1|Fyy?uiO|<#t(RcKK-83e0;r^9X;PQx~XSu zn2!%t?GP^E`M)wHsJ}cC%UjC&)&T?7JUHcUSol`RKL*dDoUXM@N*msxuB__Ze=F=IU^hZ=U7mR1-d%Clh=$&Ui{@D2!#%BRHqHTIVY5WrOPjh?dYx537lVR+raY>De7KOdgNmoDmm*2i-coh`xm z0Z*kQ`A0C$+F#-RFS#4O8$}1)Pol@pMe!4wmuAR$WoiyMT!$b81PufY z1PufY1PufY1PufY1PufY1PufY1PufY1PufY1PufYj4BP#C*tHA;;71c$ZXI+&_K{Y z&_K{Y&_K{Y&_K{Y&_K{Y&_K{Y&_K{Y&_K{Y&_DqVH1<6)+1r&wrKYUz+bu&Xr6o(2 zlqj5jd+<*>>gr%uPQO%_TGt@&ux<|P=*qM7Iy$Vc!+JS;zZLAC!tA0qN}<^2nAU;G z4KkzG??UV7vgbZm|4Yxehb!jsy1Mg*xsHzcVBWA;kj3x4vvj(7-BtCAX+JXMarcya z?$f2#(baB}v~nFC+{|@Xuixbmsg0r5(LsN{b#$6q%O!-BV&NTuF4o-uzZlPRxSTM~ zI_uqX{`X#65;a^P{ZA_Y_hjH!esH@-Y0nP*JY!z}bJ3UD=$Bo#iJiGGf7U$kG=k}M z8l0RF<-~Y0elec>z5{2SXQ|hD2)}-gJ!D^=9YrtCrhQ@eczVBXYVTy3p+B;zd;Kn7YNOxKc%r?^!c$Wd zyU}sc`?*Z&3+?gYQkBwM_eyq>sT=h*iBNkduhShk?2e~aM{0NPBq5bjrHA(oV7K~K z9S`n&xr=UJ|Jkb+_buq}>q{tyd-vZK1JeJUG7RZC)T4i-YnL}@1|3mvPZa%W##y~< z`}&vnuCUnV_70a`)$dXo`j6;O3f{LD{Y>n~fc+B%?>~Y5rJ3LfvHw#DK0QtmCi!hWnH3 zX7(rZ@5|sjCo#Q_KiwPAc^O%MAfS#5_NZnCAM3;X4aY;K%_DA7HW`DAMUm0&#$#J&&`THNlkN6I>XX8m6 zlB+|O4(fT1+$+YD@r&`C$9OW%S_8@ZnC%~yo)n%H{eDVxqK-W0u+@{8en0n+JMoPY zJQ=?jPi~I|P;Mx&IhLp^(~(3Q2bS8qeLA+I%inA^8Gxz?LXV-SHHfc_Vc^+R$G^* zwgN}6m+-90{Gq@62B{NOUqUtfyAzhJy0q_v70XwwSl+ubdf&Rft4@fPFFhgB;!cQ; zpVoOov})xN3R`o+@Z|*#K?6YpBT56S`Y&0tx_43En&U5`T4O}zEo3HW;5E^J>>LbI z4*?ev(JgPrMq}k!Pwi?{V4UnT(53pZ(#3a4e5X_o%#P>*ipTxfARqWf#1Q@r) zcS<^6^Yy!^@06K)eu)jhUZ`|AWopRKUdozB(bN!_}j=?x<92fO84^Nr{L3pz3;2AZ9=PzZyJnK7U z8qZO*e=Oq%Jm;)pJTu=@u3UCHXXn{Jwy0|H9R&CRPjSAkZ}FZM<-cRmjiUWv8NV3M z3pAdrt-|6(Bu#MwLN1OGf_mb=lLmKSX!G`&CrB=jpEvT>gW1C_dZX^5>YVT z?guPqp0!Pxa?JCWly?7*N7UZ0s~`E)z{K-z8yx7n;d_!_iRd4bG_PzgO_9+ncS~M( zO8PfT8lt~U^1Vm$8ms9m2mil|W-J{0%BJY+?K6n}rhO)r9{JEm2e19%I|gMM27me4 zjit78Md??6{VPgC*Z2Cap7NO&^(l!wi{*8Snne zx4XV}>XD`1FZ}b=XK#GnhYfUMssFT8uX7+EA8Ch{HSZvHy$5&;Ffo(^v}M0dFhj9%r4hwd2PS2}k3^GY9@DvHKGxk~Bd{>Szyy>01=!TztjYf$nSeEs;_O5gam zf9?9hly4J{u8+TX<3n4&*53WhoaYB555<#s9Qm~sGb9hGlhJWqcmG{Vzh-XphM9Po z^!e{ArPu#%TB-f2KMWkZ$HxbD`N)q4u73WLvF<1T@rIL6`PQyor@rM!1IvGV%OLTk z?-SJwP`^)mJU!vmD7s48#i^^ky?qzCyn$w%Ch4KG_2{Dcs5fss zxT{0vYo8Nk?@I>H>RZygZpGTD@A!*XFIBNd*PkV;`uoa8I z>8i*)ZIkV%jw91aZ7DtDAL1!L5#+w+pB+VWWd&8eRnJd;MAjqe`f6xa^XDz-yJGFS z{ys6cclpY-Lq~4#sap5cAdN4&c!t-T{Y1N~r|WahD>JlPJ6(r&pRZjX9}4%K888$L zwQE8~Ksp{}J(oFMF719Q2y}=Cx+>FwALy^yElr1bpsPU-j@%@jzE*I`9MC#9jB3<*Y51hj^f?G9CCObVqcUbchGK8q<~8P3V@kn{F@v z{6KfjZcA0q0eV3^(3P1E{6M!$dm0DC10CXlAN;$tC;B`_2BMteeELmXZV|tXa$x0` z(o6J+5BhOky1augaIy6Tdc+5Pnd!k-uv}YjphtYr-_7*k3%Nde#0Py;(&-0Zq`&6L zKjMRaF4KcA^yi~Te9%{!9(<8*A3frO{w1adU%~iQt@jc7LwwM8ckBFvui$)_J^4p` z&~IRR@P&N496~bdFm0r{`8{%gR)!V~9F(`?)&vPPkC$g|q!%Q`9h8(nqW;p)2w zTv6ulii@?2eB^aWpK6^6^TGEmaZwh(6+N0SkKb1ANc-yuJ(D{S`uxuiNm@DHS7Dd? zFvNo9RZ3e(j`y7<&;F!ewk{|x^mmrj9qG@$G&Mr0rXSf?j3?t4vE{oQA)@6q)A|8RaVEkm#L=&+%mQy;XuA{S9gyPDP|$a%7dM<=z@Z)iNfcVn<&Ah_n`SV>}r@;0bQ&_w=ToXw!0qpMEa=jOSZq!SCe44?K5c zKgJVIu%1H(y9&>1^|}NaACh=>ww=^YKR=#xC6?F7GT_5gQ(KZou$S=sUztDD-(#ij zSnWgRT6MyQjPY7O+$*Agc3!q$+8$MhNSZ3&@;Ta1w5OHxbbE?+7VT&m?J2jTXjdTu z?ZGmsS6a8(F5R=*?pNu``qn7Eq~*l!5^hhIWwn2h;&fArQ^pT?q67%WS^F=R82o%Qo<*vEbl4X1&z3L7S@K7F z3j8%kK!1qB=z<^5btwL7Ja3kf2~W9_T-Lv5wS8Xw{PnN-K4gvm5r@%q(fc_~;(+&k zcd1F~t$WF6JJq$GzduB4`o#Q2>sG8T_zA8Y?Je^y8-0CAkE~SRaQZw1kUpsg26-Rh z?^m>zihdh79Taba;s3CVN6+b71R)^So~Gudz6t(rJwJ`S?PKI~V_b%M@#51*#^b-_ z$36=b-|pqB_@^iQl>hCYjo0NX{{g!xeyaYKJkb82J;L`tv{SH;Z95fqBc%fujm1#^ zhuWiEWq)Xw_C4gcs_b`GY~c0aa4+We=-OenN6ZKHmspX-?@v8C&DV_8E2K>^5@d{4GYh1#*^J);^~{8o5pkW%+CdWz;kPb@yvWrch=Ip^UTjZX8wh7 z`_PEC>HR!N5`g|`ZV!Dt@IC`?;4fC{`WfZIN56B0kD~d!j9-jrMdJyaHEy&|;OfL) zh@`)J>BIEwALZ{k9Vz-ex2x=p9qb$Ed9J_LNP8if|M^_>g?9S++gsJv%IDNysXw20 z6wUu+{9-)0KZf^IgL1*lDEiPA0JMGk!kJNJox;|Stj2QM^)BCW`hQT27(5H27(5H27(5H27(5H27(5H27(5H27(5H z27(5H1`22(-}l6uCae`EG4w4mHHUKFZW&T3Em^We?R`eiX!k*V=C!<$SBG}u z`NL|StqjAuIjp1OXz}_wte3++D_GwxtbJ>CmGe4rUN)_xqkSZglk4b+U)Ee%@oPTk z0`7Hs{=N7_*19*O%J0VWhM5oM4T}$F@T4&Kj4-)=}NUU}7JmR;;6<_H(-Yg>_U~*?2z%;}_%kF^wnQ zQyp9FT_V>_;Y$3s%b@!>80+3{V?Xc&mB&@<;;v7vbEN)@?!#pD4T%rVx$xnc?g5RW zb&iZ*j3@V(fwRV7T8~@@_q_xUA^n?_|9|8`cnGI6@o;atx3cFzGcdVVcwQ^tHD0$W z_qnM?2dSUGf7>PF(=Q*MW92?P*xk;ub&_=K)OYdw$@l?JWJ)m3@V{1Wb_q%B6~Z%X zo#Syk={iU8(lF00VS1cf^6|T$qxU6sWu0Z>JGZ3hIUSGwpVQ_0UMbC%=X}hOXM5Zu z&-0Lps7#S(er%BEeB3F|_8|X*^8AmZ<(VJH>2p3R-~7^q8Bc#=!>0S+dQUgeFMWA( zsq*w*gFn1gpY!p-p2td$-uuz61Kz67`S`;5-+Ab&BW^97)0IBw<7ewnpCQvQ<2xT& z(>3Md-AZi}{`8$M?f&L_+YkB7z&+pJt83-UN0+{|`BQPu??1vf@#>8D3f|rCAqFEj|C_&jxN4pC0(o z7k@QCWeGfA==#pdxQ9#Mo6O5sZ<%rEp6X$BZV7*f8ky&ojLmy)$$|CGEs;ChwcF>G z*g738ew^DfTJme>Gn`uz#b=h#`6Y>dRQ+f8)f4tB+luI3m2*p?+75bd$!&UH*T&B+ zdG|Tuw8hKUE?>Dc(RXw1%$@3n%dy7$DvE#B`FsNBm7J!}0f;Wp;hQeeu1_8{YrefO zd^8kJ?V6A-(t-0U#Irtm>2og-4|HXw13$@HUG}*phzGhV(}5r8q9{2pL&!kH16_^j z%2s->*)2_nc%T!{>HL8oA)|cOP^bUc%Z8>U720b z+2@uZ9_Ub>zz=jsS?87@9_SDc{NO*zI=2Mn6z498nOt6(K%g90xKMecy+RQm^sAX3 ze1VIw@(p^#2mL0d2VdY*_Ru3f=*RWw^n)+t`sfiK^kt?8U!=e4k&pPGznkg77y9$j zBR=S(SvvjTi*)^6jHXe9(hG_#)j|^mJ|s+69cqi{4?{;KQ>3s3ATP2Xqe+>LtYmelsq=a#?~8~8i%XS9oaf93MgYR47Q(64N zxg~k^`dY<{@nrmBJh^)W8U5qE=2RxCq^m{7LDT(%b z7|)`p1?QF|<3l_2xh2)V(e<(Ta=Knlbo1KW5uS6Tv4HeFK1Pq5_|kdSY4hhSeD|3P zFI(EbYTfEJ3tyX5YpEEAg8N#g#U~s3g+8HQ#E0JDhkn;A>s{Tq@RGhOUqcn|$a;mo zNWXOM4*DM>=iD6{o9>WfrRi%h=eaxR|Dit^UCi~)1=>YFP@q4^{Xp~!Ar<|>+~@9m zmFqdUH*$aQrmX%Or1;&qy_qq>Br zI(LWP_mO(;&K&+8`)ZxL19>0e{us{Pnak-Qz0YN&H}Hu1STaBt@J=pH?P*yNk%KCZu*59%-R`Hc2$(^&1AB5z)%^0ln|h z_f&(r6aL+K)(b2f=D9nd&wK6;@CN?k7F|E1Tn zL|@&UC`$S(S3jKihrYwPJHO&~*NRQ^FuJczzBkJ8_2;5r%X4>rE#v#8lZWT{_l=n zvhW~iAZQ?HAZQ?HAZQ?HAZQ?HAZQ?HAZQ?HAZQ?HAZQ?HAZTEeYM`<2iOF83G;KAt zj(ZsA?qJ;<*3rdK@cM47m%~0mSl^BPe-M9yOq6}@&fY_vyYqWKcL(l$yq@ME?SgM! zH=Z}ld@yfV+@Ha3({-~nU$yRPTC}Oh^w$-+7tb5cf9}rSat$5*{PX&uKi@h!O^u|D zqIGnPAMjkimdgp_tTa-&y+>31SrSTj^>*yFi;5p|q z#xv_ZMRDKh+?{6~T~XDdz6E~3Q#_#Y#Cuwl|BgjBiq^$3elea8YCM6n#zn59+SL6p zFsiqX>eV=RhuY8c<m2WsXxy$4@vWl@=k7p# z+KusK{D3E70cZGME7yl7`nSGwTK3*a*Tsq75A&QBrpGxg9sm41y)UWj)i|eR{FAG` zN9ScUdQMAe;j9^xuK3$cYoGY1ZlXW;u2WLywEW;(Po&Oi8S{?Kndh{u{>xP}WEy5X z^2EhmH*6SFnt#;qzVoJi#^3ud2YqJXs)zULdQ6_v@}_;hIOW!52S2>vx{vg{F!sw^ z{`UW!zIn`)gJ(T-&PTR<^NBlpzWK`OazBk^-wRS=(Q|8{kKyJg1>^|!WSzf~H%wzT z9Oby@v`|?B$CjMa!rv{zIW2U$bMkF!jNEO8E;?7@=8Xq;waZ=?&S_B(!k`BwioP$amqzTh`91R&^{woS*7h##i+Y!@Y*fBCzKEhj z3ry={IT8q%O1pCNm^(pjINqsFZY>SR7U4zD3hI z&5$F4Y8XqES%e|>6 zdtUPif3r1^w54XdnjKe%&iXpJmC}+sz9zqDRZ7}U*I_zF{lE74NsapJOOQ7EN!QdV z2fF{z?k~DZ%$7eK_C5WrAWJGg>CSjIHX|%Ykm`3V5^ohVd)M@do>eQ?_V=z?n}d*} z%vgF}w0zNUm-tso;^L2{^f%n^MZ=}HH6Wtj97^vw+}auXJPX7lWat~v$&c?>!jHb~ z(R10~?4q(OzsW>xj1!ITnDrXP3s=y+e zxBMZ`L`y%^7Y6NgYWFC<(f)NkyZ75!7u9Y#_j)GPF~nbTm2T43GJn#sl>dZ`t9c3O z8oH7+TW2ai36bBZNf`MPJ6L)Y|0ueA+n-I;v=th!PNsWKd{H*oz3C4ryd?n2{p`kmShIH7g;iL3zmB3@%<~yvD>d)rhq56I7RxZ`& z?T!7g9&ehc5{GXFlD_vQH6B%ES1e-x55BL$le|<)>U(`#hn#KM7V)?Hw(+z%`Vbe& zqbUDS@e>zmcLt0E*7Wx%Dz;E@XErB1T2+>fyy)$s4>x8DpOQU zUbSmCsYUtOGnyFDNKQPOQ%d_QmOmP{Kgpna|6~0i^B9*M+wqd>R~Ye2`n^#x+oOsk zZIjwn@W)!F5bN3}njk~Aeg8_T6J+}3iN7~~M!lEzJ7^M886F=8ymwZ#Y0p{YwfWor zZTg*$DczenGm|i?i@+PMeyTIVog!6dB~)MS{XFG`;EPb#Q8}lyY<}RL=@jl%KSi14HFTIlyud9SfB<0kZok8*W~j7()q9D9f=uVNm%UD`$YjJTb#%V+tgy4`Wg zXJ@*e-nw<@dU}a0pLh#VHpz|4C-ZAn`4pTFQ)}Vgrva2F+jZlkyg|Qt%O}cJp7QyC z6pQ}P*HM3d{U22QS)H+~D%az!q=~8N^0|HS;tG{vDs$8awBNhMvV5xdDw!Oi;-aiZ zQTGf*rv7wy(^^D&##c$!_hf_1KJ#O;4y|;FiCVsxoK8nv-uZc}%6pZ|``F{AD?K5h z!OhZuUC!ZmxSbOB9iaPPt9832`u|?l%i=)*=R(88N{)IXn$f~%2<#coU&d@H}JAAjm`_Sk(nQEIMEW=%2@Z8Z1R-+&Y z<==MQtmA!ve(m=`_klDYx~KSM>Ee6T-$~#oT=eVmQ000H_Jla)aQ$9POZSr`N!Rqk6Tz~EI1;0y-at8g{s4Sqj#fRUZ(5Oxkv1-$|1@~TrN*;tXy79#sIRSwIc9J+?9cV+6jz*ibmE8hIH-;p{$^>wE(h?taS1<~o(}y|dC*v>ISg`q zUXF@BJ)!G;)AyjU!f4&6Z|ZvST@R>x>7{R_sxL0rC6*9AeUEcXh)>Vun4IDhmlYm+ zE?QRV&AII4v1v+gxaw&DWy5w|I>PU084UeWS%Do0jAc+^snPZ^K8p@pMQrhI~fe3s)FYh&P(6qI+{qY;6U7mk*;FZ@O zMy@KuNRJYx%5qy9J>xh7L_^OIkL1u^J5_F<9^`hH;_NMdB$HgyOI+T0P6Wz3Y!n&A zFoBUy*X4Y8>&9AWD@m0(FVb(;&m|}P)VE{SZ=SZYQ?kL_7-}o2>|)N0coN^_*u9Ux z$$gl=$?>;6Rc_=$Jgf=-1(&gDyhi*ycHIx*p*`V*!^-jyr+RtU10hI2t4_(PtUt?eG91M*HRC=B|5Jn)CSD$7GUAWyILXk4ha z`}KDO(kJ*so;aQJ$8sTWQsbf&1-|9P`D6Z&S7kra0eO=P%EPb%c~c9@!*?sl1AoX9Z|D56T*!M{L3#LY3whuVc~zE&bU@y; zg7Pp-4td}YdEyMtAIpWjlM2d1-2-{x4|!FVhjc*Ri3KYdtOkNS@P|C{4$dFTjpY@r zU|z?1fjsbsyei8>I%0VRD;Vtc1bN^OdE!jYAIpv96|7)hZ|9HsLtd5rNJlKMU{9kINE70fs-E|v%WkXMB~PDd=SU}%(b`9ofn{m5r5uV4kUpCu3cAy1TF)NA}%F62!tSijIX z0$tE2_(NWmLUcvh1AWI(jLtd5TAsw;2g7u3!7fs4Uc>#aO6L)g{SZ*w@VEuB4 zoj>Lec~$l!9kINE^$YF$OIMsfOP=@&=a0iL zUcvh1&6Ygyhdl9joIjQed6Ns)FUMH&z#sCeED!03a$|V~>zCs!dEgIuRhEZz#PSN(FK@Nv zfj{Jli#UHQHi_ ztFj;Ih~*WmU+~&O9{59^Sj_ojxsW#n31$7{b@19k9{59EmE|EFu{9r3Jn)CSD$7GUVtGhtgYxj&LLT@-o>;>9W4WL9unH1JiNA$2mX*JR&)Eua$|W&XoK?b+Cm=qLtd5TAsw+iB(y^bj0!s)-N+GdEgIuVjbs?<;L;~)-N+H zdEgIuRhEZz#PSN(FFe6G)+hKwp16$j$8uwN1?!htcK(<@^bj0!s)-R`7^1vVR z#O0hnmJ4}rD_FnGw&a07Ln zRrVttvAlxy%iFcMSRVL8p16|p$8uwN1?!hHEP3D$c~zE&bj0!s)-Ug{3oJn)CSD$7GUVtEDY zmv>t7z#sC&`#FCs7xJbRtY79@^1vVRsw@xbh~*WmU*2WO1AoX9f5G`Xc~zE&bR_bM)i3X}=R5P4+Vyqo{Of*5B%*nt#@_tgw3g z#AD6nS1!cQzo%WbL4Ii+WbI)DU8iFbeh0}Q=$HE8@)fZz@YDTQiVywejSv0gjgS22 ziBEHdah5K{cgtHOryn5y>AcL9g%#&=w#$1eyY_X?^1jQ@?7gxNeNOv%JwMi#R{6Z& ztv8&qK%M`_gz$^s{7$bSVz9nWmJ6glbwAJBmh^auXp z?VokdMakj=I`DxU@ORHwi=w9^ou2L6^}DcM5<%~@1~sev&|2TP42ieT607@Ht?uhz zySxup1tjNlYg;wWa6x6uCVf%*5O1jTExK~iih{|3_}HyJrqc%(4}9rEJYV{VZrz%` zg^T-Eu3Elk`AU4t0^i}fcDlfi^jDD{ydQrj@3Z2bxqM~c{J!F?G1XVJz8Add+vjfb zXJ6tl{eDooy5&RE7q7^B(3&(g>zrYnrigk4E|q1vz!%{@zM?yQejWH~F3De$?-eqE zNDqBm%1UoSnZ7%H-uy*EDZc*5$X8Rs(E z}6o)WPV%k=)(@=(p@akeZA7*B(3$bux{T-XE%Cv5me25c zmhnU5hO%8prV%^e?RXgtKY6RthX(zIt6xm%j`V4A)qeV3GSttczS~MlFaa~;`l5aPAJ^g{fso&i^o!sN4J8q6X{~Rd@?ob-A-xv5@8p4mh zBS63OttaW&$2fHz`Jqd=>~zcX&~KOJiY`3Yc8>mLsmq034k$LtL_!Dp#MR{`DF?sn zm@-Je-}A5Bug?!Be&MR80n%&M&!wYsK;Iyt-+JE&W4mRtKF6?t-Lky@qW*PjFsa2& zQoWFL)nddx!nkk2t$Gi{3SG{}8Kt_}vYKzVWR->Nu#uXtW@8li1q}oZjA#wWZcC+f zoc!C-p)CpO$0U83B=2Om93y|QTb}*nE3!XcDZx!*X5CReY+l)R3&qHiZBP;)`==fg z9mc9Cfc(v~TPm_&IN{VN65UbMZRLferLXbQi{H%~4^G)Fk8e9u>23eJ9_^wZ=@a*) z&uqIT&0p-N&&f@PO1ovHWw&rUZueKIb3yvi7qD?%+it0Czh>~bh1(w__rTum50-=7 z0wv~fNxxY?mlSpj^y{-*p1yrGm&NYVCpWM2+bvu!>~cVo^OS>nc1tzbEiQ=OkgPI* zcP~u@y9N5KXSYy%0u=SbyHoh8`cc(IX(?tIu%9G;8gHaFz`Q0+x;N&xo6v1TQpPD} zrDPg1Nm)y{P?%WFXIJ&JyO!O{*u8??tJr-%yB}b;qFr#Uo{=7JB)us6o#o&?pgBkG z1uR_G%!^AW$H&5Zkfjz+SmW;j^qc2BPzMwSad9uD+ng@rHhLc?NaJ`zzq#>d`>j=YR|DQ&blnGy;|=}h z#+&W*R^eR>c)PHALmI~$`mKj|JbvC)c>MgbzW&~&*ki++1^)x@rAd z_4lZeI?g;&^VN(35O@R)1PzQ94bZ&b3eWr1qop*Sku}coYRvm3t+$QwHhuJ%#|!CJ zUA7!0^4)`jm-q|1C) zW92~YJ;3_SZMV!RYPZZ>vFf5|7|Rv?>lH;lYD*RYINiOwkwNLznB;vs>1m zd#KuXXY3KvoqcmO&+s?V0Nw-JO=94V>`t;0M3U)@JLnb#@E#hjMPa&CW^S7c>wwFtRnU1MaiZHe}tLvNUFRqIwR?(N5625yq- zg`}$%Blc0oeG6{2-SUYr-cnSzt+QL~a-QWcn(9CoG!Qf}`ZPezj2>@6-l-px^kopL z*Vy~_hm34{A|X`YlBljSuk7&_igAP)&Alf+_D{u<+d1bQZ%J{@H{McwpA`s0gN}J( zlP0a9J^fDMeN{@9-`XD%T!wO`-Lk>5Teuy!`>T`|(wDw~jqBQWi@ndv9E`Ud+ZD!J zpiZw#`px>e1h8A6U!UEQd!87V3%eYU)Nbin(R*cU=84fB#4Ws3`yxIS?3SWB4t7gX%>}xkfuMm`R|ARNvL|nB@3ULl zWx|HCTb3+&lWIHM^rM|NpJdxDw6A^ApGN#jya$;KkuthTPW%h=#9}>_#AhtKh1+qv zze=b#W1iSp*e%n7-2xQ+F5Uy%btz!CK)=JbTew`<<$xqN!)_6mh4n2iioOsVfB4<_ zuv?(t+;&S<*8`33YfpBIn7_7v`RX-Mp+mXwdB(}i(N0KH16121ZVYxyQLP5MrKsit zUC=<#z^khP!*1b*#b>wBS<61VMeg0M+KsHVzxb|c((a(P*}VLA%f4nb_g?P%{zz=I z$j9+KG3tl!i0fObm!GZjjs{(ix2TNC@FuTsxyiCyxE;6qtCW^xk7{7!y7k6eww}Z5 zTV@BlMIk!mAM{Uc*Cm480{sr#ZsBramjjaA47;Tq>=qY9Z-|XQ{4N3P7U(y(-SX98 z*)0o}t-JIhH#NCJP|Z1!-{^!iH9*y|;^ttt6xC|5TZ(Ef&;<B}Uk*;U^9>=yY5wnRb@-(|JS*>($&930bS-l)F5d;)o@y`q?u=A3p5MUOAa zc2<7l2wSL^H+ z;*K(&<2-xb>_93dWWu?Z*- zJIFrBsr|%mxcWtT-fL2S8+MDzLiRbrdF&Wzx4dN8E!>XV{nd2m0_~#f+IEY*F28n0 zSeI{?JHJ1{+{8aCKI|6g*JronUYF11!Y&6SJ9*b#{B_#u+by+Vw`9TS32Uqz(0*dj zZ*IF~U|4nwO%z*oNwGs*(p8J0fj1lXEx6To%WrkQN9s8zBQ;+wC=Y>U&_K|@$kl*s zXDTHc)9<-ZU|7+5AROlOXjyLq18*i>R zT7`El;Ozr9BfN3Eq2JtibNkRLyu}rvUy}i>C#Z3}q2Jtib3dR}c$Wj-9&EFM8^;^^ z&5bwr_gaN_HQ=2EtS78-yrJLRcyqtLRe09|-X3hTf*Z#h`pu0u+i$JHTU;61?<{1q z!y3mM`pu0u+v%;syBzS&hBh;_alE15dU(&A7e#lmu03pXQ1~} z4UJ*6@Y3VwWA*o_kvh&iQuEcqauHYu4FnC0I1NxU!~5FT*&lM~dB2frH?q}(cG`UE zU$1zG8O^=7$IfH9GzPRIj-Mk7XwWf!F2mJ41RgI}`J(ja89xWlv@6HY_t5QjJYK}@ zIQpv?C!@3^6BGy^x?H#3`1u^HSG*{!SJXNj>Mz~{+jVI$eh&Roe^p~aabujia*v;L zxvHvaIt1h8A6-`sY~b4BfzS-t(2 z^sS6Kr@D#B7vi;Bj?G73G?uEvgwXZh&b1#2W!Zf9?0fj>T}|HBqKfj-wa#uq{!q?s z*O#IY9yAa%5H#RwV3%l53f$55S#cAZgrsZ^N;~Y9B#OSH8h+I6vvQPWw{Sad_gB-M z3$%-_YuhdMJ}ctPuwKzFcYeR!Uc>KFz;1zleRfOkeO9sw|?yG6|IUDPM$_pM!%PE9VK*KRp37kS}G+HN^U=X<2C0~x9LDvWv{3>pX; z7=;?3W`@_daPEuQEs_bL!?4M#x>CQP%%uI+h*UY|K>XG4T z_IX_0G7tKv*R$CEJa&qaJasGAw;XHPE!>XV{Z&G}8S7iNzMIbxzAMZVv(a$<6^VJ) z&y5MY1^OMf-NNO`@=; zC-br2Zt>YOOik^R{Zo%Sq^zzy*$2ZEUvH03<=?r#DV`@uc2fHQFMYszZ2pZUtHIUdXNabri z-tq<{B-^!Z7S&!+)I8%Ylv{f}%xg2=QYlTEw5GJj)3=|3{lqMPkF;rwU$!#Rp>n)s zqGh*mJ8t(^n`O7mf!*?hV7H{P$nv3oYP)V+*e%fSuy#0oiS-l#Y->cc^oOy{%sq+bOK4*q`*j^b_*$2=x*^p4~$62~gAz>2rkr za*{IOrU4B$k|9YN^KK|G8o>bsY?S?7DC?ng3G^EC|I18LAe(7@=^fE*Cm5y#KRjPW#p@pw5y z75nXx4f2oF@$=dNp7Ha~>vo&^J8JxZ+i~<)p(D~Q?g@XbOV_P8ejc42#?QGN=5WkD zs(J5{5`LXui#yI6aN0`fnT@Hv_o^qhyMgRDDE!Zuj%AA*py?O7F!)}3o z>)9<7p8!Su(5mtCbp1i&=VHbiRr2lpq{q+IZp2nLsr-psbiF02?B2%im)O07-8?02hAQo7as`U=VGPTy)ny*dG8Y9J%E1mya%fNUYQWnC%Nt^1Y~H&Bz#IC_jW@Rs#qoC2VusWL-ljV0 z+?yDi_bwUmhJJJ7&HaE@;VrHS?YE1TFT}*yym#XRZ|FBS-rV1772f55w+ogx#KhRV zcL{(u^qU)R?$@^p?`pu?1_?+7Vvh#@`jigoA)jO@P>YK z8S)u1KIy<5770eEzB>#fHx#{mIhJN_1jI zSQCTv`1##B-y?OLd8Fp6iRB=$3K|F+82K8I!xv%vJQ+i54&dV@8~j?a{#+d789%>A zx7*a;QR4^Pj-$Vde5S|0sP9CV>((1TpBu){wd!*HHT$UMy-Q5^(fB#^Oa0Yhjh}P5 zu*(5SMw!UMpPo~=?u`1!&&$DX(W=Vz*TmkucZp%QK)?0u7K%@RqJD74&!c$0ktr=H z17>LYJ~`JYvWycwe*R`vK<)gX>}XzeMz4L5Y;m8ix5WMIKEUpS>^{Wq0K1#m-OTR8 z+O>JZAJ>O02k$|%$Iq+bJ%Gw{xMo=~?_E;72heYx_dvCOT%KBmcP-%Ug5?b{F*fgA z0^kk(=Ej@rjaK0;_8GdKadR@7Lrjd#dsipG8~V+SH@6SP@pjWcx$)-yUaRn~1-xCbydfsW=DkY*yrJLR zcyqtLRd|biL;LNb%q)$Nw)ZbuD8TC zcK@E;C)jWYz}p4O8)9N?-n#_A8~V+SH}~sXg?BaJ?SkbEF)=ppT>{_@{pQA-?YCCpT?=@- zV0lAKjLmzO0C+>cx$$N@y;XRNH-z@vMavgrVr<^K@qst=TMzGe{QScjkH*K(XD?r| z!c9j5A<5?{i)fPNZ?ad9pFgMby>pH;lSa()VBbg4NX=K1%R*omG!Qf}qBSs1=B`qj zApdrB=n*;^&&V22q4AS<$lsDBOG=M#+cw>;W938dMRl-wRZ9DtUcQ(6t(Te@sDn*b(+`M}p9x-*EMdN_YF;^=KFUNT0YToswU=@~mm|2QEXom6G_WZnvqw zqs9-o9Y=o^xKdhhPck)E*VRBI>yB&NuNgGYKYuBVpAQ9y^^EkJ^|PW$FX2by=g=?p zSCf|_#wq{)?OZPGazK*vlmlIMlXBpke;(`>Qf1C7s|*0sGz-`*W!7(QyQTZ@Z+mKW z6cw{u#EkyF-gH9p`%uj}6qB8hrUs}wOmyme@0|U6U%5!aM{2&BS`q@Ypn;%)k*)#R zZK;%Yk-6;X(3S-CW0Jm1l6|sUj*&muEzkb(l`f)%o5akzv)kotyM-u~>^6C2v56w? zW5*9;za5|aJA&Qvx~C_cLa9ZA{`j^tmEK^h6tP>Xhj`}wPSovoY`1VbZueK4Ww&&{ zGn_jWm&Cd^N&h2lzqo|3TcF=z+bvu!>~cVollKPisO#3VTdKisaY6KkG*%7{l6;`w zdUgwy8v+#d1LpmvzcaZBYvWR&<0a3NvJpkQCBa6SNdl*+TQhGIP@(To@wIz=m)y{E zF4QcRAqgg5?b{F*fgA0^kk(=Ej@t zw^rd@4S2g?c|%N$&3l&sctgLr@n$={Re09|-Y!_)5EEnb-X#Fu&~H7w<9WYl=UWFd~SC#qrX!*!OaTLECwaXN&V!Qj~tKIA z@IA>%{es;BElIoOMaypCcHHi-Hp_0=x`6GLUj(}aDEM8x2e#`{z;1zlhi$iTxvD3!*o~#vgu{0Co%Xo7--=u&CW~dVlXl4cjVa!o7CO+ss(azgO*#_;s*b zimEo)Ek!jK=z<1<23}na&^MM!iN;ZIQ9mZ>%OEt0hT3NZwnGxzyvu5r{dNnjogCsy z*(@?1?fJ6Pjkl1_Crw&Y+T-clPvL!4Y=5!wmgpbUxCixj)OZWG<92_wS$4~M*e$;g zcFPcDmLK6guw9oDb_?`7Y`cZagdbj4&@wv(A@PDoP&wB0gB*MB2*KGjIgS5r$uU=}nGG%(UNAiE@$ z(!ny9t>0$_?Xz~X_J<^8b3ZC;(3&1Qb_)g4MRYmmiK+PblW(3_McNn>PK_eb9Yx*T zzXJ!?9WR6Mlk>tPUDBX8W1p4Up`LwKIxM?|+i|pc|A5*)`>Z2XhQ{Ir|}k4VPq#U3I-B#Q;|=}h#+%!RR^eR>cze-xA2g0P^qU)R z?gz9AZ*fRyzg=8?A&uh={pQA-`+KdzyBzTLftwNDINs22ZoIi)-zvPT0q+c4eL;=m z4gKcEo9(w&;av-O`@qczZyayvH#gpFr?(1kacF42Gl2C3HI6s*TMzGepOyZiNxXA%~uo4L0}a$5Hv92H9*bG zj z`>f2tyx%cThJ9ADAW4Na)?cN4R-j+%uO{yW@u91d|9nL*7j`)yE+`XvY+vyO|JbcJ zeqIiC3n?z=)mS;8^BJJu+;+?Ri`y-ih`Dla$ikP`ZaKLy%Up!2tct^e-I7aN8Ii(z z#SEo{prC=EfgMo;vfENA(Ztl&&-*pApS@m@VkG@2Gk$!}eJR&((~;k^KV(cyH~HrM zrby~~#>*J^ZP@&WrqBBohkNGz9%0!n+>YD*Rq9+M`ilF(aX(dOuu`fw?^nDl%=@id z7~%+x^;gMmfqsW=w{W?z%K;@VPdTV(w^W1O5~E!Irm=FM&R1mp*0WnM?-%t0=KbpR z5RmJpFewA>GlbAW3|g~pUXv!>8_y$t<5+qE8I{tiecR=^vdO$(dYWa_kZd7zy(Nxf z_h@$C#O^Wd9?R}=>>kfZ#w0R0|ZYzr8?WV;HDF?hwb=0{BzP9U<0dMFxH{RSQXcgYofVT^lH^jynewP4v zL%+H4=DuI6@U8{CU9h|%z}I$N0^kk(=Ej@*_^rZQ93I+l7cF0ijWhgieBcfJ=Ej?C zxmMv_4tTp@c|(A&?YacO8~V+SH{0y3!n+#qcER$7*f_)Q5&&=Lw;tZ{9N-TRb1YrV zTeWWKGB-i>L$W?QDJuDas+E@>OP{E}O^wvKvLiKLtt=RUchEr4z=+d;9Lfk|>3PPI z8bN-%jEr9^#?s42c*fEv>vo&^JL>x)x8vxqBA?9~OWzd6(iZE(atTWkQc=vGi|GJ#X-$eRd!G-i+@Ky!WS<4&EukSbCQJv%}1? zV&1#42|pT3hkofju*=fp+x9FiS@H`tmVWcb_sH_6%V3N>m#w1M*BeV0M}%*)E^@vQ z6L0h0jZgT|SUU8Zrw&woAFe}Mg?BmN?SkbEF)=ppT>{_@{pQA-+lu0NyJ;~)ssV3P z9d+(ajLmzO40uDox$)*cL96ht1-xCbydfsW=DkY*yrJLRcyr&cRd|adL;LNbdo4r+d*8<)ySl$p5WAol60N&7VJ-p+w^bJMF($DO@vai4KU9^&QUdo#5DbFYNU=ikJNlMwIl>)K?6YpBU}U2%wRt)omCp!NXFNa><@L0F7Yte zC?-^Nhql=a&0d!-*O8a9&ozl@seMZQ@_wWNydNUqwDCEzQ_Z zYb^HD>fSf3$IOBx6^8e~c2kj*hw!7Zbm*7*tHT;g=W=0}0}_TZq2E&qq4cF)U3Qc8 zxWDU|GDyGQ^H02_`^lOv{2f{YHvaItRIpp1-`sY~&4ulj)lHMYjGDji6frj6Ir|=ddRLRTb;r+_S*~%P*^wr^jG(K z`U8Vgzq@%lxyMWQ?s;K9EfayE?-R^T{Ih6aw?Mx>yXD9aUBd4}w>;X1bJdE)p48U~ zPFF2PWZvk=2x;oAwp+TxcuP^OHs2oemGi9dqNxsaK?6Ypqfi5~+fpgrC^H`R0d5}p z<0UKnZr-?2^(WDw>+u$8x83z<7yZai!98J-ZMUS;Rz1?Qc5$X&WnGkMJ|tzpo9Kwf!G~JA zcyx|^fceu{N(jjovvj>BX0tno-80xdlijn~oy+b#b{A+j#n|wWUex$A%fWll=-R~& z^|mD6aZYbvHMnTWw+5y!Idky;jz45@?0JWVac4vCL*F*`9;oqW)^EB#)KV_^7V5*Hp-(*pKDL{Rq&)NG@D~3S<{P+Z`9f@*;dkQ$Z|Jw)ce}Wr=XRqw-fmjVkaED=R7ahA;A^`s8SsXF zbK}kZgI3{P4S2g?c|&ZR;dcpuH}snuZ|(=S3h!FL+Xc%T0(@=PB>>*gZ*IJ~zuzjn z#cxCV?V{xiv2ljqjSsw`-`se!UDqnS%K>i}EN=+#wOyA0ctgLr@n-wIRd`ne-Y!_) z5F2OsT>{_@{no=fUdMRXFvrzx)}Fs|M}u|WUSc3l>PAB`hHztmq%#w3ZJu1Z(#adj>ib~zv}C=*l?^@VmsYTcF>1b_>NPKv6$nTwQZcrT8H|D37-55)pn!%V6l2-UEA2V5$$3wL#!% zjHPqgDvG^5r{aPa`UbC`__KliZ@Ff0^^X^Rck8)fPKAq{F9i75t{b1Qr?wFKO}B+Q z{=5fniRV=O_c4c+PJLq1)Dt&0s_1g7rnFsyTYt?PPy~mdA zS1heRa?h=^b-PXd9rb;Y+i~<)fh(m&4T&iZ6~@kWb%{vUYT9epx6aq&3S-X>dj(ry zWOzxxSwA-_;itwGSijU?9o85-mkYZbkYtnzoi++_%R${S_F%V=;&NVDWdJ%)vw+=F zWBul~TQ(QATb5rDElVe&F(0ZqhiZls($oM|hl#nm{@c0s)FnLtf0PY`nSOd#lee|0 z+LO#$x966f7vu?UvvU&o1q}oZjBE{ry@IJf*zDF|yyS_WynXfd3Z7@#E!>XV{nd2m zg4z$dT-UZ+?7f0(3&NgTT6cN=qMf!~mlk#l^y{-*a_<$)<-#rpBsnSjF8(@Yl~O&s zMf@R*r@AQmLTvoucjLoufqrw_E!zs)E$ddSUH;b9D|%Pc=E~nM5Nr2=Wg3 zqwxXkJ*oS;bS~p0?)xV3lRMN`NeI#HZ+eaU1C9HPTgW1L_^D`==A7d#DYp6SmWn(> zVuCytS#;}hk`xQ}td5)J@iGKI#3W7AMdK~|ez06xvuvNPd%p0S>7-BFFZI)t{%P8w z|H|=}b1b`s+i|w_Fg$TZYDlp5i^QU6&Ge3-s%=TXL^yy$3KTvwOfWZca@o3R}17OlEme{KBXcZp%QK)<=|mZyegx2#@qrJI(VA*kk@=(0j< z<)zB5I4_L16jgHT>=sH9%6OJrG{u1~Xdq}{G-{wdoXr^Jvs)S~9k5x*mDj5`-g3TW zw{Sad_gB-M3u;T~a@~64EtPkN@fNMSJb$y=Z0I~q54#2W_1P`C$6L5u*yVtvHp6bI z2D>E<;E=w?%7HrLg!P--Zt4EqZBMO^qH|OD4w)$RaOS+TXuPGrZ;hC>e2pA+S=4uF z-^#V(wEo^Tef{<6WsG#yVvx)-?ptuH?UoC|cuP^;w$5(xm3K3|XsQEU&_K|@=+gi- zGs$=h<)8X7NneIS^fmPQmhAqQrbPV&PUQN>TaGlNx%bpQ**_I)clr0V9dAhzOZEzW z2t|WlY<&y34CTu4mJ2Prh1+qvze;Io#(2vdjJI4I##@F$MnA)QV7o3Q>=x+PXSd`Y zZ{c!bmjjZVyz7)Mx?ES6UH^DXE!Ztub-Di9_`~lKW4s0W&26`w(OkP_{-u4r`OFr- z-Ex|ny=I1}vMb8LZYiqNV7C<2T%ZdY2pV{GHIUdXk^Z>uwOfXoCq{i8|6H-GcG+*Y zh@ll=+brDIilSh*NW0}C%WmOz-0rV7%WfG9yXA^tw+vlu=_%d=+jS{nw?Mzcwp+Md z*yVsEH^Xib{~gx1xG4HUZ2aMOYZMQ5Ac1uy62D_!G<^o;NK+wRetAWIB>FCgg3-x1?zD$yup~qXI*nUVTaF^9C zXWK2CAGoLVI*?_%j@=^1Skyc$N^{QfmQ-3P*ID_^7N0M4yo`pQyiLs$)8j2<=dF=z zW^O-4?v!083zrMK9FXLs?7R5ul+|CCUk-MQ3!*o~#vgu{0Co%Xo7--= zu&CWKw|DuwS-Vp*_TiYSk0g56S7bAc{sAZXy#)qrWYXvx%% zN%}HLYFxV|c}9I+Z9kyzL(g!Jb~)|(*5w~&vUTqry9H4w%{lEBkSI5w-I8Zre!_OV zyu}ZpXwX|aPwZmLZsB&^?ypi>nlVpoJ?xgx1iJ+|``vW;PsU$d3fL{s@38F_E*Ew= zAj!$QZoaSdB=PIAo7k`Ry8LReTS#F!ug1!OTHnI@&26_VD{Qy)t&GH6`KRcGYR;jb zL2FUM~vja|>|{g{kbQR5yl zCEw(;TWqSK{p00h{G^>C_aRFbtYW-n|GOUTq95rK_oPoAf3e0}((=Snhu=Z#Zqt_> zZ&_*CE!>XV{Z;B*B>Dn2u3K-sW!$-8U49yap?sv@te;B*y9N3kw%x+z!Y&6SIZrvL zXSdXX-Qt4i4aq74c=yso7;k}o>)9<7p8!Su@NNg+Y&m((1)(xPZGd@Ansjemryi|7 zgd}-ewQu_=RNhTPW>=G#@2UKW4Z0o?pJMkj?B2-kP3+#x?&sOPh26Wgo55ZMkzUk! zRxAha!9JM;(2?toG2WIeQt>}wzGRkOvcry1|7T@849(RCj*jyLq18*gqOisS93+6<`$yiIk~xo;eA=r=ds+z)6K z-r|2l`&|cQ<8kA7L%+H4=Kfx*@Gb|u4Ls}JHjX#+n;UQL*S8AqYQVc5$j0-=@rHhL zU=L6j`Y`ZoOz_?YdClW+Mt1;fl;Rc zIeekVvcvd!^ROQ;S>g9Ob&gka$Ir_XJmcs0>2^CFFXDC_{Z$&PqKi5gxYuJB6iWT6 zv|Dfdyd1{QwaRk+ZLGiADS1M_)L+$Dkf0c+uH5@Wa=Ea}0ddPy4s>VKKkv63>=sgG z&a1IWXH<@er~{%j zpnxY?@IX`&SQY|u1Y}`ER^NKE$X}w%y2`S=fBm|?-|Kr`_v>ypOzI^y)m>d*RrUA# z)mK+{mpVqe)(>PI{9f_z6aOdT-!J|e{d|!1p!g4o|FHH0N!L&7_E7R+J;;0g+&1-~ zuJgtR>FWXYo1`8%>*vC!qVTR7yc@$@kC7g4>NhdoVmFGy+j`y1&(+~Nhdo z(mphg_wYASF?bKJAm7#WcvHWL@s@r-QFz-1Zy%z=pQOi|`b~_t^!JLwyK3+r9%A3s z^mtRhiSd?xeNlK@|6|&3AEd*cq{o~3O^mmU--^P!V(=aoVgJSScvHWL@s@FVQFz-1 zZ$G5No~6f|`fY@Fef|97+1Agk**y!J{CZaG`uT~&>Ll|;y?(w{&qs~ab>@+JUzyQ2 z2^b9+4QxyrP|Fts<(cY^-$v`_XrsK&!3*m!8{&7f)cW}p6p#&i9`B#q`^2oDKc?I5 z`g)PHt>*vCYAP-P%a>BDNFKetQFMfQ7 zIDY-SYQ`;ERf+zl;{n#ssozH97OtPuKKR$q9s5{+U))<%mjm8V|1shWw|>6m1f)Mh zafbH?d2&amwWaoaf03^BxU7ReDgHX~pAr9A@t+faNcfvEGta{U#GCp}jJNay zio)CaU(yT7mJ+|F=$5`=KNJrwnWp^#hyoHiM)ceYn2_`Y40i%JDt^w7~43rO5cUk=Uxxbx`+jmOVa-u%<3oOt+1s%ejb-)dA z{alqP_wTn?_l;RUU$?#UU3c_%ob>}~$Jt-yI$2)p=cRW2UZ?|r`O`oHe*XK{8U3Qb z59{aDFZ!#Q*3X3(K^`Crazd8}At_D}d!6y8;XcLc6+LFw_P zeiP#@cB3e~t(uvii$gaYo*r-NH!R&dY)G z!2ai0yY#xKqNx4P|DfCL`g)PHPY|}{KCFwgu8B9tzEqOz`3>i;P28= zyKnle-JSLN(YNjpu^+om)%>LfSPz0eue(0iPZS2L}j3on8^K#t@@6#lwITmyA^ z@SmgJyBh8_aaA*J(W*-HH;6ya!@}T!^ZvQ$H}SaTqNc|!JSGX;IpMg)mvpjm%YYhVj91U^T5Y9LTRl%-LY|Yo)G-}{*`PN!Zec3J`D)zq za$wvd?Rc=iin6$Sz@XoRU)XOnZW+yS%PKQ&(K;ONFZ3Jb!ZIAUP`{bSEy9Z+50JB4 z*TeYhk~JQ;Shtw<)G&&1AyFKl?)@?xw@|-{$1RsNJ#JaPEYs_X_0I$PZ$zD)916)b zz_DT0D`wo%tX9pqrCH4xIimrifwx!#stY?%-c@C~fxWNjPA~fvg7zusoERJGxJ8Xq za(`dZ+CS!f#XkqeEz*t$`>PnYB)B?<@M*IqX@w{wm&Aq<%AxTZ9)u9-z2M zc%aAJ@#`%WGj16cdH=<9JaFDu6#X_Dw_uzDKxrR>?Vbo|@Tn-gs|N2la3jLg<4yf0v3m~QVmFGy+q%`v&qV+m6ONhdo(moW0cg5fx zgKboBdc3LM#CS_TpeVd;gLf3LF=6TPrhXISE&aWs@U9xXW3Y`1PLDVBn;38D*B6Di zb(?9wqmYdbOOH48n;35yzZHdd#o!$cZDeSAys6*Bc*{7wD7KiP z21dRHHq`a==;fI3+PTwS0Vb@&Y>3bO7O{R_nGmynK1#RS_4OiY$Jt-yT4`SE=WS=0 z@n~`rk*>dr^>gYM{Z;pY5~PFQKwK^`C$IpNCH z(`P>N&DQw!^QsxQXoV;Gn~n$0`--C9M&lN)pVK}B*Uwe)>h{uGRF?zZQ2%j*_c2P* z2Aj`}Yo0yjPtrL<-qse{Zds$n-&*`_#NST*vEuI_{!ZfWto?}OouGO>kbGDV@?Jl; zzGm!U90uX=^!0%HO;Qh>^;F?gQFvDj-eFi{L(=0-{U*j+>_$;|+XnAgbi)_Y<4yf0 z##`ElqVTR7yu+}@hNQ=v`b~_t^aF~*+xog`zhmJIT}Y2N^_v)P>F*VVcg5fxLNz`h zJ>JxBV!WkaUliW9!8;z_(3SLfQ@@GvmhoFrcvlVHAynf7(&J72CdOOF=|$mf-EP|N zczlCb(&J72Hp08Uem>ar`gu=(M}KE2{dazOldNL^=)B3saBFF>eOipB3 zKd+c^3sjl#7sQ|EVR7)ldH-DWn|R!EYqR5)+4K84dOHuE+1YXKvSzj^iT`faso~qp zGenPD#+h+Tvr0AJ&1zP2M$Tx!XyC2YfIDv4VjtZzLO;gs%ebY%b6s)!luVvHIk#~O zzAHS;&o^dK@yc($Q9}8z3w@|;C$1TE(APEe~pXXs=j$5eT#N(D9G(B$V zTGHE%E#aH}(aeS9XNN*^4bbD3eayI}S*@CJOS76Yaz+D218=Pc(9GoVoLI?eH=@fP z)hi$^%(#Up#QOsZ^C%aV;J9V9=r{AYMR*b90dme`++y8f-fsz`7#9-7 z0fdEr9Jf%viN`JW&bL3;TPmHdl63J6rymTNHfwfw*Md%KR%ds|1;LBS&S~aCvFC(B zat(0wXH78UmS(kT#x2ci&d3=J7!AC&8pvzh65a0Vdtp26y|egnOM`v9&^`s7lknSX z+%iy}Jb9VgYyP$m=y6NvJn)>DS9fv8YTR-_VB8|@c(A{k*SKZvOnJZMZ)V&Qhl(4{ zdJy#87(C**h5F4rZV_Gtd4T-fel5y{U)a~3(fIY2iW#?P)g}5H#GmJ3VUAm<-^AmV zZ#F${S<>0j*VKzGQu^3&3%*rZ;7^ZR4%BvN6A!EN&NKi1Jxb`UbHQVtqprHOb;G|e zFRytV<>!4TCOxA8qk)mFfw8!~@_6-Ul)U&0%NS)HH&fwp@3**iS4E2axwcndjvlu> zc<4sZ`DJ+%+=Khj~}<#X5115V@y~&9$+4a`b|7;`CjwmmY$ye#Tf=Je$(R7z-gZ20uJ@K zWsCGu)hi&=kYzU6&@dF5yLx2Pmjp*P9==RL!_02J@)kbUeVgh5AiA zZn;0(xTV=mgjG>v%{QJ|EAXesEr*$LOS3x8e;p}~=TYI!mfFY}4HykpK(=`RzbALI6AT#ev6UlWwWdH~)d`Q<;}#9Bs~z8f>@C~T{uzJmCGu)m7Vg{x;^gJ0MWuD4Wc7q32WZtXt!yL8m< zTVvK+wCWQ54c7rz=l%q^-a`Fm9=8ZDf;>R3NqC?;qw(u4)}3bE&5z)4XX$u=aSQd^ zXxswc08rY8;6B1P-f%@z`G7t16XPxILs58J-!T1}2w-D^(&J72CdOO( z0Y%|mF?h#d8x@=$Z|XNO-qPPI3UAxs9R+MmSbDsv-^6%JzrHBEs|N2FY@>qH<4yf0 z##_d3Md59I)3o1F$VP{y$D8_1jJJ%_i^98N@Q#KyGBiEj)Ndoa>-z|=ZF>EDX~(&p z*7Tm9bDMnjH+KEJJyI9B1?u(l*Y$kVNPRwbq~2F_uOyXI-ph{+i88qu&DKOysx;eH(K}@?UT>_A3ygSE};t9%j48x=(%6$ zE8TZig3kd*Wvkv-d_%X}_4OiY$Jt*cu0TDH=YFU2x!)bkbH7oD_`_Hag1$c%&bWR~ z{i46>K3fiX{03SRKldxV2=V}_kP~hluBXp@%8k~~Z8L6xDii*K`13q04jwq~D~f&- zk6WH;dfd{pu)lL@Y0@F#!Xyic9k-m3OupGz4p*%=wf%d0eUAjU?5%UbW1gd~y0x`g zMa9V#_uMbu8MipxOZF$&Pvv=7n&TGgH*VaL zc)dk<5##}KPQn9S8Haga@mps68b&cLgt!O&aD0wiEYWYHaSP%DP}+w)-&cHRxOl#h zB=0Le>XXnX;GeaHwp-R{@wXO#8}YXjf2{aBh`*EgJ8M58c_#>Zao%5(d{_^1e_zqm zgE$Bx!h`(hc{nQTK_x{!aPSsB6@|BL@D76&8$x`8eprBbQ@=^w2n|@6gt+){48}!5Ri8u9|7;otZ6oq%i;2j1lHYA8M z&%*-5oBBoMk_9a_y+xOeBw?0CdOOF=|$mPF?ff;iVX?k%=54S@uq$o;az`U z@efU}pD&-+-`6p}KU|iCA#v*mrLz(y&}1~de%_+zquyT6$KsB`0s6sXo}1@>wT{eR zqXDCVjadVmmbSs|C)!`}Eqv~GI7r04*Q1!{e(S1G&;9PE+imoBocUyF$Jt-yTB*Nt z!FDn12iMPo&;6FpHsh1J`WqfVzfmqM2>h^53H6KqYSjCRiJ$uwUIcl7EXWCe-w9V- zJ~UcCx6QZ(3QPEl;sAB+PlDqX>NoMYWsqI0oX{dU-A3j4uvOYphhnP%Kl*J9&? zbp2I~Td3c-aZBRoeuWo79-z!_T@T~0?bklSsJ#Mk|eUH@l`bO$~HI2FvG#W4(*cdgS+L;aVeMPrB zaEO_9tC`72_7_ zH}kkfcoF0Q%AJGu$+7fLQ5x;Ows``fW6B!TJmUrF{s#uc$J2`uD-I zx*P~zanGZ^=j&cAm4#7#D7NoXSE-Z zyc1Mkf0le$4|0EB(bR)D2qMDM*Mmxmdf+^7AbctcZ`iq(u?#_e- zHIN=}>NhdoGEOfF?~1`Y2G7t18{u7lU-1|{FVnnX!O_gjiwm(gjm zon{X3mmf@^ZSj~&uueq(JD*yH;6ya!@}T!v%jL~H}SaT;KtyF4r+DOVr`Fp8AIyFvFa&Cjp zaSPv3#QTXn1{TrpEBbS(92E21?~8$Pi?rjx{wg{bj-H)l(VpNJ_8UF-+jfq5E<)=r z$zSL<%7tY)ZlQiNk6VNnK^`FIBs^#|ZmF7aOBlr1kSGqY?)il{ZlQh~jav{OfYLq$ zpZis|C(?Gg9PsDwJ=@lM5^>zv0dzasvh zwI7kZ69m0D&jm_8tOt2N_iO#TS!a&JARL~)9#FqY>Vbo|@Tn-gD+cc{tg#{K@uq$g z<1KchD7Nhdo(yuQHZ`NWHIosbEqw8Za6d;Tllw%s}~2b(5n;=`W*TJtJy81<(CX zP!8+6Sf~EwKi+hnU!|`u4f6}P)p6fpk-RsLNet~%&^b{dw_&cIKc4&gxjivv{rs=G z-L9_}NjuK|Dsjzg{d}caKaZhlRB*TsxX{!iD_e=evznbZ}U*ScN2gsG2aLa;n z;^+E}*3T*ur&!S(YSZ@9sE2O(HYmjl5o?)k3nVdPPx z^>fEQM@S;vdQICc>u=)!UHsR@e?$B?#VvO$IZd36`X+I))C#Y@@B_Gyfooh)dc3LM#CVI{C<<@ucV>Pr4&88gdc3LM z#CS{lP!!%3gLfF#*pT#iQ@@GvmVQ7{c-sc=Saib|(&J72CdOO(dqv@0HF$?%jSWeU zH}#tsZ|T<;g}3#4(|*Uo8@iAlZ|XNO-ZFkG3h#=+JA`U{Kzh8X-^6&!IK3#mZG(3_ zyrC=U@uq$o;ay)ppQq<*(yyPN*4fwJd4V=lQ>XUOhNw{d}}; zx9jUg(vGvg%5}25*3YYE{XB)iNY`J*`Z@KB{%WT6bKymh2Pix_;ax9Bxc9u#`gzri zTeQj&{Y}RMXTMz0Z=-Pw*UxDmg6rp5-ajAP;NBXopF@0ML>kWe`PQ-yzK!_XiN8jF z-@b8XJ-xZ|XNO-eNb3!rL}@$D$j)kREU9H!NhdoGEOfFZ|fz~e#he*ypkSo>bDWz_4V`ajO*vqx|jC_ zi=FH|cKv)_GWlj>>Gku+^?cMwU1uJt_th-uM$Tx!Xkeq%zy`X0?yl+uP%-Q0N+H%E zG3)0~>UO)nUL@@}`>WJyUhC(R<_^DZ6~JyIKTX$P#riq*i~ee+^>g7xkOu&Sobaxf zBiwu5X#Kom#w}W9iTk=(o|hh3n_E4|%Sik7;mkN!HJ!9wc_w&#T&wSicef zcjEs+{FlUkS^QVT|Fih(wI5f+x;>P9SP$}EKetUih=U*^JbgW&ev{M#XZ>9GR21G- zgLee3aY5N@jCy|1QGH-bh3Mgtq81~$<3^H?a%`ngg~<t>*vCYAP-PRa>Bb_j&SdJ zqxJKu8MkPaCHk9=2hRKFqTfd27OtPuKIFN69<_J5_jZ%4pD*wV$g#72{x?|%|GW6F zi~olBZ;D^q^LG8+cJW7vzlHWAl6QjY_E7R+J;;0g-1?K5pNqpF9G<=&P`^p)fwO)t zd@2g>iorV!Yivk*ys6*Bc#GXA3UAxs9gA-GLVCQZ-^6%J`%o0#RfBgJ*4U8rcvHWL z@s@r-QFvRgnD#ps-q3~gcvHWL@s|ExQFvDj-XT=u1JdJ7{U*kHQ{9g*3UAxs9S?8l zN_xDh-^6&!_^l|ss|N27s__Bo@uq$g<1OR#qVTr*rhRcDue_B<(o+t6VG1YyEtkSwBxSO;})&5M1RxqzmbIPur7z3==HibMe+%(Pi@&w_+h{)`c_*lD4<#SggS^+zZBq~8AczQ0Uk|9? zB=x{qKNmg~g?H899f50HP(6F>E)LyrczV34-^6%ts_lMJcvlSG zVOV2B(&J72CdOO(0Y%|$8@yxD4PQu)H}#tsZ|Uz9g?H899fmbFBt72LZ(_WqUtbj7 z)~lxdj)gaLAwAyIZ(_V<{8kj+6@zyO)%bw)cvHWL@s@FVQFz-1?|67aSJLB6{Wik8 zzJC6hjO*vKmvnYC_W}3V_48&H<3p;8576u9+v#!1NL^J=KzI@40pgPr-t}^SIs@fK>*rN7ZqX`B z^fw(3ocGT~zm3K%TtBCM$aDQXdac>L;Uw$li`?k-BWL}52W_{koy6Z+{1)+d6Mvle zdx&p|zqj`5i3Sep^+57rJ&3-idi`QOtNXEoGReiorV)+33LZcvHWL@fN#L6yCPMI~v-^(DZmyzlrgd_Ms@es|N2#WTOMq<4yf0 z##{OUMd58dZQAc>Y@Nhdo(yuQHZ`}UQAgpnTIUvy z%e-S6K(tRm=Oh9-l#40i{K0$gMj2a&#yqNI2Gnu*hjmHj8NSMpf%4YRk3UMSXttF~ zZB84>D^dPlDv(Z6z*K5|#wQ3Umg?^)4 zSQz+W{hazme>KbcIe8J}0dh{l1I=BR2md+hy{q9~6L;sla~$4{K5dG5o&ri8_7}wg z);+%bKFj1@QqW?L%<={Kgw@h_rz&2mJYa&&GI9B90rapCgZnF&SC=$vXH1 z@edIHK=CJvKS}&U#Xn5^_h~<(SWZyg9!fr}2YIibSIqc54uXjA^!0%HO;Qh>^>g7< zQFz-1?+9Gug3{wn{U*j+>_$;|R}J2A;6{X}$D8_1jJLE8Md5Az+|0*D02>pO9&hS5 zG2YS-C<^b2!8-=qsNnQ?Q@@Gvmi}H*c-sc=C}3m4(&J72CdOO(^+n-bHF(Eh8x@=$ zZ|XNO-ZFkG3UBLK(|$)G8y%J&Z|XNO-ZD-v3h#=+I~v-^(DZmyzm4#&ub+Q4DCc?K5C?{Gmq5!Y94kYX*6Ioun}rtgIzz5S!zN1 z6m$-3h`+I{pH~i#UO!(kBd~rh?Ku0Z)M;Mp=WERRc?y$}uD^=)bLtoU)hz4h*s6VCF|$gwVL&Ft-N9WrsDzD&#B)=;}));(>`oy>*oi#Wb^6A zI#Slbj}pIC{5J8YiGPgv$B92({1dhBOVo2xUq6?8SP$}EKex^J-J|tJhw1A9^_!#~ zIP2%ar=sw#8oV2U?75yEZ|XNO-eNb3!rS^+Gau^#w83F|ys6*BcuV_G6y6nscLR*Q zm($}-{U*j+`T<4ZZ5zD3fHpWzk2m$37;owC6@_=z;N1XY@8$G(Q@@GvmVSLvcw5hz z_S=i-@W<)#rhXISE#tSM@U9rVhlkjAH9g+cZ(_Vl@np=P_sm*Un|FPp<=RnCs^=a)1BaJ|cSk+&Wpe+x7J#X~)@LZt>*r(l zGT(2H;b}y0y8bHGPZSF^02lNUiAppfK5)VevYBfQc2dDV+9!tXIwuoVE^;3SbgMjL9d_p>G`OUy3RaO z?<*Gum_&>Qj0QFq4Q#OQpKlh8z}s;BJpb>vSKlAKem?!=!29RYj*v&Oqj3w@ z&uJg>Tt6Q({7sdTte+q3lFgTz)i3Md=ZSxT_!o))aq&MX{z~z$6aRYc`x5n>)Ys1? zAJ&7s*Uu|v{O-|uqr>#|fci~R51jRL;Zsp~+XnAOAbYN-$D8_1jJMd0qVTR7yghg} zI!uo@^_v)PX&;Kh+j_yw$2LOQy`CO#>Nhdo(hn#K?~1|Og|p#Ndc3LM#CS`8uPD52 zgLgxe-3aONrhXISE&ckU@U9xXT{s&arN^84O^mmU--^Q9`lV^V8^T}Yiov^%X5)kOcvHWP@SZuNRC-Aj!1{omr#@31$4vc17IWr|nH}Ao{r$~sd@c#b zzJLCa$Q+vsw1sb#U7zuNcN6nXmP(S>&rrv>|7wVzUJObl$}t}0tQWN%V|uUswXaO4 zlC8@z9_1<`$MjzM!@@G1SB~*0XNw%u`{ggrDARf67>{yQk*kRRD_bpp=CO+9OF^W7CEMO$L;%->AZ4`N4cuVRm8vXhF!~a zUOC32ob@ZYKasoos%^@2UOC32Tt(!V-lqmiWje1M<5A8QIi~m0AO6rv=apkT%2h?K zBL0()Ki*2`m18{0S-+P16S;ftzPpvqE5~@0tB4%ayJ2v!mCh^2c$Bk6j_G}BV4#)G zE5~@0tBPDje8o+5f7*HF7>{z+fSzAwdN&NVsB&uOm18{0RYZ>IJ+p4z6gsaQ<5A8Q zIi`oW-XHA*<58|EauxAmBi?-D=U&&K5bQ z_l-Lzlwbe*->3ZTwSHfIj7Pbu$W_Gu>-zQJ^--T*G1@1`c$Bj)mHQL9?|l0}Cq6mD zaxxy}Dk8`9{_`&%E&uK}^U9C>%dyUVQjYN`XNw%ugFgT8y9MR%-80!I$9R;hid;o} z$i4W&ndP7S_yayU#-p5dncSa8?x6>#m9M>KJD(imQLZ9#Oz(xE_VUVW$CU5-?st9q zWIW2*BFFUZ|LK(S+J_%@Y>XFo#-m(STm^h-Yds=l&gpw)BEQKrk3yir$fr6+M7PT zFdpS>kz;zlc>eUVve)H*S~bake-e*!RXJyRu!By1D5Uz;@e9aVSIYfKIPIC1a^)D0 z`Bg-Y`69pPo_)4ncbOmKQO*`Qrg!fT4=-PO$t58<#-m(S&q{_ z*@$a(^QC>wi1Dtm;DR!w)^=;~C>ot|D?wPsx~RgtTR|K%HRbowEX_v(f5C}({}?oZ@Y z``r4CJMWwlYX2l2<;S``=F}onTz;>XY#(XNw%u`_q4P zmH+YI=ZEANk8)L!tBC*fQ>QrNY1kQWevC&s>l(Q~k^9y+4=6wS$nmNj^UEsl(R*S>8XC?6lLREwQgL!FdpTqB3BU~_jL7DSGA(NzWbBwl(Vjt z`;+jW{^TgwiPk^A@`|tCG9KkBBFB7@2kh?GzxFks9OF^W7CEN(n`&hW+!wCj$tTBn zl&gwdMf~-DURwU||5{%D>-t{*{mFI8S!4D54$~WY?zHkBf8SZI{^}f`9OF^0B67@E z^#jZ3gWmU}_xt1+k8-xiF})XthFZV-or6>#^f;d!<58|Eaux9}zwENsZ+_$5e$<`m;d#?`%#-p4qa!l_>_a4#u zm78vI9I~$47l}u?s+==D^jjWVJH0j79x)!}tR3Y3L|);o_)-nGe-e*!6**^mzz()W zjpKa!WIW2*BFB8y{W&t=n(zJs`Bmkd>D~3+iO&4l55E6ipB&>cKWj(1Kaoeh!x#?Z zM{jS^aMYCa-DM4JLUc)9CNh)^`FbiS6sfiPmb{@R}neptL(00&%kXj?u?$KS7GJjz)+%l(O*V;>^=S&T=y zipViN%qd>~`5nu_@igO6&K5bQ_w}#s)9USu)a{GJqg++anI3d_{Jnbvb?D+E0l`xvHEqJ>6f8=wAoq ztQNUH38!4JpT&60uOf2H*DYtDe{z6tluy_ zQKRzwa*RhguKO@OHP`C!g!z(Zj7K@9&-8dsx#MS*N*AaFBdhH(XI-gVMM$%LcKocP zd%C+j=l6H@EG->UPd)R&rdDaxxtUW5Ct!>I|K9P7-Jk0XUr!xUOD!?10C8kF@eOO> zVeO{t1%<8q$KCENKjj#Ya=cHbXPxkc$n`_Uqg*{7$#?Xq^*()_GObivpf7Eu?itQiR|F1W<`3niVN$0)7uz~W9N3L}GHe2@X==g3dZFA$#am5?QdmOyR zRDyBh<87kSR&KJw;vSKnjQkLWb5*78QSJ`J!?}w0Ugh4c&hOOmN~MPZp!#jDYu{$i zIrXq6b#YS_9kNkn(sFtIwI_UJXRkfVE8S+(fjxXl3A0=m(mt(;O3|yMY`VgCm7h@k zv8|uK%~>am$}JeCik9QgZbM>q`z-B56ZU!eyv|bZ;?BO#91G_pg=c!+s-LO=8|2UW z{GR`(_IbsoFFWly{iI5o)UoP=sOM|W(LVG$ypCD7g7qpc+*^_;8Six*d*gVIgV!h* zj5A!kG;x_eeb{l=K5qhRUMdCb^X4iF^6=)vbNWm#n{QvTbc3_cqtz{uPq;fTk6^#? z9x{36yw{DuNBW81?qL;AL7_VT5H^s}B)Kd0`PHKpY8(ltvi@D{~fd-u8T z5BAldpDnbFa=7$%8XMgA$FXg_=Z|mu`yEe3^zWnWD8mNImG3-!|MsmvZ++)cKe*qo ze#G(qg6FvA4f7rcuQ8oqoZ;f7iOclqi!2PR`kVvD2G6ULO-i+zt_z#11_|-J`S6_n zX!lbPPEs50pKo@IYfDd7S zU@YrBU)D}5m8MU-WxJQ#H1=cF`5L7W&rj**mt#&)*C$N<<(NlTD_2(MYY;Z(H^=<& z+|{@JWcxQNKiKzwPkG-%zx(yw9!c-v(a1;b%%L29UY_ZY?0>DPlP6DZ)9J*gfA1cb zPr2cSf7I*qbz5Iie&5x1)X%e*W5Y^;{R8r7D^HK>{_g9W%@K5yUl&@#$qM{UtHfh-MNqJbl6-Vmw zvD|_3<*vX+^@HVM`4%58<&$!we5}8{J>3^B?OD>*(LMKEe6K5yAKLP$bcip@!}5*( z**Q+XgyoJaUtUW&=w}R9K9+~&oBp6KAIlwAzC)uDZ6XYPpeQlwkL6+c*zU00apiL+ z|DzI48HWB@lnBem^00i2%W}t+?=VKp=*RUPXDF+^QZ=p|r;bv`>VM+jjH&t0A29P- z1MzG#^qqb<-uu&Q{Uztw5FEF{wp`J*bHp0Mfa>SZP|wy_(^UWaXm#xL z`!NE*y2P~){?}3P!+9b8K)Gj!OOI5tQ&sd~e!8!H(y^t|D%FYPT=sNzBe<8OzmtwV zy0A5dk0vcdhIrOl-*@cc5h@Kdz5rvra-(1Ql2mJa3+u*M=LbTr)qU6)i^_KTuZbKYiWSTgd#3jvb|Er^aovNonZQFSp)#aj7)) z#EXs}oxTZ8ssZ$eP@iefAqV|ePtniB;rf6&r#-jTF|JQiZn$4>&$2G(xwzpXCAikI zNB)16DDz-xDtoQNYjafGU1vh4JVE#ocS}pbKR33WSJgiow!Ul;0Leq z+>u3Ep7;z~?FnZXeO$x+^PFWtIFE6SaTxCS5{Ji#gJZ|ZG16GQ@7>>b`f<4S{mMsM zA&28PPW~wScdJ*eb>m$6wLO1|IFK!!a`%)akL&o`J-FvjRhSb#>8p27X}R+UQ;-hA z=KgpI(sIhu@+lpTG@iasha=8^eBy!DOC|ng7jNdIgE*@$cmQQ`WLq!S;fP+I{#HdWN}T92gKJtLuPq}@?M|>V5{!Ys6tQ_Lw z7(dGHqK>iN4F8?VAs*u3d|Tzlh`*ikQC|3vML7D9IERmO9Ph3ifC;o>#A*78Qc z-VEyY8~LNWaX;n{vlka9NqEbsUZ1@il>C7Su#WqE=(LkMdgCsp2ybbBr!x+w$lw>; z{>vH#1obmRVYFT%K`-DqaEG3NH!wqafivnNFh)G^0d)&pK)ijGQ%O1TQGXzZ@cos; zIqEgS5C?S=JcG?47T^_Z1Fj=YTtghxH`G0F6`Vyqg$+Qxgbd>0I_e+FjCzYY1s~@q zJL(kT(heXW)Uv_D7(_lbIidq7<1 z3uS-|(nQ*{QwYa(F=c+;v!AjG~x$abC7b-A@mPjv3{fM&=2AOSNw3VxF2AM@*l1o;v=64%DqR< zkq(Yw3!yW_L3waY`)H}-EtG>@nWP-bhjO4izy^5JcA`#M${`-?9>Q@Q;gDxKILCFw zL)lP9=oa^e@_`e`A7w)x$P4#}`-JYmOWYfD2%T@K9Kvu7+yRI1Lmb=_etRj0`^OJq zxJT#-`~lWT1LXsc!BM0)N;z;B_ke4NgD@Oh%I&QjxB^Zg95S%CYZMT)JFM4`!4GNl zDu*&6U!;#RqZ~*JWnQiva77uxt3KsEs2sTgj35jBTFQYhxOZGfnf6x>`hy;kZ;x`& zA@m6jE>jNIq0>pqLC4+70TbYa`vn%zH?9F6=mi)7FW`q`l>7b49ibfJAF132ltW%0 zRu23^dPgY-TybrRa>#dqa=@>w++5|_lmi}9l|vrT$28@RQ4V4IDc7pp@yZ>m+;rvk zl4GO^3{FrEWrZBlg&q*^MCEV|I&D*KhH|JYCo6}0K36$QxtYr0UQSW&ROJv4VW_hw zDTlJmQf`!Tvy}suIEM}qAN6;Ra?k_P9<3bWq0A@)t^qrwUr}zJa_1-q4nXHeD|fzf zA5jjvnXg<$xqXydsvPcjZ{@}-*QFfdZmQg7%AKLyhm<>7Iq3aNNaHv;MqI=}nkXOi z3SQxUzz5`wa-poq2lqc;xdqBCQEqqTKBye@xSMh<${`&3pP*cia=7>RD7U$Cy~;ty z&;ibIuiy!EwX<@4%59_Ej>;XV+%n~MQtkldwo?w}#`SHL+eA6&1lZ!Yy>iQyL;QCt z=k%i#;B3!3l+xF$aK#)@O}UEWk8MAry#9_Y%73|IKgX}0@y4wmCjc9|NXzuDrlVaB;A2l#QVDnM>^9~JV{4U)*-}Q+4=9G#@ z|11^0i7NlHamTm9|Hz|fIQh~?IO3o2LW?p1Yg*gIf8RCp)wxwZ!tqP3I=xaW(nCD@ z4A)yZ4 zPhSt-FV2JMD?HQ>xImxu$&=uDFkIi4O28?f*VpBwURz|LocQFtSIqiQ!g>2pUR*Vv#Fe;PW%8lV;lr9%_%2^iUZhLE zB;}9u8IQh;lg3TsjP!`J*3%k?XULcMk!QH?3m)6F{AR6OKJw8`%YRn&9`d-3dOv36 zDUScD3I|_mDjxjPuRTS@^Ye!3==*7NuH*iY4(x!oE3Q4*`=!Zc)c4b_J-D7O(qFIa z6T->s6(4>_`3=bz*O4B|!|-4^h$DRJfxM>w8&#iR549Zvu5IOaUw4G#SN5Hvu4%hN z{XrjBUA9^I4CU|ri#J+#b?iY!>O;k`3)2=JRo+8|f9tF7bLQRUXJs>t(F6zqp@$uWMI2*t0xA`MUfA^>bcl`b>{FkjJ!Bh!5PTKiUcEiF$)P z>OXOJ@zc1%9&7szKDs=mU8lWP*F3x}l_^KNi}Juu>-%!k(dTV)zomM+MEu?OmWtEn z=m~fsJ(LSR+9kF_Qjb(|5MT8})pfKBxQ=!}UmuWi3^?rq+CRht4q9$N`KVv;D@aFt z=Q9_gl8Oqpx@F!jAdqtYrAzd;6u%dIXag z@{I4%&+=YJxi-o0!@a(y(%>39@y;Z+?k44I<-VreoyvV%xmC)2Pq}-Q`-yUElzT|IwaPuN+&bl+Rc=VR7nQ3j z_dDfYQtlPy)+=|q>O|u?nd)@CPIa~Fk5L!ua>L*0r<~r?*S(-&yk;Vx-}zSk`sUA^ zLZ|B!avmsKz5>{7O1o2a5T0)tD34Vq7$+!pOvSS_`q>wpqfUB%qSd`0cfOrq@9O{d zf;VQ=F*s|f4m#!jO(iEOMnw;Wojhmu;*JHKeb&i63p)GzI-97jqe6*F0LBA(|H#J` zKc4NpC|%Tl;!=s%VDxo|E~G@OGh2880+Gw z(@JYS)||Wk^_g=^rB9?T!55D%mEg8f4)3qz^ZV`BcQ4Z+#T9~I%UZ2R=zlEgt?2hT z>OV%$vzPZ}YD}}rQ!AUPmTVLCZ^dJC96qAv&F-0hZfCznknxPKHPEcR82arKPJ2L} zu>O%J)GztN^V52|moI6;i038GbT%us8JzIG;qQ0KxOPlPvEw&%LXh(&-YtNfYv2EsG9D9BVZy>Rc{#Wt#{9fAgCj$0d z^vil`?WeB?i?6b)o=fUTTd}Ipq_p! z?LNmC@G(ZH+kN2=`NVq5dQ5%JR(_*AlteR6f{9u2+Z=M(kh=M$b^zqqE{#pIJE{f8i*+?4fEbG-mJm8|ki3FDXA zm`%<2g$gOiZ+Y3}(-A5Uj)PwC*(W!1_FLIcC9gQXQA1_t2i{G>k<6E2?O7!m`oU)4 zU)*oTY&Bo261B$Z{@MFu$`vm^qibpB?EVY8GYqz363<|EV^RzTqg`lg6YZ2YouGAy zb2ZJYet~(AwsFVlxlHGx^9OOYd~iO~AICj=dmpv!bgI*@r4JsWv&HiRlrpqF{P#J# zV@Yp!*HS#T(bR8qN6LARN8~ZSQO}5|zbjOR_KA9a@_2Qu{wMyOH1(LC{$-Zx_Vt{b zH-qt9M-e~$zA4^0ouT*(-g#X;M-}e;w4X*P^(5*CP8xVexY4hA(E|6|$0|Pff%O@N zAFIL|Cljno-ushdHxb_FFh!$$X|)?^?KC$DHodDo^mqS?7^_H>7nQJSUg; zk31}Doo7_3L(TWUF|{&V+t2E0;>$WuRs6i~D=t0uZwBMmd1m?7c{ayGbHn^vZV9aO z$bE8NkoU_Z>_hc8;H@M5--73L*6wDPdfwb}&Q?+DJWQAQH1vD)IuF*x)BWC>b)Gex z7knl#FDUxuILUf%w4SrB(`1#WPR%+GHIdA3)?T##$sH$QJ&?R0Pwc}rPgp-$8nezr zO>7+g;46NfkQd|$?Kb&CtsK*}thZY|g|(z}X;Z_>tnw^}dTvwof7QD2XM^*?r>PBI z(l3Qn#zost=C`r>L;ug^NsCef_4Mzm4=G(&pK3z$dD8xqSBy-)@qEtWuKDLK?OfLE zGd@}6Sq?ni=I|<~`Qb@se%Mv5=cwgXmseQ7qMnAEAI5q#`egHEC+rA$mRf0VOa>{Y(Di zdUS7L&ljoJW=d;iFQt~}1N~0XFYB}Y!DzcVtz%iI)!dS=ql&EZ)Mwu7(G^plsal`k zzTRJ-sVA;S2j@mG|KL2YCj23vtl3gug=fsXz0KFe4?J%K8$grsw2jtKMS_;%ld|e@-5eSN3tz?uor(W*%P5p5KxFq0OxF zEJxi=eKK-BN-ekbcPyQs{E56f_c+IW?##_(qd#gt>RrzBnhsBtHmE1ssU1{nq2@T% zyEgb)Q^_athkUY5(0n4#bbR&QF}K}oYAPVBJj=m;R9^F&>rK1os@8MV{$H-0LO+Un zBA?LiO`d#D`Mbw|syun}vNrOG{HgQlWU)_Tr4FQ44#?)-rz{2`z0nbMCEJ4Kn^wcR8)U3>ZGSs`VVT{KL;D>M6jd@O-)Or_QGj37>>#%%aYRIbBUXNR(Bc zk9s55rA`ns@8Kfw27Zj)Keoqkw4_qiY~EF!ZT*!@X38xF7*?4S>;)dy!liz_Q_SP=cr{f zKcA>4FQ2flvlemt<>XJDPj`!b54@=3>B!+C@HJoyUY5BX%Br};#lF$aeaP0m3fjjZx4N27Tt#!Kq2oo)COrK|QG z{d}UH8qGtIKjf2rrtnF4#=IOp$is5YJaLy*p5@4UoM)MF9#!k}TefxigmE7Alysa& z9+6k0zc1~c*ehl>dvRxHQv*CnAgesf!FDgN_2FLAFLzbzIcl%@=cA}6wtI2UMUg+` zlXbS{6M4ogG#}iKIm46sS>@e)@}1wDKF73su5R6x=To=l z6M4oQG#{GpNM)60IU4n&(yr&-Y5L_?|(tODMr4O0@ldD_zXy~1OK2cBP(}DF}td-2ne?FeiuaH0F6H_G5 zc<%C{xyj9}@+?Q4PdU9my!bT3Cs()bQOhoVK2cBP6JmaJ$~R?R(|v!~{)BGdf_!pQ z)<;c!+{TkgR(U36zK>2#6zdO-b@_CJ%8K9bd0u@p3E%S>t(-b^_R)sFlbmDUZJdu2 z-x7bk_^@M0kKZGV``$Xg%T)gklkk+^8RYj!k=`Vg9=~tP@2y)Z3_r{Hi!yDZ_GLe^ zWVL)L@_iJfgN-x2*Kr?j$KRMSVC;~npqG-}i+4Al$y?YQHP zlRof)4_y1<4}bX8v(G-eHhcE$+Nr0WT07x{6KcmDcbwW$xLiB@@WX$-|Ni^mywgrQ z&CIJ1d+f1CTcuKYYWeczwPnke)p~k*YTe!4wMB~-)liQ1_I9T%C!TnsQ;w-qr`F(( zIO2$3XDP?7yY4#am}8C^xZ;W{YFA!)W$m)dE~|a&Q=h7R@{^yeee7c&tM&Eu)jsl( zkJRSRpYN38w9`(j>*MI7k9Nv{avXHfL3gFaWrrPhu+;rN_4&_#zIOff*Vk6AT%#&sEcXSrqxguh1;)3j~;C$ z#iYN#fA!5b-(0h8yY}TTf4O$U4L8)j_{A^QP#%;8<$zwGj|(oiz`^_4TiRy>|QUx7Xm`a?34FIc~b?rg}M0 zhO4i>x_0^Hm#dvXv>w0>lmWb$J9lmkc${?7NwwpTKfZSCvBx@2;qmHLTWw{<6+r3m z+HZgR+qLg}=R37;ed}AbZ+`QewL9*(qxQA0ea$Hc%5dY2H`c!Jg)h`T_qos2KKt3v zI@}l-7^q!z(M2_I6#QJgc(GFka1&)<9YH-AKYsi`RQ{?STPm+tzyJO3*Z%3B{;9TV z)heeP-ZI>J>#en~eB~?kGF*G@wYATD<}(f-z)SE!VdsNo@~2@IdXs2Oq4#M>&4-lb<+c_}=%v=iqVIU3WQr_{KNB;qZZV1NFko1?T}~ z02jPE_|S(wR71T){@}yzyYF714ixVO{^ei(rS{N657qwppa0n@$LiIqePtjYz)#c* zashP{HiBG0y+oY|>fnqs&Zu({`A?iU@n+_)ba&Syk33R?d-&mpopL}AYu2oBu=wE* ze^>(_paawm=m7N-IG|1d2j~E0fUUgr(n}p1i8={e4IAmTO~_xh6|0%Ql3)F^pZ%=% z*kg~?9)0vtryS4)$`I58$^adpUV@8X{pwd8yMQ_gyMX#Y9l$m%UAnZkaN)ulIDj^5 z#*7(`{*OB9sHd2}(&tl8KKWz~Wq?B&pa)=qGJuo!-FIKj%SY5p;sBfQr7wNSv5(hW zb4|TIpgw-|qaStbGi(y-K~(;#ZaMk04A29x04Ko7C_VxQ>Hu{CJb(_6KkO3PtB-&D z;|>pjK}Sc2V-ryigZft(u73LIr)%rht*brt)Kj&`AAj7319*TkKnFkg!4K+Opbl6U zpabv#?E`Ex>j5x8`vhA=dw9SB2h?qV;>*p?Jo8Kq4rO@ai6@9{lJ> zKdS2hwh?s!JV1MiwvrgIeLU~H^Bh~m`)B^!ZMR*$EmZB|SjD+l|Mg%0)zJa+_v#?X zL)d88CD;UDfVxO~0DA--pdO%oM*g^e;7>aY?tASY^q}y%8Tms8z<^vtUBLabF2XiL z2VM-I18-e~Jpu<{2hc7ef7k%jzo6|Q2CAB)Cj$3WeSZpLCT|{%jpwgspIY!lfY!Um!( zME!#ehVF6i2OoU!K(MYg%G-N=%9JTHl}%N!YuvqkBlHv579xMxU$lX+JqqvDs-KtY zp1t`7kJWv&seZ*>s%||6y(4e%9&HeKukQI(b-z!kdOUzJTTni)bMzJAEX$gt+K@KY zmuwq1Zrmg|#jkpwE@^(;cgVi<-1lR@(B}6YnmB3FAxU#h7H>s@Xrn*$DW}{CqB>D9 z=Mnt2F5jVaeJ85SC6<)0=2v90#NOjOzWmyz9y14e^Z)QpX870azC*{T&t3CAcTmZq zC+jyxB>v|S+;^Rvxbx@i;U+J&E`2;qdhRw$_@Iaxk zye|t1RK?ra-*oDN1*OiC14A*D>7=R2c5YgH%{h*}T6@7PXI^;p?>^wztHCRdo`X4I zOHDM>rfo=jnti$e^x4K0zNgvcx-G4)5P$AW2ZO5k1>9O>l3YWGyr=?O-hHO8?P|EE z*<)XI?gPBR-VznLPwp+j`(=LCUb?NcC-~b;UMGXio@UgD_J8~J-OF?cx!~d7eGpA_ z%Mbpgu778*D$y_3Htq4zdd|8hSyp-8o!Qfjnn>n1YcJY2ImO2l@`60E_R~CJ{bXs( zo@Ug<#^Dcc>gNf0L7vcVlRwl-);-Cx%Cj7Kzq3=BbH?oOSdO`Ey#CN;E>G~z4)v7y zogHca$t!A-eB*g@PX%-a8O#0RAcT$V&k6Q1M_HDoKIqjf5!SB1fkC{Eqs5|)VbHc|nP}9%Q_ORv0 zfqtjxm-X2`D7xR7bx*Ra^1Qda-$@vIrK!)?)xAF6ddSd%9h7ld#{Wb>qLq6HR z(R>QpspjA2)EQ@$cZ=WV^f&V2)2izXpIqI#M?>5C`9wVh_#|sLE}zIB^2z$W*e9`5 z%%QnA5hz_MWtDeR(tOIR|601n@X6J!d(<+<&nN1Md_v5~J;0dTBHu}N^V>L&T6XpGiFzWR(C$U;X-58#Pu4%gK8c;8Ha&c(Z?7>D z_B0zX{U}20^V@gx^ND)$^2rr&=MTsq^2z=mu}@;BC{y1J} z-Ti!`o&tOd&zB2-$S3Pfu}@;Bm`9yYO@0P3t31omXni>CHxS4E<#f~DQMn#}Xq=x< z)Dzc--S=-W$LX<8!XNU;)dyyweT{mIN5 zxVm+ZhW2uKdo*GT9yyz6N%Cj7K^Qmh3QLbt|M=j(1 ze4?Hj@rnE)pX@Qx?ungZ77icWhvi1ho@OgeKZ+3h{PunPe4?Jbe2UuBjQk;=tR2KY ziJf954xgHuA=fEnm1j9mGvM*fgb z_D*7-#7;2>hYwB8K_QK-@+?QAc__w9>aV-n@F_}HL;LypL_IZ{ha!K-CuWS@M+;dUn5BX$`6FVh5V-A`RZpWPAN&T$yZa(?G|5EvX zX5PTnt$Q?hfS*s)Q-DvQ^*r*2e6sft`y_UXIcPpK-;v5H&vG>CN2OiQD;;>Y`7QvJ z>+y%)hM+taLM`cH(|=eJMv^ND(5d*_Sn+0%^tA)hSyjs$r|Id>1K=Ds|VRi5Qw ze|$wwdzy`Xk7@5*-MUAE2mAR%J&{jwdzz6ymChF^7Dy$BA*a5a!)h)o=uQXZp!*7>z-s;}h89 zG&6genLW+24QogGo@SM+a@f=C&}=#mN`EOB5OF^Gk?QVgzQ2JvkKng;gYQyGHu8*Q z$ywvyL?%nDZU5bu->MVdqCL&zJ~!k&&1Nbc!L=Io{H+Nd@^C zjNCGHj&H4?;httob=wEtU~dWDC-;`9%Kb7QdyI~6?c#4UdEM-3MosvA@7@x)GWKV( zr`dD5uLJ!%--s0ba&6PvAzIH__aw_I&$}~wno$$U{ATS%`}4g{y(Uk{3-ZL?N%MsD zlch0xno$!Qhd(&v=Lva1p3rWSKh#RrJ;}1lvmAMUo5B9P-tU#^8((W+=ocD6?ZEwgc>Xm%sd2>$&QhLMP(`@E5kNV~e+!AlxM=if}d4+d&s3-3;UC@yCTOZDz zX421Ozmr;If7ISp^Ub>CH;&%z%ly9C(~KJP``&&h_B2~DsO@3PF9ZEf(J$+>H7>f} znRQRHtn$3OM(?;VUQ&PMA$pIur13YFcofJlcM^j0uJ!(@@P~Y|_mKXn@QnJ) zx+hsyd6ok+nVYD8dA-wFdBwC3u5R6V5gdYn^TuD ztGs)s`SPh`_~feAb2RiTKcA?l0H4C|bP9jSCwsiuC*c{hXzon}N^iJ(n)M!F+B+8| z_o!t+@8#;WV=9<>BA*a5YELurhkUa36Z<4QV-8vOB+Dw#a^&6KRm~pHu4+9;?Vs}V ziFzWRbbA-x(~SHfpX>=@pTtfvi>!N+WtC?+@}9S;nt2{X&@+=4U;>hb8V{N}M{c=~g?$OYtF0U|eLp{+>F{XFk2J2Pi z5BX#tBz8*d73Dm8aWqAZtnw^J-tC@k`cbZGJx48<`}ssYkx#MXJmC-dWX;s`eB>Fk z@bFqBCv#9lEE51*QvO2{hDawGx^<5RKj-oa^H9`NLpvq>A)l-+u~Wh`=HTqf z(bUR|P9dv2%aJ#qY{Ms4wVtD)>->D8o*MCq{2`z0bHzRh&zOb72lrvQk+Y}S(`H_i z7<>Je>-~IMDSGnqDRNIU;Sc#_EfxDDc8Ym8d}?ZjT&Iv#p5>r@%IkT90kiJns@8MV z{sljus3-DCM~&FiO!z}S*}Y<)glEjc;X{*iP)H-IJj>B&9*Xgj`faAYi&WL%7yW#q zo*K?W34h2ZYnj+5;Tbb?_%H(ZG_%b(j}ZI(mK$6?Iqye`o)VAqgh%9+yl0$1glEj8z6aF^+tX~N;S(YD`7K}d^ND(D z#3%BHe6lYV`y@PLCUriIuszMz7(NkVpWlA7pHI{i+dE%m@1ADDAM%Ocks!|~=k6iZ z+?PwT%Cj8okLR_gS=Brb<*L?mG-&(zL_LvDv3r`ycSy)5CP_QRbC(azO>SnDXF2M8 z%4tutHuJoJt6I;|&@FyGQBUL(qDJm%Cf~CO^2w95bC`8cvaIqfkJ;0Vnkd$9xy|L% z5h^QwzbCIf&GVfE_OwV(XtC$)R;y|?zg?|siH!?(Zv?b;o8 z+)=yjw%cl7{pwe1H{5VT?F(P{LT%;Bl{K#p%${auPcyTpnc36K>}h89G&6gev9)c^ zAL)CVRlkzMo@SGq(`}s8*W)gD!bgu96*=GGiQ+rNoJa86y1{oTML@0XJKv9U*7!G( z$r5|YFMQu8Uw`6Tw5OTeXX-u8Ja>PQI))3*`+Q7=!3D!VuEO9r7o`rJ-zU{yxM28z z3WE#o4Yfjr!3D!tsxY`<_@D}d3x?Y&3@#XcrwW5>Q_lN)Nu9v8c>~q)1a+fu!Ek&p z9WJ;xRJRI)3x@ZqFt}iNzY2p3hF_q<;DX^FS7C6$@BtMD7u545Dhy8j_x&za0dT?a z%TxedF#K{A02d6uLIuDD!>?2UaKZ2uDgZ86{;O0NTrhm43WE!VU$4U8g5iTI3@#Xc zlL~_ihTAF(E*Snb6$YpN`+i?n0dT?a+f@KuQ2%$TFgW$!_xpwlfD49yQw6|n;ft;2 zbX6GK=s@_rDhw_dzD9+?A>6m8S(AH7VoumnH%FVcA?;~)dq5|7oU)B8dQUU^X4%W^ z7VVdI*L#}P{XA|aK^j5c21QzqNx7%lXY@A+!JEa3hrG`rb*%oED!gCG$SOUq_mtRD z`@#Ipo@POf=yUJh5(ueqPqY1XUkCbkz7Z+<<=UpbX|$fR?n#zao_A^XG@~XG`m`Gf zqcd9j<`a&@8X$Q>UXUl&=9(w0pOiCuno$!Qhd;Q5pC{x6d18y*CV!}vtb3AWm1jBf z{x*a4;&Z;S9rM}P{h=*gp5UDw>M7|vJLD00MSYTQJa6vFKuT}8dzw{`^UoQ$CEmD? zTDEd|HCCyDdh$Ng1r2$>^?`mT`$AO$)~`RcQYq`6WLf1|o<{GuFkVuB-6hYOb|OMq?OXfnGxfxG z5`y!t_5P{whkUZeNPQKaF{`Y5l4X@=IWTjW=bmOO-(%jvb9L(;4Zg$AC+dmqL)<%^ z2J8Yb&rO&3Ghku6yTGy-cwR*KXv;g{2`yL zoy0zgonj8ny@^2S4R=qo>d}TzE=ulE%XWS~QBUL(Vn*$0M*fgb_ReCTglEhl>z-s; znQhuVLhSR~cXIiJc8_}Uw|nt>nvqB3l{H@M zl<GBHg9`!^!6}P7u`9nV0`-+_sd&N9Fd}?YcA*(#g z!G2V3->f^{@X6J!do;AOpHID_C-MpHUeumudDI|H@-W+N&b*e_5os_#7o4RF`D7m~_DOigJnD_y2;0-FV%CQVvCkjc-OnfLDPW&O#%=G&AM(jMRP2-3DQ4o~ z(}>#BY`{E^Mu>fW%e(x1qMjP@iToj-?8C%9iJf95S@$H%D$jD{z5Y@)^H5Z+&u`zu zSJvTTr^H@SGV7jXS>;)dyyweTzGBviUER7zgL}HX!aNl9 z)X+`|f5<2M{bHwtXUxIblcTAX7o9>@d6pw@KCSzc;ghRd_h`uS^ND(D#3%BHe6o%b z`y@PL4h|pOhvi1ho@T2|KZ+3h{Fc4_e4?Jbe2UuBjQk;=>{hW)VyBpi!>6WZ$aM-? z1uEvKcA?lhVxLuAM(jQTI`e9Ddy$yVFd1JX8oIaKav=G{h@tb zJ~{74ik=dW^Mps_m35rhDd8LQ(tG|i-!acB&vLNc%WHjj-5JJCxw>_aTK0E&g>fGB z#C9+CxhUZe`D9NQJ0(104w?^c$DH9w{jBnCKKZ`?QaaP{$nuIEiMe4=W7{@}a)e4?Hj_M?P9BfS*s)QzJf+Kjf1&Q|y!QjG5H=G{W{YTWR=2h<$#`fqp(w zPi*gekv)5wkw4^1I{CuLG$fvkH z&B!0}iAj=YJa_rf+~j6fd6uKjr=0dQTW|Q}s@8KfIML51>WO?p)TlkpEcu>IkWZeZ zox`kql4X@=dCZ<>)I_oV(7`UBj!;?g`#pK>X~yrD#_egw?=scD!^H0{$#*LGJyJZ^ zH%XO`-_CBhr`cipJ48Y4AsuH=GtYH=hxG0EGkcnuJ}%?*42_4oF`RI5($814aWz0~@ji)OV9_g&noV zYTqfV+JtQL&<<lcqZEd}j`Fpu zD4Z|)i#kU6tZhB>KI(jScTc}{h&sslV}4AZ`Pq^m%i-jAXvWNxIP+ur%+K0R=Eo#o zC%?)0<;V1ypDp>Z98P|R<(D7RXMSowH9h|)`8xT%H^2OtKJ&9BKbFJE?|u2@$Ml&W z=QUXlC%?n<%a7?ZKc>ubIQij$-Hg9uPM&(mv#)JZ8f@SBZ!_oW#8Cfcs|zPjJ?Yq) zJC#ZYEbQuA)?YfHy90k0bS|6U*VWtK)w8tJx3pvKvgPyoFYN6s&F$@2aModGmk#J! z(7Cj~YhhPsU+I9Q9ZNb(=XG>1?<{rAR|4}omUSYDg&k^hD~#Y_2*g9pIkpB27N~|1yEA>zcqB=gtzxA!){J(!fbwUi9N z91e#A2Ag3%?Dp&B=AZ%hN7tBEvvKTyX6(D|=KjdQulC#1P6OA!*SyF2kyodmhru_r zm%hB6Uk!T6-~P<2YYINo9IWQY`JgvBZjbX@6)sMjnW5X=dO!E%9l14kyynUDsF#@S z*O%-4`hK~ddG0#=cDtP42KH&w4X#Hfr@Bbf!211Tznj(gv}y`OOSzjLuZKRsL*|dc z`PzQF%Vb^ju9#T0K5o{B;o_PFpYNB~PqY1Y%VZsn+x`5C)!v^#2hc$8I(wz*IUMK5 z(_uK=Y<9c#O0T@!(rCTvpw)&YJN7E?62N|aH{WR$cZaL}e0M!us8_Ae2ffzqW-(mM z4~O+Jlbw}eVR@SHZ$=|Jp5HF(XLjPHwzJdyYCGGV4%ZDlzXS^=;KTYL{F$Bfl<8Y< zAJ+Rl7(e6Dy#CX0vE6U3^qh;`(`<9ktFo3JnJ&*htmil58V_x5yisO;x694#+L)q( zR%A9`9DaNC*=y~s-5lIF4j{^o&b8<~Un5gvA;7fCAUpy6-sR1D<%TubS$jlv-@u!d z5$(4>Gjkt)0|Bn}N4T!v<@Nmjie=NH@_KhW0YQr8(O0*>vE#Cl4%!1(;1tdF$Jymx z)Brbmm6qGv?Ov^2nC-TQqiVOC`}OR2y;z5YzwF!$i=VA7+*bWzq`Ehy8q~9~Q zDd+0o8S@@>$ppity+)nO51aJ|?_6J4%x~6X?Qoek>-t?RQ=juI;-C@kZs!c~e!IV$ z-;VX|crOlpVRy}EEZetJgl$oSjIzD*)<}2Oca>0+<;!( z?Cs*+a*><%Og0$8T?Jsi)@Oiv+O$o7a=)vdJzKEjR`b2Lt`M3<09p5QUXi_WlL^^x zk1VUp^Q(PBIkzmH!n0#~KstGH*C!%s!VAm(6B$aQGvP6Q@2!BZWXLBQ)d6#4>$n%|e~urz1k z{Y8Sa0rkY?)ADw+oB+UH82Vu#jl)_=E??4n<|l;7ZfeM`Z3NLqqn$!0{DdV<45 z0-BU9!&E(EEk1s+5EHz=s$0%#brbZVftjHd80*D8M4CA;!u<{0qD?}o!)9fEZQ?vL zp&n9(P>a{CMJ-|H+tXf*43;6*2sIcEYw>#~-@;EWc;k=7*DzG^pdZYuM9M$=?JsPp z^%XqJ3wzkjmuu5&@bzZq`_m5Q^YEG>Xw4Aa=BvYCmUGKm58jCSGqX@OKOMJ+dqjif ze0jZ|Y11)@MMEXnX}lMk-F8CezGjlVGDgGA{UuVWcy(r91A%{emF_l=Ge7VGsRr4% z)?iZh3P4sQ2-n=_sv&tF@G=uyjKU2Hof*f1=QXU($VP~}f3~)lUfkT_>lYVOkVq;! zo$V!A6zOyQxC1FN!3PF1to3IB;{mPm%YNmy?fUX^v)q_L89zlLp-Z<!j2sY4?>pT0OC>r^@5hMYZH3uhRavOjHKZK3{zGtR6!eAM(Eboo%t5@l)tj!W)^ zH|O0R>x#c%xnpLy`)_J1BmgK8#3I*7@PX;a~VF9IWx~@rF9Fbg@gZtaLM3Ci8 z<|JiLPsW?IIt*cd9&!JF58s5ZU8U62@a> zs*m~Eqv)xlK?U^^Its%$lY3EjcH(Fq30{jgMA|j#nRuoyfg$Q=>R#tb(@EV;U1t`x zkmDrP@J8Z7*QP5bu+wJydHaF^`n_53uic1RBt;he*X?;R}6U+GkH-#AA zt|j$BxR~q9+qGqnw56L{^o;p3dIk)IiDXYO1hbHpmn3r{uHp+=F8s2p%q~wKKce9t z4#UIt_Jr;%J_9{wk&*QnQhG!iwPY9q@AlVG0v7Z88xj27M(V-*>Z;g{26cjXJ+VWj z70)+&+1X}!4cF$6&uZL~c3hbwSf~}opTkyjXDNeHOjf~d3Uc0GrvCN;`Cr-$hM3jnL2|0cQBtG?9M@yal2GgF@tWSR zwOF1MGbBC^al%F(#OM zk;I415tCgc>Gsa8UN}-$^@yOsX)mLOglkW0pQ|(A2~~H}M#G-g598RU;Jg50xNTq> z`~{2`K8@JeOEbs{+R$n?zcXJbvl!cF<|oR0zPUs0EaO{-QI&SBQ?xe7)dm|WmV0L9 zY3AP9A$L2BK@I3-5Qd6N*Ql*7ucZ(+(zLu4W4vSe(Kin_GnX!o+bcHN%!|qnMu47e z?iQy@DP}<@**38~)hoCK3uyyr$!M{+VL>%t*&`@61hW`5Y!|_1qrKz`Bbf)`?W=XG zHO-FUzFknFe3p zZa*L&$%FCeUlBMMiIvQnRHQn+v_>lJc86qzS{WUX&wGP{Q9p90Q=8SCbyodo=p$Whg zds6qBxarl7*}yh-Rw;UM2Ae*x-IV9#UZe4W2{U=|t+okdwQ8A;*e?2ZZOC)nqS|h+ znA!w4qz#W}eVtuyn|=B)nTdY?_UYAjH$t2E`>ef^+0r>Ia!+Pu|KOny*m>BF z*AmsHciM3J381uBU#zVwtDbv0#MrvI84dDoCl3z7kmayGh0~rpu-@cZ(|RV95R3lw zzCbL^fGif}a%(QYKu^Im55j|+Md)ldH(K@uIBx;s$|*I=w#5l`cK>9y%T6TU2?iXQ7eeb`Ct!W3F?#-Ys@y_jrUhN%^5X&IFy0~Qyq$qu0ldzZe+8J_lu@wY_`^^QZs(q!c;&F|3 zCN&Q?n!xZ6?xf=*cVdi?-lx}wH~ClQM&w+KZhOoSk)EuO#YW5(D1)Ra7jR=;Z!YQ4 zKTpGdeK%WeFEt0P#U!+J9=@7f z8f#o@UC6k8zr91upWPsMRmW2(hVvJoQVcVQY1=n1LO;h9u)PEchO6C4Z#wAT0(*}1 zX&G~gI{~G0sDDFm{Mg*bu?h$C=75 zq*aP;WrG0m8ZtybTwxp7Q*_*q-8*)it5oYpbB_eCL<|81SDj8)RfTyvB4H*y8eZai z^e6ofDqik4SZ@3N4u|XdO^H-Z-C^8uDaaZIJ3__L?b6!m0f%MS@Zp-_qvj||sfsKt zgao2ib^}RzF=6Bpv$Ezin%V?M?1)5OG|k|_rfrKpCcL7gfB6jxTeI9p@UTU(oh{cG zJc8#E?S5YGzjL?+53#kDY0ON3uFwVfJtJV$bG%+v_Ucx-maoZnxUmo8zgk#eTB&G6DFv|Qb^0ri`wqf+PnLTh;joVK+`M4Pn$m|n1B?wB`OjVDEPZAZ1$0?;3*-Ch{x zpxW{lyiO-6+A9lzMZvw5;;on)+Y~3Z?BVhn%jU=~@m)cI#i7MV6k<{mKxoUf!o1Nr zToGrSW1A3puBt(-@Y@ft5gASk9J@7pvl+zmG_|qixK-goC!CV_9~~uOQAP%5#}`S} zIUQt2l%++rsNk*U)kN#oc1P_x-1K&5S0G&|3o;>Dzlm7!IEb?_0Z$T^EGvne9_8~B za;G4F2C)(_LX{LLLGdEj^oAHbEw)+0CgMW&heBbpG0ps;f>9XRJ_Dlz?O8) zy*Va-?6h`a332yEGC5u z|1_a5m2;tQjaNs8b@@Bur9)6}*&((f%jL23$x{ zlLK6_xVZ<3c+|M%&$8=`iFKf;D})Q;+~h&}J%5-WQ4`r|sF`K{LmX*C0yBA7=_KI# z=3XrB@#8y55^seoPh)X$x@FV*uX%QrK*5wsCeX&g9E1C>m0l}C3?h>dh#nEL+kQa! zW_$BKwl~^qHmtGaS{zYK!7|ZwTuCAJ0daj~ajXD|n>+1%Ke9no0em7DCij?49YTp} z@Spf`9{x*z{ts@w|K#6)@$diW=|9Q@AmM;v^fD{4pMUoeCiXCj(AFhT9l$N^X(1lE z6)uh@+h;@J&3x&7XS@W@OE`>f87pz{yDJ8yzEe)b8vPYirNCNS^)qeX>bYlHGtNxf z%4MZhKD)a^>i)AjJ@k6HkQgZgP_*Ch$QU6={P~za;DwAPBFqZuC=u_yELE~7_@-8P zr${D)y#m$N2xug5W0$;(5~gvtMpJ1Tvf&8e=zh?E1d<}JM#gl?D&*|r3Yp8WSg8wU1}p9c__L7no0%7mT!VB_zZpEa)3losm;ga<7bZ;%qZV0#}E#|0uQVHgZr zku#FwSvwglWRUglJAQ!5*A8&8=)r+V-`k~}a>gEHmE*Oj488b$#pxwJCH{d&5uB1C~ihk&1ZYBDmb2B)%YY9_@UvufU|#n}3d|r8vtB(jdQ4vosqLrl`NxSuf+2 zX2L^~F@$firrZ__d_>uM;-A`Ql)8+}X~V7xr9IC+Xs#8%&mYa~CDBfp|L-=pPeITJ zLoWQo?E809zY7Y-g{EV%ebg)J$5C?*Avht76H35)LH%CU@9S2FFgPNl!Fyf!_FCl4 zFUcI@0@IO#;*+AbgNBOik3eF&5+iOIhu+v_l{oRppx3WN%FNeaw~_+;jSX5|E2J%8 z#g&Uz8_d8Rd?xDy!$GU8p@gQ$vJ4t~IAz#(7+#=WC}U(4HemtY(un2a=osRrpfXxT zyuDg)p&1-Bm>=Q73%plE4JOVd4GvFtX^`M-39xvu;fgFnOtZZ(!!yj3mmuBb>3xE@ z964iWUUhFVSO*{?=o8#_fo-`Uu_n?Di%MBG#>WNe6zll{stb}t zVul~20K}7nJiulm1`fdi?Q4L4A%b%we=q%)7+VSD{tJD)f53xs5ub6ioSZ<&V(*Ur zwU34Uhp=zeSkf1yKw?#>n=K)xvvnz|ET=x;pcDW^DgqN6L#~Va2 zFI+7nb~^JL_o-ar+e^>{!-2{;o+QuHrO{3ut`ObKBQL`)$~_F=?8Y-t87Jx)3Sx!+ zVH~5NaEu;oRs^nGSD|(iZmm{}Ke&=^TpW!ybwxN(t?WXuH}mx1_pMMNa3?c|0;5QU z2|FuDpO{G<3LvE0-EmGRnA{E3)Cp8Tat(#r9i9|ogWbkDJ?FWGgaEtu0T{L+8inARQgCvuwB+KnWe-*W$ zU-7r9>TX%w(ID7^vM#Wp(f_3UWcO)1!llqEBRUMyMTNz>RlklLQz2rddKVdiq^YF? zbv8?Fg8{%K(a!CP&^%$9?)Vu993Z1GJbmn2fMfgeJ^b4s~=k|10j@J^HY@~WL~Q}vM?Ig z>LSg%#ZbCeXcRn{;s?pC{6d#k%!H{+cEz~8Drl-_Jb{R^YhKXqjeNmAbKGNso zL}ef6ePLMYV7{6Y=)@rle92XzqbiN`oXsY%WJ9EP6&IG7wU8lSoyd7AWln_#JYZiU zNFSUp?rdJ6=U!u_qnpGQSfD@c?=|%fQpuu2>24qb!Uc@ct*6wHJQFLLs;HF@NKEbw zlIdah+YPhVP35T~SQZyaUHczT7I6<6mtm88ZSR+W_eQJHiE%=@&IvYZ{sL{gw_&Li z=rjS~IXnXp0J*C3UhsUI^N6eKPTvvM$^E4zI(xNXBEr`&3+BOHPn`6nyJ_s7OE`T{ z(1zou633+rUxetyCEiTKV?&1=k5y^o_XsEhGT=3{e2<5A%^6jcnR5_Eg9hp&EXB^Z zhLx@F<(tl(9U{7$HlBGG@k=Tu0>h8S{AY>9y<0P)UW-6*xLi&pF?k+KkDZBLvCh^vwnylIjeI-kF7d=k`C>`Tqe)n5m(J| zW(YxxBbZz~?U3@jTrs_^5sS0FU=IZqG<|>5^qF#* z?x)B;Y`TLqh~`mRUcwg$&_rI_XKM4@d-5CIa~v}%eWxg9z~J0R0Nkd;{yUn zJ;omO&iLC{9I0DEs|OdxKOHHA7`+;AGYm$j$Ld;QeCJE15G$_8A7%^i&~K_gB}2q6 zL|}w1W_>ucBS@v%OrY|IBBY@~#$08Iql+t ziReH~gDJYgbV9Cl6wjrEPPiCW>f&;Xa3SVuoyI2rS^2WR9l}5j@LZkH|zHpYxKgOYh;a-*~Zmv7{mW;_`hfv1(7aA zHCl1kNxJ2&qAw;O4xS)A9G|f6f!b1)rGXNYuwD5C23p!u@rOu8^V=)A@viSsQ8*RV zDfsGUKWi@6iU^G>!d=Vd{ZU#bS|>Z`0%=c1Ck~oDrhs)tYKX$`F_f#up#))q)+=bM z8@+mZbJ3VgOB3MO;%r zbNGRtRYeU%>O%O0!%!4ilO6#-I`%UIP z%b_bW9oiljPumv>Hp8T~y_fk+wymojp);C+Ode;b!~o>vQW%6Wczi*^jAX{v)*N=`g98nk;hceN=pF9^dzG?7$3tbY zU^&~p2hV=1!E`2vZk<=+)HlUr(+>o?U=LpM^`2@_w zD%6WLw)`;dSa+3grs|^vRe+Q7I#^{b)|84z8Whz2Yqop|96k>-^o~r>iwbw3Ho0t) zYl7KKJ5>^c(y#kgq0T{jsyd3VFR?p>~}__;NT|#H^?;5_Dj5zF3>xk)Y$`PE-P)8FEu_eSwn8OF$ppDg!*6!P*ZH1Oen z{po)t&M5>JzJz-hkwy=Mc2=!VV*@l|uOP1NURDyZ&-Nw~2^!fhF72(}nRfUr;jv8` z?|3oZXPZFIYRR}#Tf;@|4J#I-G*xn3^w;oH=>UFh4l$x>A>JyVl%(A3vXs?VB_~1b zj^{jqYG`Cf;-6atOlwpds8yMOIzf+c6V&Xxr|PB*7R+jRB`DbhYq+rqFBF=ROszE# zWci0@kVwsEjYR98wYmgi;l^mRZ0FL>K<2YniO#~*Y5=3$Ol;xx_TVe^C%IZX#Uk!16TQXFGoCS7M<3OI3n0vR zakX1p?Egf>A7Rg5@sle;oXKnV&d9kF2k}m>?fD7AW}-T7)+7ZHX0i@=uP_`*wyH8o zKcoR09>PZ8N1eG*=toW~NH)VK)$ladHqjPw)v9H4lk^VaDQ6wtY(>`-_l`LUsmz(u z9GeaqLws`bkcz0cu1In!76{8b=72E?pY&(1LgDyqz$|g8H-$^gLAfx zcU7L$GV=DAOB2|lgfr2h?D`?}A3+>8OrQ$$C}q3k7+sb>T!;Il#xoN#Rg)HvRbe*+ z%S(CulX_^bhBUSjl@J88EPfaLeez$UrK%QNRft(aCC{uBh1>aJeQT;zoU(jMQW<2o zY(`2_FoBQ^@!17-50U%?t&taM*Te<}qqWJbbAF&SeAZCo)N6xr#FXZB4;)leIJjON zqY}!mCBEl?NR9N?W?Mz6Lpq?~X;vW7{t`m!-;hw0IVElt{baH94WMpWHiY>jSBc z9Kz)vqPA4~;9!lD|0eC}2M6>(v54U!nCkEgq7kBfL@NYHUu2$Hgc;Fmj-)--ux_kMx~kI_sZrlMXa5E#re$j3mG9kr(yaGPcm_QMPz zBQ$uDu^15J^8_FgI;k?PV)|nb2ra}15KS`*lJ3a4JG3Uh2QM$28#}y)rE)yp1OhFe zz%+bxh-QsOv^i`R8_88pY8Y#OMcNzPo7qVM%XF;@H`>~<(??UC*KdT3rE$t54(W{K z^NyKK8LjV;(yfO&<>&Otp11M359LL8es;zAQnh_Q7ml>obfalqs55PVh>w`4`@V2g zhb;Bp`WmSx$e20t*GQXYnP6lKd&Pn~iXyt>c&3C~BPnkWSGyGAxqD5R561AKG8CN!jck}>9L!6mOPrWEVhuXxsO6^5MEc<#nFq#X+`{S0*B<73D#vM9ET}w z77DRv=D|)0^_~S(Tct!O(_h482b@`~x7bi67{1rZuqpwQ+AVP+?i`WQNllLTZl2e* zNsVhE*8&q3NjD4EOI)}5Bma<(Q2~3a?JV&^RR7MojQI4j^D0@jYc1jc09V3H| z86^C?YGtOVTI;0J%Hny~PC)qex#$@Y<~kn@^~$f#S2cYUSp$oQiRi>y*S78i6wOL5 zh_1uJ2H5^>xSGHkxn`(}QG>&3(p}iK1uuWvur#ml$k= zD=};pRXhPnShX;v#IY{QPT9-Ir1D9~uBFuX=)cg5UQN*wpf-3_>qvOO+4_i=tmp{| zl7DGdsl6hkSB@7OI!FUTt68OBiBev`Z=Vv@}pisX}+kY3^o zu@DDI!nak)u9IVo6N1u@P7hXkFSI8daP1&geHZ}Kbq(x-uplB@DpSzaVlGY0{w+>^ zxuQcetz7NmH7Ez190+OySCPp8j7XNP?8#p3{H`v0hlGYUh~nS`0UvyZKRvWszo7vp z3TWNXjDxKO?g2BxJ_u8P4YppS#ylDUZ6^^O*)H+RnlpH!nyIRvn{2W~sfOj-XC@KM zAVB;@Y~=3(^6>F;baidxIJd)iH`sIQY%{-J+~(G>ek={558#2_ z5(yvdV~oufZ>=>D(x`BU0m8p1x5!|)l$K%8HSDvEh1~v3Z-DVB1g&)Ta>FH zP4qmQuzG?l(uy#$V-&1rg*-+N4i%XxuVi6YB#*LFs+>vMA##rESGfZ8tE0i%NJ{K$ z`>t#m#HAomn#<-h1d3@Q=GcEP?qWmK-Br>XvHpoEMIul%kWYNBy=FVs_wq05#I+3pkYEk-<} zHwo%BO&52WCC6A$^Bh8J>y`6!$3wX0%B3fY`KcCUTv}sWFG+`n!ogT_$rA8EmVwJ* zGvE;af(*kE5O_JAs=7e2rlUIXn4K>P44BXlsYX8s3p1SHLZTfWUJVcAct_)^#|j@2GZ;K|4PK}wbN0DL~lBWilLKX@_z*UITRj2oAZ&6H67pZUXhD!8VSB-Ps z1*2735LwIK8XqHV84fD#1`g`8ew=q;=$=4kO`n74$MVQnl9Nk5AJ>E5B{4G#QaH#VZp{ZemJCl@QqH{MK(tKJ zBS;28?yzBy-}-_<37E*2@m2buGUpzc3LUkmgH$o_*HD=Rv1iXkq(O?!D_hd6ze)4C z&ioJoB3I59wcWuW&dBvs@L3{*L?&5IAq$*EOgX;DQUEy3yf;Bn!6A4O_eBIIfCR^k z02cYYg=5x@yJMKS4r=`A;xQW2BSxSq5mJ^C2rO}KHeTzH^9J@omS++P$@IwN(z%R* zM%$uD1jT5V78 z(#fW295K@5A}H<~2+r}sG#{_jFVnF4(n7&m9pwPeHm%AMnogv*?pM)*=f&#jUR^-7 zpKvLRcT@HFhHBtvP}ZWFGPdSaXPlZK`C|P_f=ZNp6$NQKb-Zl^DA(B`^NPr6MA5Bj=H5kG%TpWLS`}rG1Va$oOr!NXGt4Q&Mx8j67Bj?y0@1{_ z9L(#?kC{?-An}dN^d9q^y1O8!8gA_Tga$!PT6B@Dc+TuHf#F}VTghncd`z`Vp|LtN zjaoI+;kTTI>ncL3BOTA#K^wl7*Z7?Z$1b;cdzP}DN{liM-xG7^1aGr{liBbCyjEru zi4yvFKnKch8KFld*m8nwCnpWnw-K{r9d6nA(>cgkKtp3yu) z@>q6WUq?w$-akQD0}-60;yQjXCImH|$X=;okGBw(o^;chDAv1bLRbehA+68vWGl_) zo`)%9(IAs|o{`x5Y!W!aVa1bN43LdFg6VBDRx^1L2`IW7|1ZwWR!vu7$8oN4nqb8A#otm4{Y3LIUeWT%a11{G1FGDluw z+P3vNvJwjCnqZ?r5)+C*8RU2D04Bo9fR*GjW>uhMx0}^u8K=FX zqxR8dMaLf0f+X^!ixf>m2^EfU0k$ul050Ur-OYrDK<_|dg#St@Qsy$e+Ztijo&^Ak z1?p z>26c|C!Y)QC03s}^Ax26$iG;bPE3TxL!amyb%FD`2!q_I&!Ays(8Ji{`iNPpRadR! zB=${1?o~dv8l+Am6MKT_gcmnaP_o##smvCBL_$5f#Z__oYzfC@IPlmq6E<2>PZ5@^ z(8(sEp-6CKR^l3n#^NaOLPDmHhD$glsx<9W4PqiuX1eKg&rAth(pK| zDIns4D5g>#b)J7><$pi-PoPFC!^1uO^NjLu~=+Zd<#OEj1g zx5@;LB$+~NS)vSNsB5+>oT4#ep^P{{#XD!gK`*`wstmtc`7qU9ExH&4K%J=qnj6;C z;p3K2F}{sN?ql=0Dkr-X!EKw&SEagDT&fi@dt%m0^~Ea+?ATS%p@ge8!6i4ZY=#C#fA2G$l7w1Rf` z%J!oaq;@GoIV{V~p536iMl_W{dMxJae&r$FCYTx$>M_x<7C|8t!vXCPv)!bOiJRCT zc}{g=NgtZlTk+L0xpebvQIorqzJsoyRWW)A&N}MajN{70G_Bjn!T=!<#A@sKjnlKT zAtmfOLX)mj!DJCjbm?8)`qvaRFI8PcJ*!2Ij*2K3!^tGXtc