diff --git a/uppsrc/CtrlCore/CocoApp.mm b/uppsrc/CtrlCore/CocoApp.mm index 66d36fa59..6b1b855d9 100644 --- a/uppsrc/CtrlCore/CocoApp.mm +++ b/uppsrc/CtrlCore/CocoApp.mm @@ -73,6 +73,9 @@ extern const char *sClipFmtsRTF; id menubar; +double Ctrl::display_scale = 1; +double Ctrl::display_unscale = 1; + void CocoInit(int argc, const char **argv, const char **envptr) { Ctrl::GlobalBackBuffer(); @@ -102,16 +105,13 @@ void CocoInit(int argc, const char **argv, const char **envptr) Font::SetFace(0, ToString((CFStringRef)[sysfont familyName]), Font::TTF); Ctrl::SetUHDEnabled(true); - bool uhd = true; - for (NSScreen *screen in [NSScreen screens]) { - if([screen backingScaleFactor] < 2) { - uhd = false; - break; - } - } - SetUHDMode(uhd); - - Font::SetDefaultFont(StdFont(fceil(DPI([sysfont pointSize])))); + Ctrl::display_scale = 1; + for (NSScreen *screen in [NSScreen screens]) + Ctrl::display_scale = max(Ctrl::display_scale, [screen backingScaleFactor]); + + Ctrl::display_unscale = 1 / Ctrl::display_scale; + + Font::SetDefaultFont(StdFont(fceil(Ctrl::display_scale * [sysfont pointSize]))); GUI_DblClickTime_Write(1000 * NSEvent.doubleClickInterval); @@ -293,7 +293,7 @@ Rect Ctrl::GetWorkArea() const Rect MakeScreenRect(NSScreen *screen, CGRect r) { r.origin.y = [screen frame].size.height - r.origin.y - r.size.height; - return MakeRect(r, DPI(1)); + return MakeRect(r, Upp::Ctrl::SCL(1)); } void Ctrl::GetWorkArea(Array& rc) diff --git a/uppsrc/CtrlCore/CocoClip.mm b/uppsrc/CtrlCore/CocoClip.mm index 2363dd6ec..c0e31ed12 100644 --- a/uppsrc/CtrlCore/CocoClip.mm +++ b/uppsrc/CtrlCore/CocoClip.mm @@ -41,7 +41,7 @@ NSPasteboard *Pasteboard(bool dnd = false) @implementation CocoClipboardOwner -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type { - RLOG(Upp::ToString(type)); + LLOG(Upp::ToString(type)); Upp::GuiLock __; auto render = [&](const Upp::String& fmt) -> Upp::String { @@ -53,7 +53,7 @@ NSPasteboard *Pasteboard(bool dnd = false) NSPasteboard *pasteboard = Upp::Pasteboard(dnd); if(Upp::IsStandardPasteboardType(type)) { - RLOG("Standard type - clearning contents!"); + LLOG("Standard type - clearning contents!"); [pasteboard clearContents]; } diff --git a/uppsrc/CtrlCore/CocoCtrl.h b/uppsrc/CtrlCore/CocoCtrl.h index aa215a04e..38fd07976 100644 --- a/uppsrc/CtrlCore/CocoCtrl.h +++ b/uppsrc/CtrlCore/CocoCtrl.h @@ -6,8 +6,12 @@ private: static Ptr lastActive; static bool always_use_bundled_icon; + + static double display_scale; + static double display_unscale; friend void CocoInit(int argc, const char **argv, const char **envptr); + friend void Coco_PaintCh(void *cgcontext, int type, int value, int state); protected: virtual void MMClose() {} @@ -33,3 +37,6 @@ public: void RegisterCocoaDropFormats(); static Rect GetScreenArea(Point pt); + static double GetDisplayScale() { return display_scale; } + static double GetDisplayUnScale() { return display_unscale; } + static int SCL(int x) { return (int)(display_scale * x); } diff --git a/uppsrc/CtrlCore/CocoDraw.mm b/uppsrc/CtrlCore/CocoDraw.mm index e6c5c20e9..a19d8a62b 100644 --- a/uppsrc/CtrlCore/CocoDraw.mm +++ b/uppsrc/CtrlCore/CocoDraw.mm @@ -12,8 +12,9 @@ void SystemDraw::Init(void *cgContext, void *view) CGContextSetBlendMode(cgHandle, kCGBlendModeNormal); CGContextSetTextPosition(cgHandle, 0, 0); CGContextSetTextDrawingMode(cgHandle, kCGTextFill); - if(IsUHDMode()) - CGContextScaleCTM(cgHandle, 0.5, 0.5); + double sc = Ctrl::GetDisplayUnScale(); + if(sc != 1) + CGContextScaleCTM(cgHandle, sc, sc); } SystemDraw::SystemDraw(void *cgContext, void *nsview) @@ -152,7 +153,7 @@ bool SystemDraw::IsPaintingOp(const Rect& r) const if(cr.IsEmpty()) return false; return true; - return nsview ? [(NSView *)nsview needsToDrawRect:MakeRectCG(1.0 / DPI(1) * cr)] : true; +// return nsview ? [(NSView *)nsview needsToDrawRect:MakeRectCG(1.0 / DPI(1) * cr)] : true; } Rect SystemDraw::GetPaintRect() const diff --git a/uppsrc/CtrlCore/CocoImage.mm b/uppsrc/CtrlCore/CocoImage.mm index ee5cfd5ae..5215b8f4a 100644 --- a/uppsrc/CtrlCore/CocoImage.mm +++ b/uppsrc/CtrlCore/CocoImage.mm @@ -2,7 +2,7 @@ #ifdef GUI_COCOA -#define LLOG(x) +#define LLOG(x) DLOG(x) namespace Upp { @@ -85,9 +85,7 @@ void SystemDraw::SysDrawImageOp(int x, int y, const Image& img, Color color) if(cgimg) { - DLOG("Have cgimg"); Size isz = img.GetSize(); - DDUMP(isz); CGContextSaveGState(cgHandle); Point off = GetOffset(); CGContextTranslateCTM(cgHandle, x + off.x, y + off.y); @@ -103,7 +101,6 @@ void SystemDraw::SysDrawImageOp(int x, int y, const Image& img, Color color) sCleanImageCache(img); ImageSysDataMaker m; - LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); m.img = IsNull(color) ? img : CachedSetColorKeepAlpha(img, color); // TODO: Can setcolor be optimized out? By masks e.g.? ImageSysData& sd = cg_image_cache.Get(m); if(sd.cgimg) { @@ -273,7 +270,6 @@ NSImage *GetNSImage(const Image& img) void Ctrl::SetNSAppImage(const Image& img) { ImageSysDataMaker m; - LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); m.img = img; ImageSysData& sd = cg_image_cache.Get(m); static CGImageRef cgimg; @@ -297,7 +293,6 @@ void Ctrl::SetMouseCursor(const Image& img) return; } ImageSysDataMaker m; - LLOG("SysImage cache pixels " << cache.GetSize() << ", count " << cache.GetCount()); m.img = img; ImageSysData& sd = cg_image_cache.Get(m); static CGImageRef cgimg; @@ -329,7 +324,8 @@ void ImageDraw::Init(int cx, int cy) colorSpace, kCGImageAlphaPremultipliedFirst, NULL, NULL), NULL); CGContextTranslateCTM(cgHandle, 0, cy); - if(IsUHDMode()) { + + if(Ctrl::GetDisplayScale() == 2) { // scale can only be 1 or 2... CGContextScaleCTM(cgHandle, 2, -2); CGContextTranslateCTM(cgHandle, 0, -cy / 2.0); } diff --git a/uppsrc/CtrlCore/CocoMM.h b/uppsrc/CtrlCore/CocoMM.h index 900081660..9f48ae945 100644 --- a/uppsrc/CtrlCore/CocoMM.h +++ b/uppsrc/CtrlCore/CocoMM.h @@ -111,13 +111,6 @@ inline Upp::Rect MakeRect(const CGRect& r, int dpi) { return Upp::RectC(dpi * r.origin.x, dpi * r.origin.y, dpi * r.size.width, dpi * r.size.height); } -inline CGRect CGRectDPI(const Upp::Rect& r) { - if(Upp::IsUHDMode()) - return CGRectMake(0.5 * r.left, 0.5 * r.top, 0.5 * r.GetWidth(), 0.5 * r.GetHeight()); - else - return CGRectMake(r.left, r.top, r.GetWidth(), r.GetHeight()); -} - #endif #endif diff --git a/uppsrc/CtrlCore/CocoProc.mm b/uppsrc/CtrlCore/CocoProc.mm index 84d0ad282..fede219cc 100644 --- a/uppsrc/CtrlCore/CocoProc.mm +++ b/uppsrc/CtrlCore/CocoProc.mm @@ -124,7 +124,7 @@ struct MMImp { } NSPoint np = [view convertPoint:[e locationInWindow] fromView:nil]; Rect r = view->ctrl->GetRect(); - Upp::Point p(DPI(np.x), DPI(np.y)); + Upp::Point p(Upp::Ctrl::SCL(np.x), Upp::Ctrl::SCL(np.y)); coco_mouse_pos = p + r.TopLeft(); if(event == Ctrl::MOUSEMOVE) { @@ -300,7 +300,7 @@ struct MMImp { clip.action = info.draggingSourceOperationMask & NSDragOperationMove ? DND_MOVE : DND_COPY; NSPoint np = [nsview convertPoint:[info draggingLocation] fromView:nil]; - coco_mouse_pos = Upp::Point(DPI(np.x), DPI(np.y)) + ctrl->GetScreenRect().TopLeft(); + coco_mouse_pos = Upp::Point(Upp::Ctrl::SCL(np.x), Upp::Ctrl::SCL(np.y)) + ctrl->GetScreenRect().TopLeft(); ctrl->DnD(coco_mouse_pos, clip); if(paste && clip.IsAccepted() && clip.GetAction() == DND_COPY) Ctrl::local_dnd_copy = true; @@ -358,7 +358,7 @@ struct MMImp { Upp::GuiLock __; if(ctrl) { Upp::SystemDraw w([[NSGraphicsContext currentContext] CGContext], self); - Upp::MMImp::Paint(ctrl, w, MakeRect(r, Upp::DPI(1))); + Upp::MMImp::Paint(ctrl, w, MakeRect(r, Upp::Ctrl::SCL(1))); } } diff --git a/uppsrc/CtrlCore/CocoWin.mm b/uppsrc/CtrlCore/CocoWin.mm index 1323964ba..b87931086 100644 --- a/uppsrc/CtrlCore/CocoWin.mm +++ b/uppsrc/CtrlCore/CocoWin.mm @@ -86,7 +86,7 @@ bool Ctrl::IsWndForeground() const NSRect DesktopRect(const Rect& r) { - double scalei = 1.0 / DPI(1); + double scalei = 1.0 / Upp::Ctrl::SCL(1); return NSMakeRect(scalei * r.left, scalei * (Ctrl::GetScreenArea(r.TopLeft()).GetHeight() - r.top - r.GetHeight()), scalei * r.GetWidth(), scalei * r.GetHeight()); @@ -187,6 +187,11 @@ Vector Ctrl::GetTopCtrls() void WakeUpGuiThread(); +inline CGRect CGRectDPI(const Upp::Rect& r) { + double sc = 1.0 / Upp::Ctrl::SCL(1); + return CGRectMake(sc * r.left, sc * r.top, sc * r.GetWidth(), sc * r.GetHeight()); +} + void Ctrl::WndInvalidateRect(const Rect& r) { GuiLock __; @@ -317,7 +322,7 @@ void TopWindow::SyncCaption() CGSize MMFrameSize(Size sz, dword style) { - double scale = 1.0 / DPI(1); + double scale = 1.0 / Upp::Ctrl::SCL(1); return [NSWindow frameRectForContentRect: (NSRect)CGRectMake(100, 100, scale * sz.cx, scale * sz.cy) styleMask:style].size; } diff --git a/uppsrc/CtrlCore/GtkApp.cpp b/uppsrc/CtrlCore/GtkApp.cpp index 7fd413e06..34dc32d5b 100644 --- a/uppsrc/CtrlCore/GtkApp.cpp +++ b/uppsrc/CtrlCore/GtkApp.cpp @@ -126,8 +126,6 @@ bool InitGtkApp(int argc, char **argv, const char **envptr) EnterGuiMutex(); - Ctrl::SetUHDEnabled(true); - Ctrl::scale = 1; #if GTK_CHECK_VERSION(3, 10, 0) if(Ctrl::IsWayland()) { diff --git a/uppsrc/CtrlCore/GtkCustomBar.cpp b/uppsrc/CtrlCore/GtkCustomBar.cpp index 523c2a89b..8c454bceb 100644 --- a/uppsrc/CtrlCore/GtkCustomBar.cpp +++ b/uppsrc/CtrlCore/GtkCustomBar.cpp @@ -68,12 +68,12 @@ void TopWindow::Init() int Ctrl::GetGtkTitleBarHeight(const TopWindow *tw) { - return max(tw->custom_titlebar_cy, IsUHDMode() ? 60 : 31); + return max(tw->custom_titlebar_cy, DPI(31)); } int Ctrl::GetGtkTitleBarButtonWidth() { - return IsUHDMode() ? 94 : 47; + return DPI(47); } void TopWindow::SyncIcons() diff --git a/uppsrc/CtrlCore/Win32Proc.cpp b/uppsrc/CtrlCore/Win32Proc.cpp index 7d01546db..45606ff57 100644 --- a/uppsrc/CtrlCore/Win32Proc.cpp +++ b/uppsrc/CtrlCore/Win32Proc.cpp @@ -110,12 +110,12 @@ close 232,17,35 int Ctrl::GetWin32TitleBarHeight(const TopWindow *tw) { - return max(tw->custom_titlebar_cy, IsUHDMode() ? 60 : 31); + return max(tw->custom_titlebar_cy, DPI(31)); } int Ctrl::GetWin32TitleBarButtonWidth() { - return IsUHDMode() ? 94 : 47; + return DPI(47); } Rect Ctrl::GetTitleBarRect(const TopWindow *win) // TODO (image, cy) diff --git a/uppsrc/CtrlCore/srcdoc.tpp/Resolution_en-us.tpp b/uppsrc/CtrlCore/srcdoc.tpp/Resolution_en-us.tpp index cabb102a0..f8f88c65e 100644 --- a/uppsrc/CtrlCore/srcdoc.tpp/Resolution_en-us.tpp +++ b/uppsrc/CtrlCore/srcdoc.tpp/Resolution_en-us.tpp @@ -39,42 +39,32 @@ or [^topic`:`/`/CtrlCore`/src`/Zooming`$en`-us^ Zx/Zy/Zsz] functions.&] [s0; This, when used appropriately, works well for host GUI font size between 12`-22. When GUI font gets bigger, there starts to be a problem with Images (e.g. button icons) being too small. -This is typical for UHD display.&] +This is typical for QHD or UHD displays.&] [s0; &] [s0; To resolve this problem, upscaled Images need to be provided in this case. This is not a problem for Images that are created by code (because they can easily incorporate scaling factor into the algorithm), but would be a problem for images from .iml resources.&] [s0; &] -[s0; To this means, iml Image has UHD flag. This info can be adjusted -in Icon designed (right click Image name and choose Image..., -or double`-click Image name)&] +[s0; To this means, iml Image has target resolution attribute. This +info can be adjusted in Icon designed (right click Image name +and choose Image..., or double`-click Image name)&] [s0; &] [s0;= -@@image:924&1482 -(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) +@@image:693&1413 +(A3IB8gIAAAAAAAAAAHic7b19lBzFfe89sc2bDUhICMHNOffkPveenHv/yDmJnTPPQSFvJ8/zOI+Jrx07dmzAzlwclKvlnptkd+3HjvBrVqwTjKXYznp9HWAlIbRoJFto9YJWWhB6A7Pr4W2FdSUWWAlWaPU2CPSChNDzm67pnu6uqu7q7urprpnv59QB7XR1d/V012fqrasOHqxxwGbS4tVXX33F4uWXX37++ed37tz52GOPbdmyZcOGDUNxWZc+jyizNjV+DswhvcfAQf2ZbEIGiZ15KeNT9icJkApICKQF5gcSxaSN45CDLqX4fLJ//346wgaL9TbZ2qMJTnDI+nkHhqH38WuydlTytSMB5gSSAynC55YDXl61YdF+8YtfOD4ZHt6y68mnnht/ceLV1w4fO3ni7XcQEBDaKlDGp+xPEiAVkBAct5AoXrFhbnGKKG6lvPTSS48//jjbZfuOXQcPHcn8ihAQEHIVSAvbd+5iliBdkDQcsTC3uIsoe/bsGR4eZpFf2LM388QjICDkNpAimCtIGqQOt1gcpZBwmFKe2L7zwNR05mlGQEDIeSBRPLFjJxOLu8TiVItYxYfiZJ5UBAQEgwKVQ1hV6BUvrHl2/fr1KKUgICBECiQN1n7rbrzdt28fUwraUhAQEGIEUgcTC8mEjXPbuXMnfbJ9x67M04aAgGBooHoQaYRk8rIFGzGLTmQEBITYgQTCRuGSUp577jn69+bNw5mnCgEBwehAGiGZkFJ27NhB/9j15FOZJwkBAcHoQBohmZBSRkZGanoZfzHzJCEgIBgXqq7w/PiLJBNSyubNm+kfL71yUNdZjlRPHTh05LXDxw6+cdQJ9Cd9SJsy/xIQEBB0hao3kEas5pTN7E1kja8Nfvgjv3vFFVfc8O9+fe71NziB/qQPaVPm3wMCAoKu4LPK9LGT7O1mNndBjAP6yiSsKPLY9t0f+MAH1j/++MSRI3sOHHjx4EEK4wcO0J/0IW2iCM6OKL0gIJgbqqLA5lVgEyxEPSDZ4Hc+/JHLL7907tzZ1113zXXXzZozZyb9+5JLLvnQlVe9cuzY9DvvHDp9msLrb701eeLEwTfffPno0SuvupIizJnz67NmzaX/XnbZFXQQiAUBofmBSSBq/NDgng0papKomEFKWbz4D9eu/fjDD39s1aqP0X/p3/fcM2/GzKt/9foUWWXq1KnX3377QLU6MT398pEjL752cMaMWX/1xUf/4R8OdXdPfO1rh770pa0klgMYJ4OA0PRQjSIWRaVQcGaXimEVqr9QyYQ0MjLyqU2b/vzRR/+c/rtly6fXrPkYFV32Tr1x7N13SSyHz5597a23qJQyefz43qnXZs/+d1/5ymvf/vapb3yj+p3vnCKxUImFDpX5N4yA0BrhP/7mf77xpt+fDHubz+0Tijzvpj/4T7/5X0Ljp22Vg28cJXtQ+YRkMjT0X1nYtOmTK1b8P9fOmfmr1w8dOXfujTNnqAZ08ORJssqrx4796vWDs2Zf/3d/t4+UsnDh9De/Wf3yl1+ePft6OlTm9wIBoTUCKeWSyy7/P+f9XrBYHKtQNIpMu5BYgiM3yyqzqOJDpRTyyfr1n2BWeeihmlVeePXAodOnXjt5kpTy6okTLx0+TDWgF159edas6//+7/dbVjnyzW+++eUvvzJ79g2wCgKCruBYQiaWqkgp7siRHKLXKgcOHZkzZ6akrDLjmZdePvjmm68cPUqllJemp/dOTe1/441nXtp3DVnFW1aZNWtupHaVP/m//u9CoZD5vUNAyG3wuUKW/WX+ydAqR6qn5s6d/cgjH9+y5dMkExa2bPnUmtX/L9nm6Rf3UuFk36FDFPa+/vqeyclfvfba0y+Oz5p1w1e+fPDb3377G9848e1vn/qHf6i1q0TqA4JVEBBCg9sYByyxuLdWrRlR0lBKQqs8tn33JZdccs89v7d69c0rVnz0oYc+Sv9dvfpj//RPN14946qnxvf876mpPQcOkE/GX331uYmJF1559anxF666euZtt2388pdf+9u/3feVr7z2xS9upoPQodTPC6sgIKiEgKpQwKasrEJFiw9/5Hc/8IH3X3XVB6+55sq5c6++7rqrKMyZc9UNN8z4wPsLl11+xa7nnnvhlVeefemlZ/fvf2bfvrG9e+m/O5999vLLL3//+94/Z871s2bNufbauTNnXn3VVVfQoeiAiiUWWAUBQSXICiSyYkxynySxyoFDR6644vLR0Z+cPz9y/Ph6Jxw7NnT6zJbdu380Y8bVI08+RRoZffFFCk/v2fPUCy+M7tkz8uSTtGn37h+ePfvo8eOPnDhBYR0dhA5FB1RsXYFVEBCEQZjHfWLhlaI9xLPKa4eP/fqvX3/u3NaLF5+8eHHHxYs7rf+y8OTp049ef8OcTdueII3sfu653c8+u+uZZ3b88pe7n3t24+PbaBNF8O74JB2KDqg4aoW3yrf+8W76ZNOWx3Y8OfqpT3+mYEH/eOaF2myZEwembv/rv5k58xr68D/8h/+DIvsOSBHow9/58EfYjhST4tOHwmh0BOf4dDp2av6Yq9Y8wtIpOykCgt4QkM3dYklbKbGtcvCNo9dfP7da3UBauHDh8ffeqwf6N1ni2LFHrps7e92WrbufeeaJ0VEK255++vGnnto+NrpueMt1c6+lCBSN7WjtspMORQdU7F+WWeX7//IjEgJtJSewvE//JbGQLugf9KGTzX15nH1O0UgUzr70pzsOKYVph7Y60eh0TGK+A9JWZqeAAyIg6A3BOd0RS9pKSWKVG26Y++abG0kIZIaLF7exYP17x/Hj68gqP9u4advTv9i6axeFLTt3Du/YMbJ795qNm2gTRaBobEfrvzvpUHTAhFahXHzfwHL2CUmARaMcTdnZKXhQeYbFdO/e+eX/jz7nT0HlDd8npAh3NGYPn1VIbswh7tIOk4+TPASEJIFlXt+febMKWxha/aLCrXLd7IfXrtuyc8embds2Pf74xsce2zAy8ui2bYNrH6FNKVmFcq77Q3ICy/KsHuQEVnKgykvAKXz1GorMBMXHZAUYt1VIWRR8FShKA4orCLpC1Gze/BqQg/pFhdaA5lw3+8Hy6o0jI+uGhyk8snnz2kcfXb9ly4OryrQppRqQrxrCiiW8CtjuvsIJ5XoqSNARaKvTwOIckBU/hG0jvlOzk/r8xgI7ZuYPJILpIYlS6N8HmtJaG8Mqwa21Z85snjt39gMrVj7y6KNr1q+nsHpoaPW6dT/fuOH+FQ/NnXstRbh4cXcarbXuD1kGp8jC3d2RqQZUsCGlUAQWx3GF8PjuTT6rBJD5M4lgekiilIAPM7eKu2f5xPH1VTscPzZ09vTwk7v/9aqrrrxv+YNkksGf/awW1qxZuXr1wz//+X3Ll9OmJ5/81zNnh4+dGDpeXX/8xPrkPctJrML2JZm460Q+VzDtqFuFCkhMTXzI/JlEMCiwTOr7M6FSmiCWeFZxj4Kbcc2Vs+bOuOa6q2dddzX999obZr7/A++79JJLf7p04KHVq5c//HAtDA4uW7lyxapVPx0YuOTSSz/w/l+7fvbMOddcNfeaq6+ZcSUdJOEouCRWYfUdX9uLzxVOHxOfGCYcn1V8jboICDFCwqwd6o30xBLPKiywEft/eu/Hbv35bZ8d/NxfPvy5vxz83C0/u/VPv/enH/zQB/+1/ydkkvuXL6+FZcvuW7p04MEHf/STn3zogx/s/YM/XPlfPzFw8589/IlPfvcP/yj5iP0kVhFWTHw1oPsGlgsPNXFgig2DcWLSJ6yPiR/ugoAQKaSqlFTFksQqVLS4du61fzX0V3c8dseXhm//0vCX6L93jPz1bY/cdtWsqxb/yw/JJD+57z4W+v/t3356//33/uAH11x55apPfnLj5z439NnPbvr859d8+tNzZ1+b8O3C5GUVdzmEtc36mmdZz5G7EOIeXOeOyUovn/r0Z9xiYSPohHUoBAQ+NEEp6YkliVUOHDpC1Zhby7eST0obSqWNJfrv7Ztv/9zDn7ty5pX3fO/eH//0pz/q76+FH//4h319fT/5yT/de+/MD31o2cc/vu6zn/3ZX/zF0F/+5YpPfOLamdcknAkhiVWcDmjW7iEb27bjyVFndC6LSX+SkXw1oBOu8XJsSJ4TWdYyg4Cgq5DAwu/d9AeRLHGgMWvT7+u1ytq1a6N+Fa8dPjbbKqvM3za/VlDZQmWVL1G55QuPfOHKa65c1PvdH/64b/EPfrDkBz9Y/C//8v0lS37wox/1fPe7VFYp//mfUymFlPLo5z+/msoqs2ZHmmFSex8QicXpTQ4Yh+9+HYANwmclEGGns+8VANoRSkEQBr1KofCffvO/kFgiFTwO1GaY/P3/+Jv/WUsCSCaxrUIFjEsvv/TjP/g4ieXW1bfetuY2+i/9+2OLP3bFh674zj/23Lt48T9/73u1cM89//TP/3zPvfd+u6fnQ1dc8c9//MckEyql0H+/9yd/cvmll5o7GzYrq2DQLELsoN0qmYckVmErd5BYqMRyzXWzKFCFiP59ySWXXHbpZYvuvvue732PZOIEssrd3/3upZddRhHmzr6WKj5USiGlGL1yByuQBI/URUCQhcwNkDernJCvMva+972v4847/7Gn5xvfrEP/oD/pQ9rUMquMCd8qQmjPwDKUSpyWDwmtIgsf/sjvXnbZZXPmXDd79rVOoD/pQ0NXROXfQKQ/+Q4ghLYNvpwVGqGFQ0pWab3V2503oFmfjtMSiwFvCCdEOUslTquGlKzSeoFKJs5MKU63jnuqBIR2DsLMFRqhVQOsgoAQKfiMkXkWzmFgVllrkfn9QkDIf8g8z+Y/rHWR+f1CQMh5yDzDGhFgFQQE9ZB5hjUiwCoICMLAMojvTwRYBQEhdsg8b5obYBUEBD5knjGNDrAKAgIfMs+YRgdYBaHNA8sIvj8RYBUEhNgh8zzYegFWQWjnkHkGbMkAqyC0c8g8A7ZkgFUQ2iSwB973J0LaVqkCAEBiYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYJVUGT9w/JYVk4Wv/goBwaxAzy09vfEee1glVejW/NkDB46fvqArnLBC9Uw9vMnC2Qsn7fAWC+9ceNsOpyicq4XTtfAehTMUztfCWTu8Q+HdWjhnh/MULtTCu1a48F4tMC4CEezLYV/Uu/b3xr5D51tlX/I7rm+e3Ygz1n2xQu021e6XdeOcm0g3lN1ZdpfpjrNb7zwJ7MHQ+KTRc3vrQ5PxHntYJVXI+XSDsn7eAYjMsVPv0tMb77FnPvm5hd4MBaqwCjCWJFYhmcAq6eG2SuY1ZQrZPqjAIGCV3AKrAEOBVXJL7b6cgVWAeZw4DavkFLovJ8/CKsA83jwDq+QUui9vvwOrAPOg5xZWySd0X06fqw/wyFwpsApQ5/Q5WCWn0H05ex5WAeZx9jysklPovpx/F1YB5nHu3feSWMVBb4YCVcsqF96DVYB50HObqVUqvcWCRakcHEW+vVVxZ+R5j50Nu5NnlyyBVUBeyIdV5N6AVWpWObl/+mwj1D92fTJ9UrNVlry+ZPzs/sdehlVADHJglWKxKPcKrMLVgFjRZeNgiiUTdgpYBcQjD1bp7S1JyyuwCqwCTCMXVqlUy5ZXav8SRoFVYBVgDjmxSlXmFbFVKuXeUtFpkykUS71l737W0ayDVcqlYiNaxTmA86n7Y8FZGicplsrieGmQzCovz3/s5H5n/+mzGwdfFsQZP+uOs2SwcXAfpJc0n0HQauTGKhKvCKxit/FSNq9RFPUj1a1SrkVl8Ww9UDRmlPredWVwYnG0ExIvJRJY5fWN02y/sxvHT24cr1vCXfCoxWGfTp+sxZlulEzmDR61PnFtHT+5ZBBWARHIkVVsXXhyrtAqJV+pwXecqm0o72euHifBxyJxeR3CnyVF4lrl5SWWELwOYR82ep/5Ck6t6LLkZd8pUAMC8ciTVUS5Wa1dhYslrk/VXSMsDvF78yeVfZ4CMa0yeLK2w/jrwZ/PH78oPQKsAhKTL6vwWVxqlUq53NtrVYCKrsqNjTj/S0ob/siNRhk/8i26iWcVSxfCEXFWlWf66DzXEaiCM18yygVWAUnIm1X8XhHVgMqeNlSr2URY2pA0/Ya1CLtqSkLya5V69UeKbRXWVGt/KGjLhVVAEvJnFa9XOKs4laRyUOuLBquwVloB/v6mVEhilf3j9VZWf3js9Xnu+EteZ+20NbzlFlgFJCGPVnF7xe8LcY2Iq/AksUoT6zlydNeAAoJdbmmUZGAVkIh8WqXhlV5RjvdbRdLeGtcqTe3skRDPKqLOHSWxWIWck/NhFaCDvFql0TlcEOR4T5XIaWbRZRXn3NywNzqXbMicZui+vCeZCSGwZ7k+EIVrKqECifMG4svzB71Vofpefqu4+5KyeTqBgbyXi5kQpIWChlf4dhXP4DauRJPUKu4mW99gu+aNVzl/IYZVflVYcrTxUrPVnLLfHhTnWKXe/FIf5FZvWvEc0D6IFefsRoytBcrQc5tnqzS84svx3HB7ra21jdN4Xguo6aUpDbUWdF/ekcwFFz5in81j0LjPpJejnsbYQUc1NfbXmmq5StNgY8z/RoytBcq8cx5zweUUui9nMBs2MJCEs2HDKulB9+UUVu4ABpJw5Y4kVinEIl5STYTuy1tYZQwYyMmziVYZQ1klPQpYERWYSTXZiqiwSnoUsHo7MJOEq7cTP7PQm6FAFVYBxpLEKmvWrIFV0sNtFQAMAlbJLbAKMBRYJbfcsmLyzx44QDdIVzhuhROn66FK4cy7b9rhJIWztfDW2QsU3qbwTi2cssPpc/Vwxg5nz9fDO+ffq4V33ztnh/MXauFdK1yg8F4tOC8gAB/Wd2N9S/aXxr7A8/b3Sd8t+5Kd7/zM+fpdcO6Lc6fYjXvbuo8s1O6s617Tfa9aDwB7Eo6frj0YGp+0mx84cOtDk/Eee1glVcYPHKdbk3lzCgJC1EDPLT298R57WAUAoBdYBQCgF1gFAKAXxyr0j6zTAgBoBUgmrKACqwAAtACrAAD0AqsAAPQCqwAA9AKrAAD0AqsAAPQCqwAA9AKrpMrBgwdXrVr1LcBBXwt9OVnfnxq4RzJi3yNYJVXovqxYseJMGGct3rE5Z3He5l2bCxbvZfHicO1tXBuWGCd5LLVO4tm1hF4yfS3lsn+VlUzAPdJ+j2CVVCHh091p8tOVkOPHj7/wwgvPpwYdnE5x+vRp+nKyvj81cI+03yNYJVVMfGLpiTp16tSF1KCD0ylglSTk/B7BKqli4hNLP1XpPa4MOgWskoSc3yNYJVXwxGp/YrWDe6T9HsEqqUI35ezZs1k/g9FozhNLGTk/VsE90nuPmFXWWGi/X4BuyjvvvJP1MxiNqE/stm3bYjyxlJHzYxXcI733aI0L7fcL0E05d+5c1s9gNNSf2MOHD996660f/ehH6b/070hPLGXk/FgF90jvPYJVUoVuyvnz57N+BqOh+MQ6jysj0kNLp6CMnB+r4B7pvUewSqrQTXn33XezfgajofLEun8BqXQd9deQTkEZOT9WwT3Se49glVShm0I3KOtnMBqhTyxfqI5azKZTUEbOj1Vwj/TeI1glVeimGLcsV/ATK3s4Iz20dAr6WvJjFdwjvfdIn1Uq5d5SsVhoUCyWeiuuCOVSbWvR81mrQzeFfyT2b5w/b57zNc2bN3/j/tDHaON8K/L8jSk8oj6Cn1gqS8seS+ehDe1xoFPQifJjlcDvY/+SeYV5S4JukfeG0h0NjK2DnN8jPVap9BYbLrGo/1VyvZsEq7AH0H725hPOwxisi9qDnROrsIdW9ktHn6t0Yio9sfWHquR7va1cEn2q8x65vveNS+Zb37zcE86tYTe0dkvnpX+b8nKPJDhKWb16ddzbwu5zsVT2+qJWeOnNxVup2cE9sTWpzFviLpzsDy+GOCbKh1WSo/jEMq94foe0O0VmFUcohSCrsDvjvaHNID/3SAjJJKlVrBvdXkUQZXirbBQ8gOzZlBnD2jp/ifWb2F5WcX6w7IeLaUarU4RWcRUoSS5Sq2yUb0qZPN0jAcmtIvhBATZhdXbG/gBj2E9uUBy95OuJdRVOUnGKzCrz5tcLIFJ1NO+O8OTrHnHoKqsoaMVXeGV/ivEezGoHbmzjqlo5Rs0q8rJK45Fu3jPcnLfsLyo/sfWycDkdp4TeI5lVspRK7u6RDw1Wcdpq6c4HZXefVSq9JQ52HM+TU7aFUnTFMKZopGQV6fPpfqCb9xAfP358+/btW1ODDk6nuBjhiXV+gFJwSmyrsIrpRtYD5LS/KHXoJSd/98iDDqtUXXmfMr/MLaENbXyEuq98RReDqlwKVql3InC+YJ87j3NTrdKEecYuRnhinS7GHFmlfj/m1/uIInTo6SB/98iDJqvUqDTUwncIVcOsIupHlO2RQkdASoSOhbA7EfwPLVcpat8aUL05pZTWTY9nlUaD7pJoHXpayNs98qHRKhYVl1t8xYkgFwjLH/LOJWO6nYKeWLvkLPsd9H6O1tq0fkwSWYW/IbLfCa3k6x5x6LYKw1GLdxSc7KEQt/e6htYJMdkqjlEE1XDxY9meVvE+M5JxcSndozqBVhHJI3icgB7ydI8EpGOVGpwqotZn7EYVvlGXEdw2nA/EI/aX1Msoooa9xlhNOen+EObnieV/bdLwSqJ2FVm/HaySjlU4WwjtEfSYGFPPkcM/sfXajbSrYP+S2phvAbZRiPnZWqVJo8HFJdh6f1BzRuzXCOwDkpZVsq4BZTtiP22rBJdVgn96TOrskeB/YuPXY/JSA8r8zTXtxLSKrEzSjKJK3u+RjrG1pVK54s/5Ff6XhrNKuDWcHyZfFDp6yQjZ+N6yT6CGvFgl87fstRMyE4K86OGMCWhsk/bpaSbn90jXiH2LoveFZZ8wxGNri6J2k8Z+jaPXIxaNGgfnmxHI7o4U13HmBTkjL1YRPpwxZgSi/wY/sYEtSzVSukd+gio0TiPYvHmu8SpNuEk5uUcyNNSA2MQq7qlVikXRWDiPVYLG6/urSb6JW4QHzym+2QsbLx8LMcQqF3TMXpirueCCZpgMaSbxvNvcrKG1eb9HKbarAMy0LH9iMRt2EnJ+j2CVVMGqELInFit3JCHn9yihVUIrv3qrwMaBFaxkTyxWGUtCzu8RyiqpgtU2ZU8sVkRNQs7vEaySKlgZXPbEYvX2JOT8HsEqqYInVvsTqx3cI+33CFZJFROf2Oa8ZQ+rJCHn9whWSRUTn9jmzDMGqyQh5/cIVkmVVatWrVix4nQYZyzOWrxjcc7mvMW7Fux35D2LJj/GdEZ2dpaS8zYskSzNLP3sWkIvmb6WcjkX827hHmm/R8wqqy203y9w8OBBui/fAhz0tdCXk/X9qYF7JCP2PVrtQvv9AgC0IbAKAEAvsAoAQC+wCgBAL7AKAEAvsAoAQC+wCgBAL7AKAEAv5XIZVgEAaARWAQDoBVYBAOgFVgEA6AVWAQDoBVYBAOgFVgEA6MWxSk6m0AEAmA6sAgDQC6wCANALrAIA0AusAgDQC6wCANALrAIA0AusAgDQixarTExMDAwMdHR03B6Rrq6uvr4+2l3jFQEAsiW5VYaHh8knd955Z39//6qI0C60I+1OB9F7XQCArEhoFSpmkBO+/vWvT05OvhUL2pF2p4OgxAJAa5DQKlR/ocJGbKU4YqGDUB1K+9UBAJpPQqt0dXVRLSaJUhh0ECquaL86AEDzSWiV22+/fdWqVcmtQgehQ2m/unSo9BYLhUIJfWYACMm5VcqlggQnU5dLtTxe7K0k+RoiAasAEIQZVinywCoA5BRmFfov5esYuzfFKs00hgqwCgBBUHaGVSICqwAQBKwSHVgFgCDMtwprerHzOMvxXJb3RqpHLfdaTTKFetNNqSw6jS9WLRKsAkAQrWYV2yuefQROKduqKJZq2H/4zlR3lOUcJ1qR/R9WAUBM61nF/sTZiy9a1GXhPS5vo3oHlLcM44gGVgFAjAlWEeDK/KLKjeszQXVFtAf/uaQuFbABAFCDWYW9QRxj96zGq5SCreIUcsqCJhB5+cezRR4N7SoABOGelyDG7rmsAbk/5zc5VRgJ9bMFqANWASCI1rWKrAHEblQpSegtwyoAJKFVrVLP+SVZo0voGBhBV1LwGQEAjNa0iuszfnOAL/hjCKI5XUPB+wPQrrSiVUL7coRdxrWo5ZKrGVjc/yxrrgEA1ElolY6ODl2zNnV2dvLHj2EVvowh8EqjydYe3yYaB+ePVbeMqGMJAGCT0CoDAwO6Zpjs6+vjjx/ZKuJ6i6jSYg3Fd3UH1bqr+TH7ngH79VH9aK0FIIiEVtE1G/aCBQswGzYArUFCqxCbN29OuHIHKQUrdwDQMiS3StUqsVD9pbOzM+oqY+QTqkMJSykFBRJcNwAgLbRYBQAAHGAVAIBeYBUAgF5gFQCAXmAVAIBeHKU8/PDDWacFANAKkExgFQCARtK2ysTExMDAQEdHR9ShLF1dXX19fRhwC4BxpGqV4eHhhMNuaXcMuwXALNKziq5XhOggKLEAYBDpWYXqL7peZ6Y6lN60AQDSIz2rdHV16Zp6hYoretMGAEiP9KyS9jRxuaT5U69gEl2QO8y1imwBMlcWY8ueNnPxd1gFAPOtwi1AVpuwzYkCqwDQdAy3SjONoQKsAgCsohdYBQBYRS+wCgCtbZXQhYH4SPWo7pn17an1OXyxapEiWaVSKbtn+fesSd84gTcClxDpgrDh+zYSWynXL8Q5jj9pvdAWUKaNrCJctVCQKe0cVl+KWbRQUNW1VlDRFa3I/q9iFecs7tMUuTXOnO2u6LJlGvm0Be9rW8XVm8a22x+4lkrKW6EQ5Jm2sgq3XBBftBAvWMjZSLj6oWy9eB5nBXnv/mWnOCFeXbEiWNdIdo0K+9Yvi6whWFvNv2RjBVYBqhhuFQGu7BCyBLOguiKrTyjVpQI2BB2NJ+AwXJYXl8eU9rUt6IuLlhqQEOOtwo9WCbaKU8gRrWsqL/94tsijKbWrhGbboGKYXw3CBWHV9pUsYy8uSAGgjOFWiVoDcn/Ob2q0RwQWgwLUoWKV0DjBEXzX7bvGKPtKrOJqvJW3VAMgpy2tImsAcX6lJdSbPVrLKrK47l4gqAVEoQ2tUs9MJcFGtTEw0t94tTaJgN0VIvhVILSK2r5K1TW73IJOIKBM21nF9Rm/OTTDu48hiCbooxEQ2qbb1Nba8GbZ5g/tA2bTZlYJ7csRd8taP9muZmBx/7OsuUaWMH/qK+Ve+wNZBN5a0p7l8H3FtqiUy76LR6cQiEZ6Vuno6NA1a1NnZyd//BhW4csYAq+4hpC5RoH5z+WPVbeMqGNJCH8W3ynEEfhLDhkFF7SvxCp1Y3oH0MEpQJ30rDIwMKBrhsm+vj7++JGtIq63iCotvtHu4qHy3gH79b6SKJUFfsC+b6iZYEQ/nwpJQUJlX0livXsK0gVAMPmfDXvBggWYDRsAg0h15Y7NmzcnXLmDlIKVOwAwiyasMkb1l87OzqirjJFPqA7VzFJKQY2mpQcAQ8GKqAAAvcAqAAC9wCoAAL3AKgAAvTCrPGyRdVoAAK3Awy6yTgsAoBWAVQAAeoFVAAB6SdsqExMTAwMDHR0dUUfBdXV19fX1Yaw+AMaRqlWGh4cTjtin3TFiHwCzSM8qut4upIOgxAKAQaRnFaq/6JoJgepQetMGAEiP9KzS1dWla9YmKq7oTRsAID3Ss0raM0zmEkzxCoDBVpGtXejK1Gx6+GbODq/FKs1PNgA6Md4q3NqFxSKsAkCWGG6VvGU91IAAgFX0AqsAAKvoBVYBoLWtErqmGB+pHtW9KId0BXNfrFokdav4V9bobezjS5G8WZpf50wl2QCkjKOUwcFBvUfOnVWEC54KnFK2c6ZnhS7/mZyVvDyrhLH/q65dWHAtYyZf5LTSy68jXyzETDYA6UMyaR+rcCuN8UUL8VqnnI2EC6c6olFaZ9lfzKjIrKJyXYrJBqAZGG6V4EpByOrtguqKLEMr1aUU1mUPPInadtE51JINQHMw3ir8aJVgqziFHNGSyPLyj2eLPJpSu4pdsJA1ewS5QFj+UEw2AM3BcKtErQG5P+c3NVY9DywGBahDsbW2Um4sqM7LRW4V8VLRiskGoEm0pVVkDSBOIUJCfQH05FZhcV29QB61RK3PKCYbgCbRhlap5/ySYKNahSGgFTRGQ4ZdbmkcT3iQoCYb1HNArmBWGbTQe+ScWsX1mawrJTR/iisizobIzaO+Eo4g2cHNwOjsAbli0IXeI+fSKqF9OcIu46pVonA1A4s7cmXNNT4q5bLv6MJhb66DhFtDLdkANIX0rNLR0aFr1qbOzk7++DGswpcxBF5ptH0WXYPU/Ofyx6pbRtSx5MdWUtE9oM29j3hsbVHUbsKJLiTZADSB9KwyMDCga4bJvr4+/viRrSKut4gqLdbId1e/Sq27mm/x9IyPr3flqPUsl/1Hdw2B8yc7aLx+rGQDkDbpWUXXbNgLFizAbNgAGER6ViE2b96ccOUOUgpW7gDALFK1StUqsVD9pbOzM+oqY+QTqkPlsJQSVCHxknVKAciGtK0CAGg3YBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXhylrFy5Muu0AABaAVgFAKAXWAUAoBdYBQCgF1gFAKAXWAUAoBdYBQCgF1gFAKAXWAUAoBeSCawCANAIrAIA0AusAgDQC6wCANALrNIeiBayByAdMrRKY2VgKVgjWBdpWKVcCltUHrQnmVqlVHTjrA7coJTTJ5ataJzTxAlJZhXx9cIqQEx+akBKC5/nA5ZUo7JTIqsYeL0gS2CVGBiYy2AV0DxglRgYmMtgFdA8zLEKq9s3GnJLveWKeP8Kq/B7Dmbt7exbLNG+shN6otbjek8SoU05OFWB52rE8Vx3sdefYv83w301VYFVpF+3J2bg9cpMFS093jsjiAqMwxCr2E83PaE17GfWk0Hr+7Pn1bPZyRv27uwBLglOaGf9giumyxu1LOB8zKKUAjNCQKrCzlWP44nClRka2b6emsafnguLa5Wg6xVZJWJ6elkpyPMFmFFeBQFoscq3lAk4SIhVSiVR0cSVw+ofUMbzlR3qGdO7v/P082Ly7S8o/keoEUhTpXQu0Ykq5Yrzp/DSaoUb7tJiWiXwevmYUdNjFb14R4q+WfVnDGiBuwMRSG6V5GlgRG1X8ce3PeE7Qv1jQTmd2yAr0UtyZASr8EdVO1dwa4j00gQpbIZVoqeHj8s+RxNOxiTM1AmtokspVSWrVMrl3l6rIlDkS8tBj77wKfWfUB7TvyV6WUU9VZ4t9m+3qLkl6CD8eZthlUTpUTkIaCJJsnZyq8Q7L09wDajsadS0KuK+H0bxox9wVN+msKG+Oq2ifi7XhfvbcoM17MudTbBKsvRI4oEMaW2rONXtMlcH91vFv39kq9hNkhyuk0e2ijhVSueyort6gRpqgVVAurS0VcRbxLV6cf6V1zUUC/DKB1VLe7y8Y5db6vsFpkJYvWuGVeKmx7MBVskFLW0V4RPINQ0G5V/RY8q1F6q7IrlV4o4r8xxOQ2ut7PtqUmstrJJrWtoq/NPaaG0ItYqkC9cZPxLeMWrF9rznGJB9lK9K5VyVctm32ZcZ6wfxd1qr9CwLvxfx1yK7XmnPcqz0uDfAKrmgpa3iH8XGHvFepXYVz+7u/Yu9ZcEO/qhOe4ZoXEk9UuCb1SpXJT2XnfG9A8pEGd51kIIowcJc7Cik/rWw4kSvIKb4ekNGwUVOT2MDrJILWtsqVW8nEHus1Vprnd25AfvSyol3vLl4wLlrYKygvBHhqoLP5R2szxLOnaviH9IvSq84F4u+Fkl+F1yvJGaC9FRhlTzRAlZpOpgeDYAgElplpUUzz5s9cRtMAWgTkljFoZnnzZwILa4AtCWwipxyL1exr4g7KwAADWAVOY3XYz09Lf538wEAHmCVAFhPS3i3DgDABawCANALrAIA0AusAgDQC6wCANALrAIA0AusAgDQC6wCANALrAIA0AusAgDQC6wCANALrAIA0AusAgDQC6wCANBLC1hlcurIdx/Ze/O9T/37zsdZ+K2FT9Cf9OHzE2/oOgsAQBGjrbJ+dPKmnt2Fv3o0INzS90u4BYBmYq5V/vt9zwb7xB2+unI84ekAAIqYaBWq8oQWUfhw03d2046xTwoAUMREq/zW156IqhRHLLFPCgBQxDirUF0mnlJYWPDAc/HOCwBQxCyrPD/xhtQYX9hQuG3dr926tnDr2l+7dV3hixtkMdePTqqfkVsEkJ9c3xOlvo5f2JHE0fh1FrkYgSsaSmj5Vf2sC8QU5fnBLKuIm1O+uLEmk1vWXPPXQ7/9tZE/7tlB/yh8fk3h1p8XvrgpQT3ItfRvfb1i0oLv4bU94FqIWZSDFaLVFy+rTbdtrzfKHSdO9smfU9gk41pTBK/kCoOsIi6okDdu+TlpZMnG/SdOnbto88ATr/7G3z5Km4RiUSmusHU7ioFz6ttxGlEq/Edq0bh8wWeUOFnHclWunJLO2o+xCnEgJQyyyldXviCwyi0/+43/+egrR05d5Hhl+u2ZVGihYoygo/mFkJOp/MKLswe3tqFSNIExah8FaUeFPP6Gp7SibP7KZO2LQVYRVH++sIGs8sATk7xSGIs37acIfBtLWCVI6ZdPmju8q7urRfMphPsolh7y+QOe1jrVOSyWtSsGWWXG32zxW+W2dVT3kSmFOPH2uZl/vY6i+Xb893/3eNCZ1HKj9MfRm23UovHWcGeSeEUO4WU0kuNqG3Y1DSu0GFvNIgVXJL6S6G3kLlI9spEgH77WpXqTkr2jv1G7cU1OQp0LRGklLxhkFUH159a1f/SP2wOsQvz210YKt/qtQiHoTE4m9mQx3xMeoB73460YjROHSypxc4vQRfWDla3Du9uOazHZ5dYXgBVlekcMvlVi+fafxnEa7qwpwzlyvQXc7STn25anwP467XN4zp3H+l5bYrZVbln7O1/dGmyVP/rOtlqbbRSr1J/c+mNd9Kyx7HuEA3RR36QYzd0HRLAfbGtT7F9gcY3AyfLutuNGGULwsa8EVfIVH/wVGlEFp1Ku+A/LJax+NmHHWOOz+gd0P0R9/Lms8bUhBllFUAO65Wcz/9sad9cPz8z/tpq3yoy/GQ44kfM76P2VrH/saV/VaJWqlWX9dRK3Gtybg/umGofnTizqo3IuWVwwUWpf8tZDgvYRW0W2m+9zW4Di46Owkg8MsspN3+Faa28bKvzF8sUbfiVTygOPv1T4zPLCF4Yitdb69OHf4G7r0GsVDn81qFZ1cUoyIdlH8sMtPqmk/CBLYYXS0MsG8HCFOLvMIRsNKD6XvETm3RLc1IuWlXxgkFUWPPCcoBL0mRUzv7DilcNv8UqhD3/jv5cpgmjc/jMBJxL/nDe2lFxNj9raVQS4nMLVZsI7PIKsIql+SD52H8Pd0lSriBTtiqK7yOVqBhbIRVhFErXjulC0CrqB8oFBVlk/OikeqP+pB35j/uDAyP92df2cXbzuhWtuW1b49APCofsbxg4EnEguAt4q8s6d+v6K0SSpKPL1IPfm0HKOZqs4bR9uVYhP5O4F8qolwCp2Cy6Hc8LgLw1llXxgkFWqwkpQTSzrC58eKHzyf828ZekffX39b//9mpm31P606j4CpYR0K1cDHk6+10b0gHt3V4zmw+sRgULCf5YD2lViWkWcpQMrcna5hW9wVa0BBaZIdHFoV8kcs6wiLq7U21jWFj47WPiLZYXPPFj7x22PyGL+eHh/2HkkGcX3cVAs/6BY1ezN8CsjTlklqA8oplWEVxLapCsu8AgbhsO1EmgV9AHlBLOsQtx8z9NSsSiEW370S5WzOFkl8A0feSx+PFt4NF986ZA4C5Vf5aDxKgnKKuImFKeDvFz2taT4L1WiIaeN3J+GcqnkL+hILhwVoJxgnFUmp45SFSaeUmhH5engnPZD1iApGu0li1XkHnrFaIygmot7NEto9gkZW8tHVW1X8YyfK/V6ItktJPZr3oLvzTOSzj2st/GOuHeMHVd9CrAxpJIDjLNK1Xp5OYZYaJeI02J7Ro9LJ05Ri6UaLaAQIm//lF6AIAsm7AMSjOj3F2j8M9KU3EPgnFQ4Ubwtud63AWp796qOaIZUcoOJVqlaYqG6jLpS2nTS2nZqaED1Jz8YahVG3+Z9oYUWirBi+0TycxlKu+S1dvJn/jHaKowNYweo3PJbX9vuk8mC+54JHpfSHjSqVIVkZH0hQaBLOVe0gFVACC2f5Vr+Ak0DVgEA6AVWAQDoBVYBAOgFVgEA6AVWAQDopQWsMjY2Njg4uGjRoq6urtstOjo66M+BgYE9e/boOgsAQBGjrUI+IXswk9x99939/f2rLJYuXUp/ss8pAkVLfi4AgCKGWmVqaur+++8naXR3d69fv356evotjsnJyZGREYpA0ajcQrskOSMAQBETrTIxMXHXXXeRK5YtWyb0iY9yuUyRFy5cSDvGPikAQBHjrEJFDlLKnXfeuXv37lCfOFBk2oXEghILAGljnFVYxSeSUhyxsKpQvPMCABQxyypjY2NkBqrRRFUKY+nSpbT76OhoxNPWXoiVv/frmVAkaIKV0GgKq5FGeTU3bNZGwXbxXPe1qfR7wxcgAoBhllUWLVrU3d0dTynE9PQ07d7T06N+RmeyJUnmtD3gWVyUj6sQrbF2YaUim+0t2mt0CaxSdOHWi1B1AHgxyCrj4+NU0hgZGYltFWJoaEi1uOJbSFy6ApZ3KlvRYkIq0Thh8AaJ+mpubKvwu1QaJS3ZxJgA2BhklYGBARLC5ORkEqtQcYUOMjg4GHKyxqqoJZKLOHOKsyA32bNSNIExah8FaScUjVbxpBmTDoBgDLIKVX/uvvvuJEph0EHCK0GUh50pUyXzqUlzoHBV9pBoPoVwH8WZQkS7VaqiYhcAfgyySkdHx9KlS5Nbpb+/v6urK8KJJVaRTt7ozZpq0XhruCd3jjctURpWgVZAOAZZhWouq1atSm4VOggdKsKJg6al17bOsl8c/oXbY+TjVKyisOY8aHeSW+Whhx5qznlzZpWA3OXepBjN3QfkrPhjbYo/n3XYkuj1hqOIVsG80yCM2FYhmTTZKlRtocpLcqtQNYoqUxFO3ByrVGsZ1ul0svtwvWu4NzYXFYaPiLqJi1yfMawCdGOQVXp6eprXWuumaVbh8FeD6N+Nkkxozk6nBgSrgDAMssrg4CDVXFReJwxgcnIy8rj9prSrCHA5hVuXT2WhvhTbVdBaCwIwyCqjo6MkhKGhoSRW2bp1Kx1kfHw8wokjLSJa9YlEMRqPt+7DOcT6KLjEkGIfEIoqIACDrFK1KkHd3d1JiitRR+zXCOpZFuUvb3zFaD68HhEoRKGwkoJV0K8MFDDLKqy4EnvUCuv9iTw1nCz7S3zhj64YzY1fGfkoq9Rbf+EUEIJZVqna4/ZjzISwa9cu2vGBBx6IfMqQcWwF9+vHFYFDFKP54kuHxDXSFFIP0WcV12tAsleyAXBIaJWHLJp53qmpqYULF0adtYmUEn/WpoBShTMkxHoZWfr+nWI01+kkRRv3aJbQMoOed5YLDnhjGagRO3evWLEiE6tUrRkmyQ+Kg+Kmp6fZtCrxZ5gM7vXwvNocNL2KUrSAQoh7ghb5/u4dNM2vUlMhZlcB6pholapVYmFVoe7u7q1btwpfZCafDA0NsdmwqeKDuSUBaA6GWoUxNjbW09PjrNxBZRK2ckd/f7+zcgdFwModADQTo63CGB8fp3IL2WPBggXMJJ2dnfQnJa9tfcJXY0LJOsmgdWgBqwAAcgWsAgDQC6wCANBLcqvQP5p5XgBAzkliFVZQgVUAAG5gFQCAXlrAKmNjY4ODg4sWLerq6mI9yx0dHfTnwMDAnj17dJ0FAKCI0VYhn5A9nFFw/f39bBTc0qVLnVFwFKFtR60AkAmGWmVqaoot497d3b1+/XrhjCuTk5MjIyNsxD6VWzBiH4DmYKJVJiYm7rrrLnLFsmXLVGZwKpfLid4uBABEwTirUJGDlBJ1JgSKHH8mBABAFIyzCqv4xJi1iXaJPA82ACA6ZlllbGyMzEA1mqhKYbCJVkZHRyOetjbrSMisTb4ZSUQTx7mmR5FMsFKLEjxFUtxlM6zZXTwzMBVlU6bompWlNvNTqRfzsrQjZlll0aJF3d3d8ZTyljXjStTZsJ3JlsTZjE0Uya3jxRnB1oU1F5ytDsFEtGzJ+IpstrdYa7i7VFXwz+8mkJueGeTcmsEccu2GQVYZHx+nksbIyEhsqxBDQ0OqxRXP7G1BVgmd6pGfmb7CfcQJgzdIDKc0prb0Fxro6uqbfEfUOIe2a7pb2XSaoBUxyCps8jfhtG+Riit0kMHBwZCT1QshVnbolatDxSriPOdMke06kDfn1T4K0k44tlOke9lFrSiLd0Re7yM0FaDVMMgqVP1p3oqolN+clge5OlSaOaRZzjPNvk8h3EfxnRLsvLpXPIs961+bDMsItRcGWaWjoyP2SkBu+vv7u7q6Ipw4mVVC1v2ob+Gt4V6sI1Z7ircwpBwtxRUPoZU2IaFVVlg057yKk+qHwtYai3BiuVUUKkCq6yz7xeFfuD1yjlRdutSvhBRXZ0YlqE2AVcIJsYoLQX9tQHbybmr0ATkr/libYq6WrpDT3REbKZR0lvuIapW4XeLARAyyClVbqPKS3CpUjaLKVIQTB1mlyPqKS57RINwigypWqdayntPpZPfGetdwb2zm+nT8KFvFnwxRN3GR6zOGVYAcg6zS09PTvNZaNxFKCxWuVyWKVfjD+apBtQ5ipyQTnEcTllV014BglXbCIKsMDg5SzUXldcIAJicnI4/bj1YH8WUx1XYV2ZHYdm6dZcF67qKj56tdBa21bYJBVhkdHSUhDA0NJbHK1q1b6SDj4+MRThwxRwibS6R9QNJs7637cA6xPgot5+SrDwhFlTbBIKtUrUpQd3d3kuJK1BH7NRJZRZ6hAg/r9YhAIeGFlQjjVbhSkF6roF+5zTDLKqy4EnvUCuv9iTw1XDSriMfi81oJOqpfGXHKKs6ZQ7O7YJyMRqsoyQ20FGZZpWqP248xE8KuXbvYMu6RTynN/+Ve/8t5wtHpTiWDew9IbAVB1YjTiuK4OKdDh+vxbrx0mNp7QK7XgERvaIOWJYlVHJp53qmpqYULF0adtYmUEn/WJrlV6mWBYr1vmeVS0Wt2zkt+xUZE2Qt3YmGwRLhHsyj++nvGnzTtnWXXKfHGctthnFWq1gyT5AfFQXHT09NsWpX4M0zKx9xbs5a486xs0hLfG9Di6VUaJxP6xj1Bi3x/8cn5lEoPpWt+lZpAMbtKe2KiVapWiYVVhbq7u7du3Sp8kZl8MjQ0xGbDpooP5pb005BBREkBEIihVmGMjY319PQ4K3dQmYSt3NHf3++s3EERsHKHHJgF6MdoqzDGx8ep3EL2WLBgATNJZ2cn/bly5cq29QlfIVEh61SDFqEFrAIAyBWwCgBAL7AKAEAvsAoAQC+wCgBAL7AKAEAvLWCVsbGxwcHBRYsWdXV1sZ7ljo4O+nNgYGDPnj26zgIAUMRoq5BPyB7OKLj+/n42Cm7p0qXOKDiK0LajVgDIBEOtMjU1xZZx7+7uXr9+vXDGlcnJyZGRETZin8otGLEPQHMw0SoTExN33XUXuWLZsmUqMziVy+VEbxcCAKJgnFWoyEFKiToTAkWOPxMCACAKxlmFVXxizNpEu0SeBxsAEB2zrDI2NkZmoBpNVKUw2EQro6OjKudyT2cSOCOKJ6I8nkK0xvxsssmOoiyAETJRCr9Z18wqtdlbSr2YW6V9McsqixYt6u7ujqeUt6wZV9Rmw7aX9allbvcsb3x2tj3A5oOzZ3zjMqZCtMbahZWKbLa3SOstN9Mq3CpkQWoErY5BVhkfH6eSxsjISGyrEENDQwrFlVru9a4OWBFNHC2aoV40n7xKNE4YvEEiruHePKsIptRslMxkE2mC1sUgq7DJ34TTvkUqrtBBBgcHA09FdSzZLLX+Bda5LKW4wI4vmsAYTG1y7YSRpVXcW7EOUNthkFWo+pPNiqh1/G0a0hyluMyYJ5pPIdxHkZ2SB6tURcU00PoYZJWOjo7YKwG56e/v7+rqip5kf1lFPkm2J6upReOt4V6sI4ZTcmIVaKUdMcgqipPqh8LWGoucYnFRJXQBZdV1lv3i8C/cHj1f5sMqWAy1DYFV1OBXLQ7ILe5NitHcfUDOij/Wptjrnse0ShhRrRKlMxy0BgZZhaotVHlJbhWqRlFlKsqZAzp29Fmldp7GkkF2n6x3DffG5mL4cJDYVinKgFWAEgZZpaenJ4PWWntkmnjsiF6r8Cf3VYNq3d1OSSYsp+akBgSrtB8GWWVwcJBqLiqvEwYwOTmpPm7fMYpwIKzedhXR6RtO4dZZFqznLk5g5laJXYMDxmKQVUZHR0kIQ0NDSayydetWOsj4+Hjo6er1AWlVQ56hPCJRjCY5fpGvB7k3B5cAgvMzf/oU+4BQVGkrDLJK1aoEdXd3JymuqI3Yd5pOgxovpPnFm5sVo4kS4GwUKCRpYSWGImLsgn7ltsQsq7DiSuxRK6z3J3xqOMWmAIkv/LZQjMYlwLUtXlklKE+LdKDbKk5pD05pM5Jb5cEHH2zmedm4/RgzIezatYst4x56CuX2Rae7mXvBhx/PFh4t6PycVtTGxTm9Ot5qnP1Sky+/67OK6zUgrN7chhhnlampqYULF0adtYmUoj5rk/3CsqR/lW+KsOI23m3m36dTjOY6vaRo4x7NolQGcI9B8b5SLEtl0neWC43z4Y3lNsU4q1StGSbJD4qD4qanp9m0KuozTDrTIIjx5UZ72gLbGtLpVZSiBRRC3BO0BLf4iE7tzu3WdAuCeJrmV6mpE7OrtDMmWqVqlVhYVai7u3vr1q3CF5nJJ0NDQ2w2bKr4YG5JAJqDoVZhjI2N9fT0OCt3UJmErdzR39/vrNxBEbByBwDNxGirMMbHx6ncQvZYsGABM0lnZyf9uXLlyrb1SWAVTkDW6QUtRQtYBQCQK2AVAIBeElrlQYtmnhcAkHNi526SCawCAOCBVQAAeoFVAAB6aQGrjI2NDQ4OLlq0qKuri/Usd3R00J8DAwN79uzRdRYAgCJGW4V8QvZwRsH19/ezUXBLly51RsFRhLYdtQJAJhhqlampKbaMe3d39/r164UzrkxOTo6MjLAR+1RuwYh9AJqDiVaZmJi46667yBXLli1TmcGpXC5HersQAJCEhFbJZCYEUkrUmRAosvpMCACAJCSxilNcaeZ5WcUnxqxNtIv6PNgAgNiYZZWxsTEyA9VooiqFwSZaGR0dDT+Tb06SgAlRPPOeBE6wEhrNntOfzaAinxhSZW5p8XRv3uOErwVdlW4XL0lWm9SqF3OrtDtmWWXRokXd3d3xlPKWNeOK2mzYdpaszdxWashAOKV10RXVnvGNy54K0RprF1YqstneIq23XL8IQXRuIUY9s8C5NYN54NoZg6wyPj5OJY2RkZHYViGGhoYUiiuUq72/txVxFuWnmw5Y5jA4GicM3iCR13AXz4YtmKRa4zzYrilrZVNoglbHIKuwyd+E075FKq7QQQYHB6OeXTljKRYEfNEExqh9FKQd5UR7zy1SjfY1O+ySDLzSlhhkFar+ZLAiqk2Epf6Eq7KHRPMphPsojlMaZ2/sGVR80bu+GJYCal8MskpHR0fslYDc9Pf3d3V1RT07v3yPdEEfb4ZTi8Zbw71YR1ynNE7D9pZWW9JbtRBaaUMMsoripPqhsLXG1M8raTzVvM6yXxz+hdsT5E5n4Xd/3cx3LamssIxKUBti0Ci45lrF03MqWuoiIM+4NylGc/cBOSv+WJt0rH7uWotEvCiIsJvYT1SrqHeDg9bCIKtQtYUqL8mtQtUoqkyFna1WQKn3K9sdwd5+Id1WqZ2zsWSQ3TPrXcO9sVm6qLzkapxFDIOGr8jWVSsKdoZVgBSDrNLT05NZa609Pi18+WT/pihW4U/rqwbVqjFOSUY9v3oWMFRddTlkO6wCpBhklcHBQaq5qLxOGMDk5GTMcfv+3hTN7SqyM7Lt3DrLgvXcQxPO9Xn7oqTSroLW2jbEIKuMjo6SEIaGhpJYZevWrXSQ8fHx6En25SN5tvKIRDGa5HRFvh7k3hxlhXnuD/mlSQ4Sqw8IRZU2xCCrVK1KUHd3d5LiitqIfSH+fCTNNd7faMVoopN5DeY9hGJhxd+9Kx4jnIJV0K/cxpg1vworrsQetcJ6f2JODcdnI4kv/LZQjMady7UtbllF7fUB/VYRjekF7YNZVqna4/ZjzISwa9cutox76CkqZe6120pZlE8aLRauiLxDFKP54kuHxFmojIuTFBdSfQ/I9RqQ/C1v0OIYZ5WpqamFCxdGnbWJlKI+a5Orn9XzyrLgbTmnd8WKKn2rTjEaQywMVrRxj2YJKQnIX1nmvaLnneWCA95Ybm+Ms0rVmmGS/KA4KG56eppNqxJhhkn2g+vqjbWmQ5HkE3vkrW0N6fQqStECCiHuCVoCpntxHUZemvFt1jW/Sk2amF0FmGiVqlViYVWh7u7urVu3Cl9kJp8MDQ2x2bCp4oO5JQFoDoZahTE2NtbT0+Os3EFlErZyR39/v7NyB0XAyh0ANBOjrcIYHx+ncgvZY8GCBcwknZ2d9OfKlSvhEwCaTwtYBQCQK2AVAIBeEs6vAqsAAHzAKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9wCoAAL3AKgAAvcAqAAC9ZGgViAWA1iNJ1k5olYRnBwDkkISZOrlVnDQAAFqG2DbQZRUAAHCAVQAAeoFVAAB6gVUAAHpxlLJ8+fKs0wIAaAVgFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXmAVAIBeSCawCgBAI7AKAEAvsAoAQC+wCgBAL7AKAEAvsAoAQC+wCgBAL1qsMjExMTAw0NHRcXtEurq6+vr6aHeNVwQAyJbkVhkeHiaf3Hnnnf39/asiQrvQjrQ7HUTvdQEAsiKhVaiYQU74+te/Pjk5+VYsaEfanQ6CEgsArUFCq1D9hQobsZXiiIUOQnUo7VcHAGg+Ca3S1dVFtZgkSmHQQai4ov3qAADNJ6FVbr/99lWrViW3Ch2EDqX96iJSLhUKhWJvJet0AGA0+bZKpbdY4CkWi6Xe3rL+zJ9bq5RLxZymDAAeM6xSdOHWS0lvPmtjq1TKvaViHi8dGIgJVuEf9kqF8lldOKVyrAsXkVurpI7siwYgBglnbcrIKu6thYI2r8AqbXjpQD8mW6Va94C23ACrtOGlA/0YbhWhVlgbgbv1xduyy45aK+FUWIOFXdwRWCW0PBTkNbatsWuyhPnTEHY0115WVHFEYYM49AKSYLpV+JzrNPCWatjZzp0j7czLdg2wSqUeoxTU3yRPozdpiRMmiBp4NHuvXpbAesSCL2JNOM7nLE6J8xMAUTDeKo3fd/vvUklUAnDLop7Niv4je62iopSARPo/Tpwwn1VCj2ZfQMHbt1P3kahIhiIK0ELLWUUhirRW47KKMO8FHt93OJV8Gi1hYa3S3DfhaFF4NIFqYBWghRa1SqVc7u21agZFrswvP6ptlShKaeworMxwyY2fsKhHk7uIbz+CVYBGzByv4kLQFuJqwSxazQnFSJm3GFEpjT0bpxBk6OQJ81gr/GiwCsgI063iyzlOKaPMZRl/5pXmtnr2jDa8zntMWbZNlDC+qhR8NFgFZITZVvG3EIizpThPBuW2yFUgb1JluTZZwvzuDDsarAIywmCrCLK+eFyHvzE13CrOoSJkNCetosPrSFhjg9rRYBWQEUZaxfUakLd7lc9ZjfaHiFZpjFZRzmvuClRolo+eMK6eF3K06FbR9+4DaGsSWqWjo0PXrE2dnZ3c4QXvLBccRG8sOyNF2aAvllV6o7WruI7qnF9NLM7oNcH7kMkTJuiCDjxaFKt4R9XpfhcctBkJrTIwMKBrhsm+vj7u8OLh5MXAwZ/u3hGWPSK21npzVCSxBJZukiZM3qUkPlokq1Tt6RbqBR5YBcQnJ7NhL1iwALNhA9AaJF+5Y/PmzQlX7iClYOUOAFoGXauMUf2ls7Mz6ipj5BOqQ+kqpfCVJR4tJwIABIAVUQEAeoFVAAB6gVUAAHqBVQAAeoFVAAB6gVUAAHqBVQAAeoFVAAB6gVUAAHqBVQAAeoFVAAB6gVUAAHqBVQAAenGssmzZsqzTAgBoBWAVAIBeYBUAgF5gFQCAXrRYZWJiYmBgoKOjI+pccF1dXX19fZixFoBWIrlVhoeHE85bS7tj3loAWoaEVtE1xz4dBCUWAFqDhFah+ouu9YCoDqX96gAAzSehVbq6unStXUjFFe1XBwBoPiSTJFZJeZ1lAHKOdL3JdibfVhGuiFqQrPMJGGxlU+U155tEPlOVHFhFgBlWKXLk9jZWyr2lYqaZJ5/5N5+pSg6sIsAEq5h0y1iKWy/zAAnmPaJNAFbRC6zSZpj3iDYBWEUvsEqbYd4j2gTMtkq93cUfoVxyf2r9ZeVzq83Dbu+ttc0I8z5rGSm44vX6IzbSVSnXj1jqFbUsh+nFfy7+VOwU7uT0hjzB3qt3Hcd/oJIvcQoX7jq498sUJbx5qZIR+vVGvBxPpIL1AMWzivozmSSFxQQpTASzyvLly420irBs4H9+63ewzHJ9sVTDftT8ub7R6cTilRp/lrlopTI7lQXd5d7GDvW9gzNAoy3akybPmewzuKKEmUqYf8sl74UVfVeveOH2wZlCi0VP1MAHN9VUCVH/etUux0mMO16R/T+mVRSeyaxSmAgTrMLj/uLZ1+58wovIyfre34H6sflD+eLVygr+e1h3GT0HYi2p1YAqpCHfqXx7i45WKVeiWsW+VN8XUHacp3zhDct5EiX4LpuaKiHhX2+UyxEmxnk+Y1klyjPZ9BQmwgyrcP3KIp2XqyKnuL5tydHtDZLqVGOTP7fLjxm7XcWXflm1IRBup7CjRLhw6Zfpc3uTU6UK93ioXo40MQGpDEL1mcwuhYkwwSrhX4inQBncysIf3r4zjbpuWMyAxzr6A18pl3t7rQKw0wLgpFX8cx6CsAYY9CVGuHD5wYIOknqqAgj6etUvR56YBO0qSsbMKoWJaA2reIqUwk3iJ9C9Kfhc3oPosUql7G76K1qVYf/viiuOtHlZlFS/mQK+wygXrs0qelMlPoLC16t4OQGJSdpaG7YpqxQmolWsElB/TM0qyQrnTtXYrQrxgd3dJOFqgVUa+4d9vbBKKrSGVerxSsI7oFg2DPSBLyEarCI+RGC9wP7xDTm6MP8G7BPlwjVbRVeqVGOoN+6olk3jNX1FqK9klcJEtIJVXF+b6BuUNiQKe48itNYmsorwXoe2rKl8If4jKx40SmuthnYVrakKP53kkJFaLeTPUMx2lfBnMrsUJsKxSrxVxnJgFe/XLngWG0M+PIVhebep79ZIe5blD7xy8UrchOJcTLksHbUlh48ju65e+wPlC1d+yBV0pDFVPCpfb4TLEfed20kJvd+yrjTlZzL1FOpluYsYu3d0dOiatamzs5M7vKxnufHSMq9ocd9csbeXxSx6BlP51e4aduUdcOS7WWG12Pr+3EBR0alcSaoPdvIpsugdAhah6BZyXeLxZkEXHq1AHtY+oC1V8b5e9csRpcVutlH44ZN8G0rPZHNSqJmEVhkYGNA1w2RfXx93+Mb3xOGq8PgfMZ/xna9fbYy5YAg5FzGsKbHYSGXQ0+/upWAC8rffeJLCeoHi1a1E1+U7lsqF6yyraEyVmNCvN2qFTjS6XqmSLv42lJ7JJqVQLwmtoms27AULFqQ2G7ZKLyQAzaSZz2QGrbUJrUJs3rw54codpJQ0V+6AVUDeaOIzmXC0dyySW6VqlVio/tLZ2Rl1lTHyCdWh0luzo6BMSgkAQETzrJLFgH09Vsk3KKuAvJHGM1nu5Zpl7C6gZj/9sAoATScVq5ScRlr3xApKs0ZohvmkpVdvh1VA3kjlmWRzVHn7DNVnuNKJltXbAQDAAVYBAOgFVgEA6AVWAQDoBVYBAOgFVgEA6AVWAQDoBVYBAOgl4dqFAADgA1YBAOgFVgEA6AVWAQDoBVYBAOgFVgEA6CXhekAAAOADVgEA6AVWAQDoBVYBAOgFVgEA6AVWAQDoBVYBAOgFVgEA6AVWAQDohWTClgSCVQAAWoBVAAB6gVUAAHqBVQAAeoFVAAB6gVUAAHqBVQAAeoFVAAB6WW4DqwAAtACrAJAXKuXeUrFYqFMs9ZYrns29zjYhxd6K7Miy3YvcSXQAqwCQC5xMXywRtgBKZXcMco4QPm7ACfy7heooMrAKADmgXOKyd90C4VmeRQyNJ4pGpSPFk0QBVgEgcyReUNOFJSQFLUiOxnwWUs6JBqwCQNbI7RFuDLavihQC1QWrANBSyDN2aEFCtaBSlVklBanUrYJVxgDIjpCyitwakZTAn6XeqlLU6xSsXQhADpDJg30utUq0JhFhxzQZRXvHMqwCQA6w+3vcebxSd4rUKhFqP65ziHqWNRdWYBUA8oCrJNHI8UU2cEWY6RX7k0N2SKVrGVYBICdYQ2tdo16p3GKZQJjjo7eySjWkvW8ZVgEgv1gZXpTfY3TdSK0SudQTBqwCQG7RKRVYBQAgr5nEGmSCGhAAbY3diqqnoVa+F1prAWhVqLxQ6/kpWe8r2022esap2Mh7lguaB63AKgDkgIp7ZpWQaU9itoOIR8GlMQwOVgEA6AVWAQDoBVYBoOXhaz4ytJwOVgEA6AVWAQDohVkFszYBAHQBqwAA9AKrAAD0AqsAAPQCqwAA9AKrAAD0AqsAAPQCqwAA9OJYhcg6LQCAVmC5i6zTAgBoBWAVAIBeYBUAgF5gFQCAXmAVAIBeYBUAgF5gFQCAXphPWmDWpucnj93Ut3/Gt14sfHUcIfNw830TdEcU793Ro0f37t37/PPPPwtywL59++iOJMmMTCmmW4UeYPLJN7ccPnbq/AWQNXQXvr/jyIxvvqgiFnqAySeHDh06fx73LnvoLhw+fJjuSBKxtIZVbr5/YvGOo1nfEODhG8NvUOkx9N7RL+P09HTWiQUepqamqPQYOz+2hlWoyI1SSt44duocFVdC7x0Vuc+dO5d1YoEHuiNUXImdH1vGKlnfByCA7kvovSOrZJ1MIIDuS+z8CKuA9IBVzAVWgVXyCaxiLrAKrJJPYBVzgVVglXwCq5hLEqu0xtqFsEo+gVXMBVaBVfIJrGIusAqskk9gFXOBVWCVfAKrmAusAqvkE1jFXGAVWCWfwCrmAqvAKvkEVjEXWAVWySewilY23FEg7tjQlJPBKgZZZd++DYvvuPHGgsONN954x4Z9+4RxF9fi3biY38ieL3rAhPvlhrxahbsJdBfuWJzzLxNWaTKGWGUfPcpum9zosQsvD4lV2MdC2+SMHFpl34Y7vEp33YS8f6OwSlMxwSp1FxT4H0XnQfc/1SKr2Epp0rOVjLxZRX4PasWXO+6AVRrAKvm3Sr3KIv013MciiAzi/syp+aSbWl3kyyqGVBvlwCpNJe9Wqf9GBj8PvFd8VjGn5mOTJ6uItG0YsEpTyblV5K2uHrjn3rOfibkiR1aJnCN9Dbp8palxRCuqPKJ9wFpN192u5q9weQ5TsBrxE15DEpJbxfT1gHJuFetpUPCB/6lpWKXe8mJKzccmP1aJmCGdBpgb76hhy8Czf/2Qi9lNqkcsCCKyyPYmV0Rv3fZGbrv/oTHGKmx+FVglTdgjqvAw+Ms09b/vsJViUjHFIjdWUSwtuuLf4fu6pW1cJBT3YUXV1EYLu6fVbINTqBHWbWVnhFWaREtbpSD62TKDnFklUX7kDiFrOeeqqqEykEXwfw6rNJWWsgpfA9pQt4tpFaC8WSWqmPdt2LB4sVUBulFQtZHmcV+FV00qwrRFPZJOYBUTrJKoXSW0Zzqf5MYqyk1bdTxj5WpSueMOrmVL0SqhvymeEqkAWCUj8m2VaH1ArofGs5/TgGiQWPJjlUiFFaeZYwPXOJKeVVgrrYBGKmCVppJzq0QZr+KOI2tnMaYmlB+rqN0Cd1R/TO72RLJKkM+Uy1GwSlPJu1XCx3Uq9gIYVhPKkVUifHXCzMtbSbVdJdRnsWvIqaJl5Q5YJWWcl304s0R6D8iomlCurBL4GpA1BO0OT13TXRV1mlliWEXmMzqj/YHsN8f3bhKs0lRMsMoFb7vcjb53lnnZyH/E7JESua8J5cwqF4SvjfPvLDv3iY1tY45ZHLNdxXtEz3A5bhi1O4JgHBys0lQMsUoNNhDc8zK+dGYPedHYkAJL/qxSQzS7iv8muDuB2ND6uK21rrP6Buz7ZtXhJ97xPxiwSlMxyCptRT6tAlSAVWCVfAKrmAusAqvkE1jFXGAVWCWfwCrmAqvAKvkEVjEXWAVWySewirnAKrBKPoFVzAVWgVXyCaxiLrAKrJJPYBVzgVVglXwCq5gLrDLjWy8eO3U+6/sAPBw7dU7FKs8///y5c+eyTizwQHcEVrmpb/83hg9nfSuAh+9vP3LzfROh927v3r1TU1NZJxZ4OHz48L59+2Lnx9awyvOTR6m48v0dR1BiyQN0F74x/AbdEbovoffu6NGjVFyhxxglljxAd4EkT3eE7kvs/NgaVqlaYrm579nC/3i8cOdjCNmGGV1P3LT4GRWlMOgB/uUvf7lly5ZhkDUjIyNjY2NJlFJtIasAAHICrAIA0AusAgDQC6wCANALrAIA0ItjFeKNN97IOjkAALMhjTCfPPzww/TfvXv3Zp0iAIDZkEaYUsrlMv3jF7/4RdYpAgCYDWmEZEJKWbt2Lf1j8+bNWacIAGA2pBGSCSmF/YP0knWKAABmwyo+pJQdO3awBpbJycmsEwUAMBUSCDPJDgtWCdqwYUPW6QIAmMqmTZtY9YeUsnPnzscee2y5xdNPP5110gAA5kHqYA4hmey02L59+7p169iHr7zyStYJBACYxKuvvsrsQRohmTCrUImF/l0ul5ctW7Z+/fqs0wgAMAmSBimFBEIaYdWfXbt2sdYVKro89NBDTCwosQAAQqFSCumCpEHqIIEwk+yycIorw8PDTCwExsUBAAIgRTBXkDRIHe6Cyu7du53WlSeeeIKEs2rVKoq5dOnSDRs2kIuyTjsAIF+wIgopgkRBuiBpbLdgJtlt4bSuPGGxbdu2tWvXLrUYGBhYuXLlpk2bnnzyyRdffPHQoUNZXxAAoNlQxqfsTxIgFQwODpIWmB9IFKQLkgZvFacS5FiF2Lp16+rVq8lFD9jcb3Pffff9G8dPRfwvET8Joz8KP05MXxj/GsaPwvghyJTQGxR6i92EPjDJn8nQxz40E7kRZkN3PuWzM3GfjZPxmQdIKaQFkgMpgrmCWcVd/SH/sNYV1rTiWOVxi5GRESrqUCGHqk7Lly+PahWhYSJ9IaG2aYJVImkn9AFOTtZ5tNk04StN/gy4McIkQnirOEqh7E8SIBVs2LCBtMD84FjF3U5LSvn/AZi9nGg=) &] [s0; &] -[s0; U`+`+ can be switched to [^topic`:`/`/Draw`/src`/UHD`$en`-us`#Upp`:`:SetUHDMode`(bool`)^ `" -UHD`" mode]. In this mode, if icon is retrieved from .iml and -it is in Standard resolution, it is upscaled 2x (relatively smart -algorithm is used so that it looks quite fine on UHD display). -Correspondingly, in standard mode, UHD resolution icons are downscaled. -Icons with `"Fixed size`" are left intact.&] -[s0; &] -[s0; It is also possible and preferable to have both standard and -UHD variants in the same .iml file. If this the case, UHD variant -[/ name] is changed to [/ name]`_`_UHD in C`+`+ code (to avoid name -clash). Also, in UHD mode, UHD variant is used to actually overwrite -standard resolution variant.&] -[s0; &] -[s0; UHD is normally activated when standard icon height is greater -than 22.&] +[s0; U`+`+ tries to pick icon with exact scale first, then tries +highest resolution icon and downscales it. Recommended approach +is to provide 600% master source. The advantage is that this +scale does not need to be drawn with antiliasing, it is oversampled +enough to give good results after downscaling.&] [s0; &] [s0; In addition to Zx/Zy/Zsz functions, U`+`+ also provides set of [^topic`:`/`/Draw`/src`/UHD`$en`-us^ DPI functions] which simply -double arguments when UHD mode is active `- this is sometimes -better approach for scaling things that are related to images.&] +scale arguments by currect fafactor `- this is sometimes better +approach for scaling things that are related to images.&] [s0; &] [s0; Last but not least, some host platforms (Windows) need to be specifically informed that the application is able to handle diff --git a/uppsrc/CtrlLib/ChCoco.cpp b/uppsrc/CtrlLib/ChCoco.cpp index 5cc0e66a1..6d8842271 100644 --- a/uppsrc/CtrlLib/ChCoco.cpp +++ b/uppsrc/CtrlLib/ChCoco.cpp @@ -66,6 +66,11 @@ Image CocoImg(int type, int value = 0, int state = 0) Color CocoColor(int k, Color bg = SColorFace()) { +#if 0 + static int ii = 0; _DBG_ + String p = GetHomeDirFile("Color" + AsString(ii++) + ".png"); _DBG_ + DDUMP(p); PNGEncoder().SaveFile(p, CocoImg(bg, COCO_NSCOLOR, k, 0)); +#endif return AvgColor(CocoImg(bg, COCO_NSCOLOR, k, 0)); } @@ -135,11 +140,20 @@ void ChHostSkin() SColorPaper_Write(CocoColor(COCO_PAPER)); + if(Atoi(GetEnv("UPP_SCALE__"))) { + if(IsDarkTheme()) + ChDarkSkin(); + else + ChStdSkin(); + return; + } + SColorFace_Write(CocoColor(COCO_WINDOW, White())); SColorHighlight_Write(CocoColor(COCO_SELECTEDPAPER)); SColorHighlightText_Write(CocoColor(COCO_SELECTEDTEXT)); SColorText_Write(CocoColor(COCO_TEXT)); SColorDisabled_Write(CocoColor(COCO_DISABLED)); + ChBaseSkin(); diff --git a/uppsrc/CtrlLib/ChCocoMM.mm b/uppsrc/CtrlLib/ChCocoMM.mm index 96e5b968c..d00d551e2 100644 --- a/uppsrc/CtrlLib/ChCocoMM.mm +++ b/uppsrc/CtrlLib/ChCocoMM.mm @@ -17,8 +17,9 @@ void Coco_PaintCh(void *cgcontext, int type, int value, int state) { auto dopaint = [&] { auto cg = (CGContextRef) cgcontext; - if(Upp::IsUHDMode()) - CGContextScaleCTM(cg, 2, 2); + double sc = Upp::Ctrl::GetDisplayScale(); + if(sc != 1) + CGContextScaleCTM(cg, sc, sc); CGRect cr = CGRectMake(0, 0, 140, 140); if(type == COCO_NSCOLOR) { CGContextSaveGState(cg); diff --git a/uppsrc/CtrlLib/ChGtk3.cpp b/uppsrc/CtrlLib/ChGtk3.cpp index 76c2ba0cd..bdab0cb7e 100644 --- a/uppsrc/CtrlLib/ChGtk3.cpp +++ b/uppsrc/CtrlLib/ChGtk3.cpp @@ -84,11 +84,11 @@ Image CairoImage(int cx, int cy, Event draw) { Image m[2]; for(int i = 0; i < 2; i++) { - ImageDraw iw(DPI(cx), DPI(cy)); - iw.DrawRect(0, 0, DPI(cx), DPI(cy), i ? Black() : White()); + ImageDraw iw(cx, cy); + iw.DrawRect(0, 0, cx, cy, i ? Black() : White()); cairo_t *cr = iw; #if GTK_CHECK_VERSION(3, 20, 0) - cairo_surface_set_device_scale(cairo_get_target(cr), DPI(1), DPI(1)); + cairo_surface_set_device_scale(cairo_get_target(cr), Ctrl::SCL(1), Ctrl::SCL(1)); #endif draw(cr); m[i] = iw; @@ -100,8 +100,10 @@ Image CairoImage(int cx, int cy, Event draw) Image CairoImage(GtkStyleContext *ctx, int cx = 40, int cy = 32) { return CairoImage(cx, cy, [&](cairo_t *cr) { - gtk_render_background(ctx, cr, 0, 0, cx, cy); - gtk_render_frame(ctx, cr, 0, 0, cx, cy); + double sx = cx / Ctrl::SCL(1); + double sy = cy / Ctrl::SCL(1); + gtk_render_background(ctx, cr, 0, 0, sx, sy); + gtk_render_frame(ctx, cr, 0, 0, sx, sy); }); } @@ -166,7 +168,7 @@ void Gtk_New(const char *name, int state = 0, dword flags = 0) sCtx = context2; } ASSERT(sCtx); - gtk_style_context_set_scale(sCtx, DPI(1)); + gtk_style_context_set_scale(sCtx, GetDPIScaleRatio()); Gtk_State(state, flags); GtkBorder border, padding; @@ -205,15 +207,16 @@ Color GetInkColor() void SOImages(int imli, dword flags) { - int n = (GetStdFontCy() - 2) / DPI(1); - if(n < 18) // sometimes it gets too small relative to text.. + int n = GetStdFontCy() - 2; + if(n < 14) // sometimes it gets too small relative to text.. n = 14; for(int st = 0; st < 4; st++) { Gtk_State(st, flags); CtrlsImg::Set(imli++, CairoImage(n, n, [&](cairo_t *cr) { - gtk_render_background(sCtx, cr, 0, 0, n, n); - gtk_render_frame(sCtx, cr, 0, 0, n, n); - gtk_render_check(sCtx, cr, 0, 0, n, n); + double sz = n / Ctrl::SCL(1); + gtk_render_background(sCtx, cr, 0, 0, sz, sz); + gtk_render_frame(sCtx, cr, 0, 0, sz, sz); + gtk_render_check(sCtx, cr, 0, 0, sz, sz); })); } } @@ -250,8 +253,8 @@ Image Gtk_Icon(const char *icon_name, int size) GdkPixbuf *pixbuf = gtk_icon_theme_load_icon_for_scale(gtk_icon_theme_get_default(), icon_name, size, 2, (GtkIconLookupFlags)0, NULL); if(pixbuf) { - int cx = gdk_pixbuf_get_width(pixbuf); - int cy = gdk_pixbuf_get_height(pixbuf); + int cx = gdk_pixbuf_get_width(pixbuf) * Ctrl::SCL(1); + int cy = gdk_pixbuf_get_height(pixbuf) * Ctrl::SCL(1); Image m = CairoImage(cx, cy, [&](cairo_t *cr) { gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); @@ -397,7 +400,7 @@ void ChHostSkin() Gtk_OverrideDialogIcon(CtrlImg::I_exclamation, "gtk-dialog-warning"); Gtk_OverrideDialogIcon(CtrlImg::I_error, "gtk-dialog-error"); } - + YesButtonImage_Write(Gtk_IconAdjusted("gtk-yes", DPI(16))); Image no_image = Gtk_IconAdjusted("gtk-no", DPI(16)); if(Difference(no_image, cancel_image) > 20) // it is ugly when No and Cancel buttons have the same icon diff --git a/uppsrc/CtrlLib/ColorPopup.cpp b/uppsrc/CtrlLib/ColorPopup.cpp index f53bfea03..06e00cd44 100644 --- a/uppsrc/CtrlLib/ColorPopup.cpp +++ b/uppsrc/CtrlLib/ColorPopup.cpp @@ -156,15 +156,12 @@ int ColorPopUp::GetCy() (withvoid ? StdFont().Info().GetHeight() + DPI(3 + 2) : 0); } -void ColorPopUp::DrawFilledFrame(Draw &w, int x, int y, int cx, int cy, Color fcol, Color bcol) +Rect ColorPopUp::DrawFilledFrame(Draw &w, Rect r, Color fcol, Color col) { - DrawFrame(w, x, y, cx, cy, fcol); - w.DrawRect(x + DPI(1), y + DPI(1), cx - DPI(2), cy - DPI(2), bcol); -} - -void ColorPopUp::DrawFilledFrame(Draw &w, Rect &r, Color fcol, Color bcol) -{ - DrawFilledFrame(w, r.left, r.top, r.GetWidth(), r.GetHeight(), fcol, bcol); + DrawFrame(w, r, fcol); + r.Deflate(DPI(1) + 1); + w.DrawRect(r, col); + return r; } void ColorPopUp::Paint(Draw& w) @@ -213,7 +210,7 @@ void ColorPopUp::Paint(Draw& w) for(;;) { for(int x = 0; x < 18 * DPI(16); x += DPI(16)) { if(i >= GetColorCount()) { - if(!norampwheel) { + if(!norampwheel) { // draw selected color in the bottom Rect r(DPI(8 * 16 + 1), cy + DPI(4), DPI(10 * 16 - 1), sz.cy - DPI(4) - DPI(24)); DrawFilledFrame(w, r, SColorText, dark ? DarkThemeCached(color) : color); @@ -229,15 +226,19 @@ void ColorPopUp::Paint(Draw& w) } Color c = RealizeColor(GetColor(i)); - DrawFilledFrame(w, x + DPI(1), y, DPI(14), DPI(14), SColorText, dark ? DarkThemeCached(c) : c); - if(i < 18 && scolors) - w.DrawRect(x + DPI(2) + DPI(6), y + DPI(1), DPI(6), DPI(12), dark ? c : DarkThemeCached(c)); + Rect r = RectC(x + DPI(1), y, DPI(14), DPI(14)); + Rect rc = DrawFilledFrame(w, r, SColorText, dark ? DarkThemeCached(c) : c); + if(i < 18 && scolors) { + rc.left += rc.Width() / 2; + w.DrawRect(rc, dark ? c : DarkThemeCached(c)); + } if(i == colori) { + r.Inflate(1); if(GetMouseLeft()) - DrawFrame(w, x, y - DPI(1), DPI(16), DPI(16), SColorShadow, SColorLight); + DrawFrame(w, r, SColorShadow, SColorLight); else - DrawFrame(w, x, y - DPI(1), DPI(16), DPI(16), GUI_GlobalStyle() >= GUISTYLE_XP ? SColorText : SColorHighlight); + DrawFrame(w, r, GUI_GlobalStyle() >= GUISTYLE_XP ? SColorText : SColorHighlight); } i++; } diff --git a/uppsrc/CtrlLib/CtrlUtil.cpp b/uppsrc/CtrlLib/CtrlUtil.cpp index 800be5ccb..38e8fca55 100644 --- a/uppsrc/CtrlLib/CtrlUtil.cpp +++ b/uppsrc/CtrlLib/CtrlUtil.cpp @@ -93,7 +93,7 @@ bool CtrlLibDisplayError(const Value& e) { String s = GetErrorText(e); if(s.IsEmpty()) s = t_("Invalid data."); - Exclamation(s); + Exclamation("\1" + s); return true; } @@ -101,49 +101,7 @@ INITBLOCK { DisplayErrorFn() = &CtrlLibDisplayError; } -/* -String SaveCtrlLayout(Ctrl::LogPos p, const String& classname, const String& variable, - const String& label, const String& help) { - String out; - if(classname.IsEmpty()) - out << "\tUNTYPED("; - else - out << "\tITEM(" << classname << ", "; - out << variable << ", "; - switch(p.x.GetAlign()) { - case Ctrl::LEFT: out << Format("LeftPos(%d, %d).", p.x.GetA(), p.x.GetB()); break; - case Ctrl::RIGHT: out << Format("RightPos(%d, %d).", p.x.GetA(), p.x.GetB()); break; - case Ctrl::SIZE: out << Format("HSizePos(%d, %d).", p.x.GetA(), p.x.GetB()); break; - case Ctrl::CENTER: out << Format("HCenterPos(%d, %d).", p.x.GetB(), p.x.GetA()); break; - } - switch(p.y.GetAlign()) { - case Ctrl::TOP: out << Format("TopPos(%d, %d)", p.y.GetA(), p.y.GetB()); break; - case Ctrl::BOTTOM: out << Format("BottomPos(%d, %d)", p.y.GetA(), p.y.GetB()); break; - case Ctrl::SIZE: out << Format("VSizePos(%d, %d)", p.y.GetA(), p.y.GetB()); break; - case Ctrl::CENTER: out << Format("VCenterPos(%d, %d)", p.y.GetB(), p.y.GetA()); break; - } - if(!label.IsEmpty()) { - out << ".SetLabel(\""; - for(const char *s = label; *s; s++) - if(*s == '\n') - out.Cat("\\n"); - else - out.Cat(*s); - out << "\")"; - } - if(!help.IsEmpty()) { - out << ".HelpC(\""; - for(const char *s = help; *s; s++) - if(*s == '\n') - out.Cat("\\n"); - else - out.Cat(*s); - out << "\")"; - } - out << ")\n"; - return out; -} -*/ + void Show2(Ctrl& ctrl1, Ctrl& ctrl2, bool show) { ctrl1.Show(show); ctrl2.Show(show); @@ -473,10 +431,11 @@ struct ZoomIconMaker : ImageMaker { ImagePainter w(sz); w.Clear(RGBAZero()); w.Move(DPI(11), DPI(11)).Line(DPI(16), DPI(16)).Stroke(DPI(2), SBlack()); - w.Circle(DPI(7), DPI(7), IsUHDMode() ? 12 : 6.5).Stroke(IsUHDMode() ? 3 : 1, SBlack()); + int scale = GetDPIScale(); + w.Circle(DPI(7), DPI(7), scale > DPI_100 ? scale * 3 : 6.5).Stroke(scale - 1, SBlack()); String txt = AsString(int(zoom * 100)); - Image numbers = IsUHDMode() ? CtrlImg::Numbers2() : CtrlImg::Numbers1(); - int gcx = IsUHDMode() ? 6 : 4; + Image numbers = scale >= DPI_200 ? CtrlImg::Numbers2() : CtrlImg::Numbers1(); + int gcx = 3 + scale / 2; Size tsz(txt.GetCount() * gcx, numbers.GetHeight()); int y = DPI(7) - tsz.cy / 2; int x = DPI(7) - tsz.cx / 2; diff --git a/uppsrc/CtrlLib/Ctrls.iml b/uppsrc/CtrlLib/Ctrls.iml index c05d27883..2e2d90242 100644 --- a/uppsrc/CtrlLib/Ctrls.iml +++ b/uppsrc/CtrlLib/Ctrls.iml @@ -1,4 +1,5 @@ PREMULTIPLIED +VERSION(20260403) IMAGE_ID(S0) IMAGE_ID(S0h) IMAGE_ID(S0p) @@ -113,61 +114,61 @@ IMAGE_ID(TB1) IMAGE_ID(TB1h) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,152,75,110,195,48,12,68,221,174,155,109,239,127,19,251,40,217,228,28,109,80,32,128,76,113,88,146,214,135) -IMAGE_DATA(118,168,64,11,3,243,8,145,30,59,147,44,183,229,182,124,60,63,96,253,48,91,212,114,11,176,172,22,176,94,70,165) -IMAGE_DATA(167,231,188,24,51,106,214,94,31,152,252,182,181,54,236,227,241,168,54,96,89,45,96,189,140,74,79,207,121,49,102,212) -IMAGE_DATA(172,189,62,48,249,173,185,97,215,117,173,54,96,89,45,96,189,140,74,79,207,121,49,102,212,172,189,62,48,249,173,185) -IMAGE_DATA(97,183,109,171,54,96,89,45,96,189,140,74,79,207,121,49,102,212,172,189,62,48,249,173,185,97,13,217,229,112,174,42) -IMAGE_DATA(207,5,24,168,23,56,81,143,52,163,25,67,63,158,185,121,239,143,199,7,21,75,246,110,157,58,195,150,231,210,100,55) -IMAGE_DATA(174,31,41,39,130,254,89,205,104,198,208,143,103,110,222,251,227,241,65,197,146,189,91,167,206,176,229,185,52,217,141,235) -IMAGE_DATA(71,202,137,160,127,86,51,154,49,244,227,153,155,247,254,120,124,80,177,100,239,214,169,51,108,121,46,77,118,227,250,145) -IMAGE_DATA(114,34,232,159,213,140,102,12,253,120,230,230,189,63,30,31,84,44,217,251,69,12,43,14,15,109,203,74,38,153,146,177) -IMAGE_DATA(110,250,134,117,21,9,252,31,100,50,193,153,41,134,213,100,29,41,139,37,243,190,204,20,195,6,254,15,50,153,224,204) -IMAGE_DATA(20,195,142,202,59,26,157,133,161,26,238,154,211,255,199,188,174,209,140,16,35,205,84,170,131,250,148,106,161,53,250,158) -IMAGE_DATA(78,49,108,148,140,84,106,52,12,213,112,215,40,119,105,106,160,25,33,70,154,169,84,7,245,41,213,202,12,27,32,35) -IMAGE_DATA(149,26,13,67,53,220,53,202,93,154,26,104,70,136,145,102,42,213,65,125,74,181,142,206,186,21,51,197,176,81,50,82) -IMAGE_DATA(169,209,48,84,195,93,163,220,165,169,129,102,132,24,105,166,82,29,212,167,84,43,51,236,128,188,51,154,209,244,220,130) -IMAGE_DATA(25,213,79,68,102,138,97,163,100,216,214,140,166,231,22,76,228,25,100,134,61,17,163,233,185,5,19,121,6,189,153,41) -IMAGE_DATA(134,141,146,97,91,51,154,158,91,48,145,103,16,45,195,46,95,203,247,242,249,252,20,75,11,255,105,123,125,109,164,62) -IMAGE_DATA(245,169,239,174,55,61,239,91,249,178,184,223,239,234,183,204,75,219,235,71,81,234,83,159,250,190,122,235,243,126,248,101) -IMAGE_DATA(209,43,223,167,62,245,169,239,171,183,62,239,191,35,30,32,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_DATA(120,156,237,152,75,110,195,48,12,68,221,174,155,109,207,147,155,39,71,241,198,231,104,131,2,1,100,138,195,146,140,62) +IMAGE_DATA(180,67,5,90,24,152,71,136,244,216,153,100,185,44,151,229,227,241,1,235,135,217,162,150,91,128,101,181,128,245,50,42) +IMAGE_DATA(61,61,231,201,152,81,179,246,250,192,228,183,107,107,195,110,219,86,109,192,178,90,192,122,25,149,158,158,243,100,204,168) +IMAGE_DATA(89,123,125,96,242,91,115,195,222,110,183,106,3,150,213,2,214,203,168,244,244,156,39,99,70,205,218,235,3,147,223,154) +IMAGE_DATA(27,246,126,191,87,27,176,172,22,176,94,70,165,167,231,60,25,51,106,214,94,31,152,252,214,220,176,134,236,242,114,174) +IMAGE_DATA(42,207,5,24,168,23,56,81,143,52,163,25,67,63,158,185,121,239,143,199,7,21,75,246,110,29,58,195,150,231,210,100) +IMAGE_DATA(55,174,31,41,39,130,254,89,205,104,198,208,143,103,110,222,251,227,241,65,197,146,189,91,135,206,176,229,185,52,217,141) +IMAGE_DATA(235,71,202,137,160,127,86,51,154,49,244,227,153,155,247,254,120,124,80,177,100,239,214,161,51,108,121,46,77,118,227,250) +IMAGE_DATA(145,114,34,232,159,213,140,102,12,253,120,230,230,189,63,30,31,84,44,217,251,69,12,43,14,15,109,203,74,38,153,146) +IMAGE_DATA(177,110,250,134,117,21,9,252,31,100,50,193,153,41,134,213,100,29,41,139,37,243,190,204,20,195,6,254,15,50,153,224) +IMAGE_DATA(204,20,195,142,202,59,26,157,133,161,26,238,154,211,255,199,60,175,209,140,16,35,205,84,170,131,250,148,106,161,53,250) +IMAGE_DATA(158,78,49,108,148,140,84,106,52,12,213,112,215,40,119,105,106,160,25,33,70,154,169,84,7,245,41,213,202,12,27,32) +IMAGE_DATA(35,149,26,13,67,53,220,53,202,93,154,26,104,70,136,145,102,42,213,65,125,74,181,94,157,117,43,102,138,97,163,100) +IMAGE_DATA(164,82,163,97,168,134,187,70,185,75,83,3,205,8,49,210,76,165,58,168,79,169,86,102,216,1,121,103,52,163,233,185) +IMAGE_DATA(5,51,170,159,136,204,20,195,70,201,176,173,25,77,207,45,152,200,51,200,12,123,32,70,211,115,11,38,242,12,122,51) +IMAGE_DATA(83,12,27,37,195,182,102,52,61,183,96,34,207,32,90,134,93,190,150,239,229,243,241,41,150,22,254,211,246,250,218,72) +IMAGE_DATA(125,234,83,223,93,111,122,222,175,229,203,98,93,87,245,91,230,169,237,245,163,40,245,169,79,125,95,189,245,121,127,249) +IMAGE_DATA(101,209,43,223,167,62,245,169,239,171,183,62,239,191,130,149,22,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0) IMAGE_END_DATA(576, 23) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,154,219,79,19,65,24,197,27,124,51,196,39,99,252,43,253,43,124,241,193,248,138,196,71,76,52,33,6,99) -IMAGE_DATA(136,220,90,90,10,244,126,167,8,4,104,75,111,20,138,92,20,125,92,251,97,166,41,223,204,116,207,148,141,172,116,182) -IMAGE_DATA(249,165,219,153,115,182,105,119,230,163,103,134,240,100,224,89,96,162,247,160,163,90,173,58,189,39,8,161,109,54,155,16) -IMAGE_DATA(194,103,245,86,111,245,254,208,27,207,247,39,129,231,253,98,225,220,225,72,237,93,56,83,243,71,16,164,21,135,120,141) -IMAGE_DATA(64,218,65,95,114,247,28,130,251,18,59,223,33,184,47,254,237,12,130,251,98,219,93,8,238,219,44,159,66,72,190,173) -IMAGE_DATA(19,8,238,219,40,117,32,184,111,189,116,12,193,125,209,98,27,130,251,214,10,45,8,201,151,111,66,112,95,36,215,128) -IMAGE_DATA(224,190,112,182,14,193,125,171,25,12,238,11,165,107,16,178,175,10,33,125,190,212,30,132,116,31,82,101,8,105,156,165) -IMAGE_DATA(114,16,131,190,81,235,210,40,135,47,138,105,179,251,27,130,127,185,141,211,95,16,94,249,234,39,215,16,94,249,142,58) -IMAGE_DATA(63,33,188,242,213,142,127,64,120,231,187,130,224,190,106,251,10,66,246,93,66,112,95,165,117,9,33,251,46,32,184,239) -IMAGE_DATA(176,121,1,33,251,206,33,60,243,213,59,16,146,239,168,1,33,249,170,21,136,177,46,166,111,62,102,33,248,151,107,125) -IMAGE_DATA(214,103,125,247,231,155,158,203,65,112,223,251,133,60,4,247,125,142,20,32,124,81,76,195,225,176,51,42,229,82,218,153) -IMAGE_DATA(253,240,22,130,180,194,39,94,35,144,118,208,183,85,76,65,112,95,169,144,132,224,190,98,62,1,193,125,133,92,28,130) -IMAGE_DATA(251,242,217,24,132,228,203,108,66,112,95,46,189,1,193,125,217,244,58,4,247,101,82,81,8,238,75,39,215,32,36,95) -IMAGE_DATA(34,2,193,125,169,120,24,130,251,146,177,85,8,238,75,108,134,32,184,47,190,17,132,144,125,43,16,178,111,25,66,254) -IMAGE_DATA(124,75,16,210,125,136,45,66,12,250,70,173,75,163,16,160,13,150,71,189,7,29,221,206,142,51,10,239,102,131,206,203) -IMAGE_DATA(233,121,231,197,171,79,183,160,54,234,227,90,98,175,218,114,186,231,87,253,170,78,231,212,38,250,133,158,174,65,237,161) -IMAGE_DATA(124,87,73,182,152,185,209,8,61,189,47,93,43,152,59,85,114,112,80,252,171,25,208,211,17,204,157,40,233,95,147,233) -IMAGE_DATA(87,178,29,37,94,233,151,51,199,74,244,250,182,18,189,190,165,68,167,95,74,55,149,120,165,95,76,53,148,232,245,117) -IMAGE_DATA(37,94,233,67,153,138,18,149,158,198,91,52,183,175,132,143,55,49,158,227,197,29,37,124,60,139,249,64,237,116,45,209) -IMAGE_DATA(78,231,212,198,231,139,233,124,52,225,191,41,22,135,237,107,37,186,98,161,211,235,138,133,78,175,27,76,86,255,176,245) -IMAGE_DATA(149,246,165,18,93,177,168,181,207,148,232,138,69,189,221,81,98,139,133,162,88,172,167,211,78,190,92,112,14,42,219,55) -IMAGE_DATA(208,57,181,169,138,5,181,191,238,101,110,21,212,199,139,5,93,75,167,167,62,126,179,233,253,117,122,234,179,250,241,211) -IMAGE_DATA(79,209,90,141,2,149,158,198,212,204,66,94,9,31,111,98,60,207,69,10,74,248,120,54,157,47,190,47,22,209,240,156) -IMAGE_DATA(243,245,203,140,148,145,168,141,250,184,150,216,223,77,56,141,90,190,223,78,231,212,38,250,69,59,93,131,218,117,49,132) -IMAGE_DATA(250,72,35,244,244,190,116,45,93,12,161,62,210,12,234,233,121,88,12,81,233,135,197,10,47,244,195,98,136,90,175,143) -IMAGE_DATA(33,106,189,62,134,168,244,195,98,133,23,250,97,49,68,173,215,199,10,47,244,195,98,8,215,211,152,210,197,16,62,222) -IMAGE_DATA(196,120,214,197,16,62,158,77,231,139,233,124,52,42,22,143,111,254,41,212,188,88,32,213,11,141,28,232,186,4,186,30) -IMAGE_DATA(129,174,67,160,235,9,166,58,183,245,6,116,157,1,93,95,64,215,9,76,117,110,235,8,232,122,128,169,206,109,189,0) -IMAGE_DATA(93,39,64,215,7,76,126,234,123,249,87,219,87,147,207,45,231,163,249,30,205,125,86,231,79,157,91,254,70,115,55,154) -IMAGE_DATA(183,31,244,228,115,203,3,104,110,70,243,50,154,163,172,206,159,58,183,60,139,230,88,52,191,154,228,86,95,76,62,228) -IMAGE_DATA(119,48,250,251,26,205,161,104,254,68,115,39,154,31,77,117,110,249,18,205,149,104,158,68,115,161,169,206,45,55,162,249) -IMAGE_DATA(207,84,231,150,15,209,92,136,230,65,147,28,232,101,254,11,76,6,158,246,166,222,196,173,201,103,247,157,237,190,243,93) -IMAGE_DATA(245,118,223,249,223,239,59,223,219,100,118,203,139,252,203,117,203,141,252,230,249,109,95,210,234,239,87,63,14,251,194,158) -IMAGE_DATA(77,102,187,111,107,245,126,214,143,195,190,237,31,102,158,15,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_DATA(120,156,237,154,91,79,19,65,24,134,27,188,51,196,43,99,252,57,254,34,127,133,55,94,24,111,145,120,137,137,38,196) +IMAGE_DATA(96,12,81,14,45,45,5,122,62,83,4,2,180,165,39,10,69,14,138,94,174,253,48,211,148,111,102,186,239,148,141,172) +IMAGE_DATA(116,182,121,210,237,204,251,110,211,238,204,71,223,25,158,77,6,158,4,38,122,15,58,170,213,170,211,123,130,16,218,102) +IMAGE_DATA(179,9,33,124,86,111,245,86,239,15,189,241,124,127,20,120,218,47,22,206,45,142,212,238,185,51,53,127,8,65,90,113) +IMAGE_DATA(136,215,8,164,29,244,37,119,206,32,184,47,177,253,29,130,251,226,223,78,33,184,47,182,213,133,224,190,141,242,9,132) +IMAGE_DATA(228,219,60,134,224,190,245,82,7,130,251,214,74,71,16,220,23,45,182,33,184,111,181,208,130,144,124,249,38,4,247,69) +IMAGE_DATA(114,13,8,238,11,103,235,16,220,183,146,193,224,190,80,186,6,33,251,170,16,210,231,75,237,66,72,247,33,85,134,144) +IMAGE_DATA(198,89,42,7,49,232,27,181,46,141,114,248,162,152,54,187,191,33,248,151,219,56,249,5,225,149,175,126,124,5,225,149) +IMAGE_DATA(239,176,243,19,194,43,95,237,232,7,132,119,190,75,8,238,171,182,47,33,100,223,5,4,247,85,90,23,16,178,239,28) +IMAGE_DATA(130,251,14,154,231,16,178,239,12,194,51,95,189,3,33,249,14,27,16,146,175,90,129,24,235,98,250,250,67,22,130,127) +IMAGE_DATA(185,214,103,125,214,119,119,190,233,185,28,4,247,189,251,154,135,224,190,79,145,2,132,47,138,105,56,28,118,70,165,92) +IMAGE_DATA(74,59,179,239,223,64,144,86,248,196,107,4,210,14,250,54,139,41,8,238,43,21,146,16,220,87,204,39,32,184,175,144) +IMAGE_DATA(139,67,112,95,62,27,131,144,124,153,13,8,238,203,165,215,33,184,47,155,94,131,224,190,76,42,10,193,125,233,228,42) +IMAGE_DATA(132,228,75,68,32,184,47,21,15,67,112,95,50,182,2,193,125,137,141,16,4,247,197,215,131,16,178,111,25,66,246,45) +IMAGE_DATA(65,200,159,111,17,66,186,15,177,5,136,65,223,168,117,105,20,2,180,193,242,160,247,160,163,219,217,118,70,225,237,108) +IMAGE_DATA(208,121,49,61,239,60,127,249,241,6,212,70,125,92,75,236,86,91,78,247,236,178,95,213,233,156,218,68,191,208,211,53) +IMAGE_DATA(168,61,148,239,42,201,22,51,215,26,161,167,247,165,107,5,115,39,74,246,247,139,127,53,3,122,58,130,185,99,37,253) +IMAGE_DATA(107,50,253,114,182,163,196,43,253,82,230,72,137,94,223,86,162,215,183,148,232,244,139,233,166,18,175,244,11,169,134,18) +IMAGE_DATA(189,190,174,196,43,125,40,83,81,162,210,211,120,139,230,246,148,240,241,38,198,115,188,184,173,132,143,103,49,31,168,157) +IMAGE_DATA(174,37,218,233,156,218,248,124,49,157,143,38,252,55,197,226,160,125,165,68,87,44,116,122,93,177,208,233,117,131,201,234) +IMAGE_DATA(239,183,190,210,190,80,162,43,22,181,246,169,18,93,177,168,183,59,74,108,177,80,20,139,181,116,218,201,151,11,206,126) +IMAGE_DATA(101,235,26,58,167,54,85,177,160,246,87,189,204,173,130,250,120,177,160,107,233,244,212,199,111,54,189,191,78,79,125,86) +IMAGE_DATA(63,126,250,41,90,171,81,160,210,211,152,154,249,154,87,194,199,155,24,207,115,145,130,18,62,158,77,231,139,239,139,69) +IMAGE_DATA(52,60,231,124,249,60,35,101,36,106,163,62,174,37,246,118,18,78,163,150,239,183,211,57,181,137,126,209,78,215,160,118) +IMAGE_DATA(93,12,161,62,210,8,61,189,47,93,75,23,67,168,143,52,131,122,122,30,22,67,84,250,97,177,194,11,253,176,24,162) +IMAGE_DATA(214,235,99,136,90,175,143,33,42,253,176,88,225,133,126,88,12,81,235,245,177,194,11,253,176,24,194,245,52,166,116,49) +IMAGE_DATA(132,143,55,49,158,117,49,132,143,103,211,249,98,58,31,141,138,197,195,235,127,10,53,47,22,72,245,66,35,7,186,46) +IMAGE_DATA(129,174,71,160,235,16,232,122,130,169,206,109,189,1,93,103,64,215,23,208,117,2,83,157,219,58,2,186,30,96,170,115) +IMAGE_DATA(91,47,64,215,9,208,245,1,147,159,250,94,254,213,246,213,228,115,203,249,104,190,71,115,159,213,249,83,231,150,191,209) +IMAGE_DATA(220,141,230,237,123,61,249,220,242,0,154,155,209,188,140,230,40,171,243,167,206,45,207,162,57,22,205,175,38,185,213,23) +IMAGE_DATA(147,15,249,29,140,254,190,70,115,40,154,63,209,220,137,230,71,83,157,91,190,68,115,37,154,39,209,92,104,170,115,203) +IMAGE_DATA(141,104,254,51,213,185,229,67,52,23,162,121,208,36,7,122,153,255,2,147,129,199,189,169,55,113,99,242,217,125,103,187) +IMAGE_DATA(239,124,91,189,221,119,254,247,251,206,119,54,153,221,242,34,255,114,221,114,35,191,121,126,219,151,180,250,187,213,143,195) +IMAGE_DATA(190,176,103,147,217,238,219,90,189,159,245,227,176,111,251,7,172,160,14,202,0,0,0,0,0,0,0,0,0,0,0,0) IMAGE_END_DATA(1088, 16) IMAGE_BEGIN_DATA @@ -221,20 +222,20 @@ IMAGE_DATA(205,84,212,187,160,211,128,248,239,130,62,83,209,204,255,103,139,203, IMAGE_END_DATA(1504, 56) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,150,81,75,2,65,16,199,183,76,173,215,162,62,175,95,35,232,193,160,135,160,32,161,160,183,56,72,8,130) -IMAGE_DATA(162,52,77,67,189,243,50,205,174,196,178,94,66,184,118,54,71,118,198,187,83,122,58,186,221,227,135,187,243,255,223,238) -IMAGE_DATA(221,238,236,158,98,77,108,138,148,188,244,146,203,229,124,29,193,10,196,44,203,34,232,190,32,157,251,140,199,120,226,224) -IMAGE_DATA(17,102,3,24,79,146,61,34,43,183,64,212,6,136,74,252,121,3,97,31,113,209,194,38,3,248,243,73,144,207,231,9) -IMAGE_DATA(252,36,224,58,247,25,143,241,196,193,99,62,133,198,147,104,143,217,0,198,147,104,143,249,47,56,193,252,23,52,158,68) -IMAGE_DATA(123,228,73,144,21,203,242,90,145,23,79,124,78,92,118,116,20,34,35,95,104,73,94,33,101,102,55,79,98,60,238,7) -IMAGE_DATA(196,253,144,120,152,63,170,127,250,60,1,15,29,117,67,212,32,81,15,22,246,50,139,246,79,11,159,229,226,170,216,82) -IMAGE_DATA(185,148,22,27,42,112,110,21,252,211,227,61,255,112,127,91,1,117,136,161,6,184,78,197,247,94,28,5,212,49,14,94) -IMAGE_DATA(104,127,125,122,132,147,194,238,180,79,184,103,244,209,39,28,29,236,76,199,3,255,251,176,71,64,13,245,225,224,137,192) -IMAGE_DATA(245,193,219,35,129,235,175,94,155,192,117,175,111,19,184,222,127,110,17,184,222,235,54,8,92,239,118,30,8,92,239,184) -IMAGE_DATA(53,2,215,221,118,149,192,245,182,125,71,224,186,221,44,17,184,222,106,220,18,184,94,191,191,36,232,58,172,111,165,84) -IMAGE_DATA(36,224,250,98,126,220,92,157,17,48,63,48,143,160,13,247,0,80,199,248,188,252,44,102,38,201,156,98,201,28,148,196) -IMAGE_DATA(229,242,133,66,79,94,104,55,237,170,2,234,56,80,173,126,61,141,67,29,7,199,24,98,226,139,199,91,206,47,122,28) -IMAGE_DATA(230,214,145,9,13,224,60,227,186,184,110,85,129,235,18,182,142,97,235,62,123,244,165,85,182,164,36,235,36,91,120,166) -IMAGE_DATA(64,209,179,68,47,152,33,163,209,80,181,225,23,223,70,47,73,142,141,199,223,36,198,231,42,104,78,131,230,62,104,141) -IMAGE_DATA(204,247,203,124,191,254,205,247,235,7,175,208,225,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_DATA(120,156,237,150,209,74,2,65,20,134,167,76,173,219,162,158,167,71,243,53,130,46,12,186,8,10,18,10,186,139,133,130) +IMAGE_DATA(32,40,74,211,52,212,93,55,211,108,75,44,235,38,132,109,206,228,145,57,199,221,85,186,90,218,153,229,195,153,243,255) +IMAGE_DATA(59,179,59,115,102,86,177,34,214,69,74,94,122,201,229,114,190,142,96,5,98,150,101,17,116,95,144,206,125,198,99,60) +IMAGE_DATA(113,240,8,179,1,140,39,201,30,145,149,91,32,106,3,68,37,254,172,129,176,143,184,104,97,147,1,252,249,36,200,231) +IMAGE_DATA(243,4,126,18,112,157,251,140,199,120,226,224,49,159,66,227,73,180,199,108,0,227,73,180,199,252,23,28,99,254,11,26) +IMAGE_DATA(79,162,61,242,36,200,138,69,121,45,201,139,39,62,39,46,59,58,10,145,145,47,180,32,175,144,50,181,155,199,49,30) +IMAGE_DATA(247,3,226,126,72,60,204,31,213,63,125,158,128,135,142,186,33,106,144,168,7,11,123,153,121,251,167,133,207,242,230,178) +IMAGE_DATA(216,80,185,148,22,107,42,112,102,21,252,147,163,93,255,96,111,75,1,117,136,161,6,184,78,217,247,94,28,5,212,49) +IMAGE_DATA(14,94,104,127,125,122,132,227,194,206,164,79,184,103,248,209,35,28,238,111,79,198,3,255,251,160,75,64,13,245,65,255) +IMAGE_DATA(137,192,245,254,219,35,129,235,175,94,139,192,117,175,103,19,184,222,123,110,18,184,222,237,212,9,92,239,180,31,8,92) +IMAGE_DATA(111,187,85,2,215,221,86,133,192,245,150,125,71,224,186,221,40,18,184,222,172,223,18,184,94,187,191,36,232,58,172,111) +IMAGE_DATA(185,120,78,192,245,197,252,184,185,58,37,96,126,96,30,65,27,238,1,160,142,241,89,249,185,153,25,39,115,138,37,115) +IMAGE_DATA(80,18,151,74,23,10,61,121,161,221,176,43,10,168,227,64,213,218,245,36,14,117,28,28,99,136,137,207,31,111,58,191) +IMAGE_DATA(232,113,152,91,71,38,52,128,243,140,235,226,186,21,5,174,75,216,58,134,173,251,244,209,151,86,217,146,146,172,146,108) +IMAGE_DATA(225,153,2,69,207,18,189,96,134,12,135,3,213,134,95,124,27,189,36,57,54,26,125,147,24,159,171,160,57,13,154,251) +IMAGE_DATA(160,53,50,223,47,243,253,250,55,223,175,31,122,108,223,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) IMAGE_END_DATA(512, 17) diff --git a/uppsrc/CtrlLib/DlgColor.h b/uppsrc/CtrlLib/DlgColor.h index abad82e84..14494c776 100644 --- a/uppsrc/CtrlLib/DlgColor.h +++ b/uppsrc/CtrlLib/DlgColor.h @@ -95,8 +95,7 @@ private: Color GetColor(int i) const; void Select(); - void DrawFilledFrame(Draw &w, int x, int y, int cx, int cy, Color fcol, Color bcol); - void DrawFilledFrame(Draw &w, Rect &r, Color fcol, Color bcol); + Rect DrawFilledFrame(Draw &w, Rect r, Color fcol, Color bcol); int colori; bool notnull; diff --git a/uppsrc/CtrlLib/DropChoice.h b/uppsrc/CtrlLib/DropChoice.h index 3ae8eda97..6fd2a5d24 100644 --- a/uppsrc/CtrlLib/DropChoice.h +++ b/uppsrc/CtrlLib/DropChoice.h @@ -230,7 +230,6 @@ public: DropList& SetScrollBarStyle(const ScrollBar::Style& s) { list.SetScrollBarStyle(s); return *this; } DropList(); - virtual ~DropList(); }; void Append(DropList& list, const VectorMap& values); diff --git a/uppsrc/CtrlLib/DropList.cpp b/uppsrc/CtrlLib/DropList.cpp index 139d6563e..79100205f 100644 --- a/uppsrc/CtrlLib/DropList.cpp +++ b/uppsrc/CtrlLib/DropList.cpp @@ -19,8 +19,6 @@ DropList::DropList() usewheel = true; } -DropList::~DropList() {} - int DropList::FindKey(const Value& k) const { return key.Find(k); diff --git a/uppsrc/Draw/Font.cpp b/uppsrc/Draw/Font.cpp index 5196be50c..5627630b7 100644 --- a/uppsrc/Draw/Font.cpp +++ b/uppsrc/Draw/Font.cpp @@ -129,7 +129,7 @@ void Font::SyncStdFont() } LLOG("SyncStdFont " << StdFontSize); - SyncUHDMode(); + SyncDPIScale(); } void (*whenSetStdFont)(); @@ -164,6 +164,13 @@ bool Font::std_font_override; void Font::SetDefaultFont(Font font) { LLOG("SetDefaultFont " << font); + int override_scale = Atoi(GetEnv("UPP_SCALE__")); + if(override_scale) { + int cy = decode(override_scale, 3, 18, 4, 24, 6, 36, 12); + for(int i = 0; i < 20; i++) + if(font.Height(cy + i).GetCy() >= cy) + break; + } if(!std_font_override) SetStdFont0(font); } diff --git a/uppsrc/Draw/Image.h b/uppsrc/Draw/Image.h index 4466be17d..200c7b549 100644 --- a/uppsrc/Draw/Image.h +++ b/uppsrc/Draw/Image.h @@ -81,6 +81,7 @@ class ImageBuffer : NoCopy { void InitAttrs(); friend class Image; + friend void iml_ReplaceAll(Image& tgt, const Image& src); public: void SetKind(int k) { kind = k; } @@ -179,7 +180,7 @@ private: friend struct scImageMaker; void SetAuxData(uint64 data); - friend void iml_ReplaceAll(Image& tgt, Image& src); + friend void iml_ReplaceAll(Image& tgt, const Image& src); public: Size GetSize() const { return data ? data->buffer.GetSize() : Size(0, 0); } @@ -274,42 +275,41 @@ Vector UnpackImlDataUncompressed(const String& data); Vector UnpackImlData(const void *ptr, int len); Vector UnpackImlData(const String& d); -enum { - GUI_MODE_NORMAL = 0, - GUI_MODE_DARK = 1, - GUI_MODE_UHD = 2, - GUI_MODE_DARK_UHD = 3, -}; - -enum { +enum { // internal - represents binary flags in imported iml data IML_IMAGE_FLAG_FIXED = 0x1, IML_IMAGE_FLAG_FIXED_COLORS = 0x2, IML_IMAGE_FLAG_FIXED_SIZE = 0x4, IML_IMAGE_FLAG_UHD = 0x8, IML_IMAGE_FLAG_DARK = 0x10, IML_IMAGE_FLAG_S3 = 0x20, + IML_IMAGE_FLAG_QHD = 0x40, + + IML_IMAGE_FLAGS_UNKNOWN = 0xffffffff, // internal - flags are not yet known }; -Image MakeImlImage(const String& id, Function GetRaw, dword global_flags); +Image MakeImlImage(const String& id, Event GetRaw); class Iml { struct IImage : Moveable { std::atomic loaded; Image image; - IImage() { loaded = false; } + IImage() { loaded = false; } }; struct Data : Moveable { const char *data; int len, count; }; - Vector data[4]; // 0 normal, 1 HiDPI - HD, 2 DK - Dark, 3 HDK - HiDPI + dark - VectorMap map; + Vector data; const char **name; dword global_flags = 0; bool premultiply; - Index ex_name[3]; // 0 HiDPI - HD, 1 DK - Dark, 2 HDK - HiDPI + dark + VectorMap map; + Buffer flags; + int img_count = 0; + + int version = 0; void Init(int n); @@ -322,15 +322,16 @@ public: int Find(const String& id) const { return map.Find(id); } void Set(int i, const Image& img); - ImageIml GetRaw(int mode, int i); // tries to get image for mode, can return Null - ImageIml GetRaw(int mode, const String& id); // tries to get image for mode by id, can return Null + ImageIml GetRaw(int i); + dword GetRawFlags(int i); // these methods serve for .iml import Iml(const char **name, int n);//Deprecated - legacy .iml - void AddData(const byte *data, int len, int count, int mode = 0); + void AddData(const byte *data, int len, int count); void AddId(int mode1, const char *name); void Premultiplied() { premultiply = false; } void GlobalFlag(dword f) { global_flags |= f; } + void Version(int v) { version = v; } static void ResetAll(); // clears all .iml caches static void SkinAll(); // reskins all .iml caches diff --git a/uppsrc/Draw/ImageOp.h b/uppsrc/Draw/ImageOp.h index 24def543a..201f76288 100644 --- a/uppsrc/Draw/ImageOp.h +++ b/uppsrc/Draw/ImageOp.h @@ -7,7 +7,7 @@ Image WithHotSpots(const Image& m, Point hotspot, Point hotspot2 = Point(0, 0)); Image WithHotSpots(const Image& m, int x1, int y1, int x2, int y2); Image WithHotSpot(const Image& m, int x1, int y1); -void ScanOpaque(Image& m); +void ScanOpaque(Image& m); void DstSrcOp(ImageBuffer& dest, Point p, const Image& src, const Rect& srect, void (*op)(RGBA *t, const RGBA *s, int n), bool co = false); @@ -266,19 +266,37 @@ Image Upscale2x(const Image& src); Image Downscale2x(const Image& src); Image Downscale6x(const Image& src); -void SetUHDMode(bool b = true); -bool IsUHDMode(); -void SyncUHDMode(); +enum { + DPI_100 = 2, // Normal resolution + DPI_150 = 3, // QHD + DPI_200 = 4, // UHD + DPI_300 = 6, + DPI_600 = 12, // Used for 'master' Image drawn without aliasing +}; -// Image DPI(const Image& m); -Image DPI(const Image& img, int expected); +void SetDPIScale(int scale); +void SyncDPIScale(); -inline int DPI(int a) { return IsUHDMode() ? 2 * a : a; } -inline double DPI(double a) { return IsUHDMode() ? 2 * a : a; } -inline Size DPI(Size sz) { return IsUHDMode() ? 2 * sz : sz; } -inline Size DPI(int cx, int cy) { return Size(DPI(cx), DPI(cy)); } +inline int GetDPIScale() { extern int DPIScaleGlobal_; return DPIScaleGlobal_; } +inline double GetDPIScaleRatio() { extern double DPIScaleGlobalF_; return DPIScaleGlobalF_; } +inline double GetDPIUnScaleRatio() { extern double IDPIScaleGlobalF_; return IDPIScaleGlobalF_; } -inline Image DPI(const Image& a, const Image& b) { return IsUHDMode() ? b : a; } +inline int DPI(int a) { return (GetDPIScale() * a) >> 1; } +inline double DPI(double a) { return GetDPIScaleRatio() * a; } + +inline Size DPI(int cx, int cy) { return Size(DPI(cx), DPI(cy)); } +inline Size DPI(Size sz) { return DPI(sz.cx, sz.cy); } + +Image DPISmartRescale(const Image& src, Size sz); +Image DPISmartRescaleCached(const Image& src, Size sz); + +int ImlFlagsToDPIScale(int imlflags); +int DPIScaleToImlFlags(int dpiscale); + +inline int DPI2(int dpi200val, int dpi100val) { + int scale = GetDPIScale(); + return (dpi200val - dpi100val) * (scale - DPI_100) / 2 + dpi100val; +} struct RGBAV { dword r, g, b, a; diff --git a/uppsrc/Draw/Iml.cpp b/uppsrc/Draw/Iml.cpp index 598b91baf..18ba2e817 100644 --- a/uppsrc/Draw/Iml.cpp +++ b/uppsrc/Draw/Iml.cpp @@ -41,10 +41,13 @@ Vector UnpackImlData(const String& d) return UnpackImlData(~d, d.GetLength()); } -void iml_ReplaceAll(Image& tgt, Image& src) +void iml_ReplaceAll(Image& tgt, const Image& src) { // this very special function replaces all unmodified instances of Image with new content - if(tgt.GetSize() == src.GetSize() && tgt.data) { - tgt.data->buffer = src.data->buffer; + Image h = src; // make sure src has refcount 1, basically 'clone' + ImageBuffer ib = h; + h = ib; + if(tgt.GetSize() == h.GetSize() && tgt.data && src.data) { + tgt.data->buffer = h.data->buffer; tgt.data->NewSerial(); } else @@ -55,6 +58,7 @@ void Iml::Init(int n) { for(int i = 0; i < n; i++) map.Add(name[i]); + flags.Alloc(map.GetCount(), IML_IMAGE_FLAGS_UNKNOWN); } void Iml::Reset() @@ -72,25 +76,181 @@ void Iml::Skin() } } +void Iml::AddData(const byte *s, int len, int count) +{ + Data& d = data.Add(); + d.data = (const char *)s; + d.len = len; + d.count = count; + img_count += count; + data.Shrink(); +} + +void Iml::AddId(int mode1, const char *name) +{ + map.Add(name); +} + static StaticMutex sImlLock; +ImageIml Iml::GetRaw(int i) +{ + int i0 = 0; + for(const Data& d : data) { + if(i >= i0 && i < i0 + d.count) { + ImageIml m = MakeValue( + [&] { + String key; + RawCat(key, d.data); // all is static + return key; + }, + [&](Value& v) { + Vector& m = CreateRawValue>(v); + m = UnpackImlData(d.data, d.len); + ASSERT(m.GetCount() == d.count); + int sz = 0; + int ii = i0; + for(ImageIml& img : m) { + sz += img.image.GetLength(); + if(premultiply) + img.image = Premultiply(img.image); + if(version == 0) // cleanup some bits that are set for legacy reasons + img.flags &= 0x3f; + flags[ii++] = img.flags; + } + return sz; + } + ).To>()[i - i0]; + m.flags |= global_flags; + return m; + } + i0 += d.count; + } + return ImageIml(); +} + void Iml::Set(int i, const Image& img) { // TODO: MT IImage& m = map[i]; - Image h = img; // make sure h has refcount 1, basically 'clone' - ImageBuffer ib = h; - h = ib; - iml_ReplaceAll(m.image, h); + iml_ReplaceAll(m.image, img); m.loaded = true; } +Image MakeImlImage(const String& id, Event GetRawFlags, Function GetRaw) +{ + int scale = GetDPIScale(); + bool dark = IsDarkTheme(); + int best_i = -1; + int exact_scale_i = -1; + int best_dark = 10; // minimize this + int best_scale = -1; // maximize this + int ii = 0; + for(;;) { + dword flags; + String iid; + GetRawFlags(ii, flags, iid); + if(IsNull(iid)) + break; + int q = iid.Find("__"); + if(q > 0) + iid.Trim(q); + if(iid == id) { + bool isdark = flags & IML_IMAGE_FLAG_DARK; + int iscale = ImlFlagsToDPIScale(flags); + + if(isdark == dark && iscale == scale) // found perfect match + return GetRaw(ii).image; + + if(iscale == scale) + exact_scale_i = ii; + + int idark = !!isdark - dark; + if(CombineCompare(best_dark, idark)(iscale, best_scale) > 0) { // prioritize color, then find highest scale + best_i = ii; + best_dark = idark; + best_scale = iscale; + } + } + ii++; + } + + auto AdjustColor0 = [&](const Image& img, dword flags) { // flip light to dark if needed + if(dark && !(flags & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_COLORS|IML_IMAGE_FLAG_DARK))) + return DarkTheme(img); + return img; + }; + + if(best_dark && exact_scale_i >= 0) { // dark color version not found, but we have exact scale + ImageIml m = GetRaw(exact_scale_i); + return AdjustColor0(m.image, m.flags); + } + + if(best_i < 0) + return Null; + + ImageIml best = GetRaw(best_i); + + auto AdjustColor = [&](const Image& m) { // flip light to dark if needed + return best_dark ? AdjustColor0(m, best.flags) : m; + }; + + if(best.flags & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_SIZE)) + return AdjustColor(best.image); + + if(best_scale == DPI_600) { + if(scale == DPI_100) + return AdjustColor(Downscale6x(best.image)); + if(scale == DPI_150) + return AdjustColor(Downscale2x(DownSample2x(best.image))); + if(scale == DPI_200) + return AdjustColor(DownSample3x(best.image)); + if(scale == DPI_300) + return AdjustColor(DownSample2x(best.image)); + } + + return DPISmartRescale(best.image, scale * best.image.GetSize() / best_scale); +} + +Image MakeImlImage(const String& id, Event GetRaw) +{ + return MakeImlImage(id, + [&](int i, dword& flags, String& id) { + ImageIml m; + GetRaw(i, m, id); + flags = m.flags; + }, + [&](int i) -> ImageIml { + ImageIml m; + String id; + GetRaw(i, m, id); + return m; + } + ); + +} + Image Iml::Get(int i) { IImage& m = map[i]; if(!m.loaded) { Mutex::Lock __(sImlLock); if(!m.loaded) { - Image h = MakeImlImage(GetId(i), [&](int mode, const String& id) { return GetRaw(mode, id); }, global_flags); + Image h = MakeImlImage(GetId(i), + [&](int i, dword& rflags, String& id) { + id.Clear(); + rflags = 0; + if(i < img_count) { + id = map.GetKey(i); + if(flags[i] != IML_IMAGE_FLAGS_UNKNOWN) + rflags = flags[i]; + else + rflags = GetRaw(i).flags; + } + }, + [&](int i) -> ImageIml { + return GetRaw(i); + } + ); iml_ReplaceAll(m.image, h); m.loaded = true; } @@ -98,89 +258,26 @@ Image Iml::Get(int i) return m.image; } -ImageIml Iml::GetRaw(int mode, int i) -{ - Mutex::Lock __(sImlLock); - if(data[mode].GetCount()) { - int ii = 0; - while(ii < data[mode].GetCount()) { - const Data& d = data[mode][ii]; - if(i < d.count) { - static const char *cached_data[4]; - static Vector cached[4]; - if(cached_data[mode] != d.data) { // cache single .iml - cached_data[mode] = d.data; - cached[mode] = UnpackImlData(d.data, d.len); - if(premultiply) - for(int i = 0; i < cached[mode].GetCount(); i++) - cached[mode][i].image = Premultiply(cached[mode][i].image); - } - return cached[mode][i]; - } - i -= d.count; - ii++; - } - } - return ImageIml(); +int ImlFlagsToDPIScale(int imlflags) { + int scale = DPI_100; + if(imlflags & IML_IMAGE_FLAG_UHD) + scale = DPI_200; + if(imlflags & IML_IMAGE_FLAG_S3) + scale *= 3; + if(imlflags & IML_IMAGE_FLAG_QHD) + scale++; + return scale; } -ImageIml Iml::GetRaw(int mode, const String& id) -{ - ASSERT(mode >= 0 && mode < 4); - int ii = -1; - if(mode == 0) - ii = map.Find(id); - else - ii = ex_name[mode - 1].Find(id); - return ii >= 0 ? GetRaw(mode, ii) : ImageIml(); -} - -Image MakeImlImage(const String& id, Function GetRaw, dword global_flags) -{ - int mode = IsUHDMode() * GUI_MODE_UHD + IsDarkTheme() * GUI_MODE_DARK; - - const static int mode_candidates[4][4] = { - { GUI_MODE_NORMAL, GUI_MODE_UHD, -1 }, - { GUI_MODE_DARK, GUI_MODE_DARK_UHD, GUI_MODE_NORMAL, GUI_MODE_UHD }, - { GUI_MODE_UHD, GUI_MODE_NORMAL, -1 }, - { GUI_MODE_DARK_UHD, GUI_MODE_DARK, GUI_MODE_UHD, GUI_MODE_NORMAL } - }; - - ImageIml im; - Image original; - const int *candidates = mode_candidates[mode]; - - for(int i = 0; i < 4 && candidates[i] >= 0; i++) { - int cmode = candidates[i]; - auto Mode = [&](dword m, const char *s) { return cmode & m ? String(s) : String(); }; - im = GetRaw(GUI_MODE_NORMAL, id + Mode(GUI_MODE_UHD, "__UHD") + Mode(GUI_MODE_DARK, "__DARK")); - if(IsNull(im.image)) - im = GetRaw(cmode, id); // try alternative iml - if(!IsNull(im.image)) { - original = im.image; - if(im.flags & IML_IMAGE_FLAG_S3) - im.image = DownSample3x(im.image); - break; - } - } - - if(IsNull(im.image)) - return Null; - - if(!(mode & GUI_MODE_UHD) && (im.flags & IML_IMAGE_FLAG_UHD) && !((im.flags | global_flags) & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_SIZE))) { - if(im.flags & IML_IMAGE_FLAG_S3) - im.image = Downscale6x(original); - else - im.image = Downscale2x(im.image); - } - if((mode & GUI_MODE_UHD) && !(im.flags & IML_IMAGE_FLAG_UHD) && !((im.flags | global_flags) & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_SIZE))) - im.image = Upscale2x(im.image); - if((mode & GUI_MODE_DARK) && !(im.flags & IML_IMAGE_FLAG_DARK) && !((im.flags | global_flags) & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_COLORS))) - im.image = DarkTheme(im.image); - - ScanOpaque(im.image); - - return im.image; +int DPIScaleToImlFlags(int dpiscale) { + return dpiscale == DPI_600 ? IML_IMAGE_FLAG_S3|IML_IMAGE_FLAG_UHD + : get_i(dpiscale, 0, 0, + 0, // DPI_100 + IML_IMAGE_FLAG_QHD, // DPI_150 + IML_IMAGE_FLAG_UHD, // DPI_200 + IML_IMAGE_FLAG_UHD|IML_IMAGE_FLAG_QHD, // 'DPI_250' - not used + IML_IMAGE_FLAG_S3 // DPI_300 + ); } Iml::Iml(const char **name, int n) @@ -190,20 +287,6 @@ Iml::Iml(const char **name, int n) Init(n); } -void Iml::AddData(const byte *s, int len, int count, int mode) -{ - Data& d = data[mode].Add(); - d.data = (const char *)s; - d.len = len; - d.count = count; - data[mode].Shrink(); -} - -void Iml::AddId(int mode1, const char *name) -{ - ex_name[mode1].Add(name); -} - void Iml::ResetAll() { for(int i = 0; i < GetImlCount(); i++) diff --git a/uppsrc/Draw/Uhd.cpp b/uppsrc/Draw/Uhd.cpp index ab8c72132..a65dd87a1 100644 --- a/uppsrc/Draw/Uhd.cpp +++ b/uppsrc/Draw/Uhd.cpp @@ -6,21 +6,17 @@ namespace Upp { // should eventually lead you to a forum topic where he made the algorithm public: // http://board.byuu.org/viewtopic.php?f=10&t=2248 -namespace Upscale2x_helper { - int d(RGBA c1, RGBA c2) - { +Image Upscale2x_(const Image& src) +{ + auto d = [](RGBA c1, RGBA c2) -> int { int r = abs(c1.r - c2.r); int g = abs(c1.g - c2.g); int b = abs(c1.b - c2.b); return 48 * (r * 299 + g * 587 + b * 114) + 7 * (r * -169 + g * -331 + b * 500) + 6 * (r * 500 + g * -419 + b * -81); - } -}; + }; -Image Upscale2x_(const Image& src) -{ - using namespace Upscale2x_helper; Size isz = src.GetSize(); ImageBuffer dst; dst.Create(2 * isz); @@ -102,18 +98,60 @@ Image Upscale2x(const Image& src) return Image(h); } -Image Downscale2x(const Image& src) +Image DPIRescale(const Image& src, Size sz) { if(IsNull(src)) return src; - Size s2 = src.Get2ndSpot(); // see above... - Image m = RescaleFilter(src, src.GetSize() / 2, s2.cx > 0 || s2.cy > 0 ? FILTER_BILINEAR : FILTER_LANCZOS3); + + Size s2 = src.Get2ndSpot(); + Size sz0 = src.GetSize(); + if(sz0 == sz) + return src; + + // When 2nd spot is defined, we are likely rescaling Chameleon item (e.g. button image) + // in that case, filtering by smarter Lanczos could lead to artifacts - stay BILINEAR + Image m; + if(s2.cx > 0 || s2.cy > 0) + m = RescaleFilter(src, sz, FILTER_BILINEAR); + else + if(sz.cx * sz.cy > 128*128) + m = CoRescaleFilter(src, sz, FILTER_LANCZOS3); + else + m = RescaleFilter(src, sz, FILTER_LANCZOS3); + ImageBuffer h(m); - h.SetHotSpot(s2 / 2); - h.Set2ndSpot(src.Get2ndSpot() / 2); + h.SetHotSpot(s2 * sz / sz0); + h.Set2ndSpot(src.Get2ndSpot() * sz / sz0); return Image(h); } +Image DPISmartRescale(const Image& src, Size sz) +{ + Image m = src; + for(;;) { + Size isz = m.GetSize(); + if(isz.cx * isz.cy == 0) + return Null; + if(isz.cx >= sz.cx && isz.cy >= sz.cy) + break; + m = Upscale2x(m); + } + return DPIRescale(m, sz); +} + +Image DPISmartRescaleCached(const Image& src, Size sz) +{ + return MakeImage( + [&] { StringBuffer s; RawCat(s, src.GetSerialId()); RawCat(s, sz); return (String)s; }, + [&] { return DPISmartRescale(src, sz); } + ); +} + +Image Downscale2x(const Image& src) +{ + return DPIRescale(src, src.GetSize() / 2); +} + Image Downscale6x(const Image& src) { if(IsNull(src)) @@ -126,31 +164,29 @@ Image Downscale6x(const Image& src) return Image(h); } -static bool sUHDMode; +int DPIScaleGlobal_ = 2; +double DPIScaleGlobalF_ = 1; +double IDPIScaleGlobalF_ = 1; -void SetUHDMode(bool b) +void SetDPIScale(int scale) { Iml::ResetAll(); - sUHDMode = b; + DPIScaleGlobal_ = scale; + DPIScaleGlobalF_ = 0.5 * scale; + IDPIScaleGlobalF_ = 1 / DPIScaleGlobalF_; } -bool IsUHDMode() +void SyncDPIScale() { - return sUHDMode; -} - -void SyncUHDMode() -{ - bool uhd = GetStdFontCy() > 24; - if(uhd != IsUHDMode()) - SetUHDMode(uhd); -} - -Image DPI(const Image& img, int expected) -{ - if(img.GetSize().cy <= expected && IsUHDMode()) - return AdjustImage(img, Upscale2x); - return img; + int fcy = GetStdFontCy(); + int scale = clamp((fcy + 3) / 8, 2, 5); + if(scale == 5) + scale = DPI_300; + int override_scale = Atoi(GetEnv("UPP_SCALE__")); + if(override_scale) + scale = override_scale; + if(scale != GetDPIScale()) + SetDPIScale(scale); } }; \ No newline at end of file diff --git a/uppsrc/Draw/iml_header.h b/uppsrc/Draw/iml_header.h index be14e385c..c9e69d035 100644 --- a/uppsrc/Draw/iml_header.h +++ b/uppsrc/Draw/iml_header.h @@ -1,10 +1,16 @@ //#BLITZ_APPROVE +#ifdef VERSION +#undef VERSION +#endif + #define IMAGE_META(k, v) +#define IMAGE_VERSION(v) #define IMAGE_SCAN(s) #define IMAGE_PACKED(n, d) #define PREMULTIPLIED +#define VERSION(x) #define IMAGE_BEGIN_DATA #define IMAGE_DATA(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,ba,bb,bc,bd,be,bf) #define IMAGE_END_DATA(n, c) @@ -50,6 +56,7 @@ public: #undef IMAGE_SCAN #undef IMAGE_PACKED #undef IMAGE_META +#undef IMAGE_VERSION #undef IMAGE_BEGIN_DATA #undef IMAGE_END_DATA @@ -58,4 +65,8 @@ public: #ifndef IMAGE_KEEP #undef IMAGECLASS #undef IMAGEFILE + +#undef PREMULTIPLIED +#undef VERSION + #endif diff --git a/uppsrc/Draw/iml_source.h b/uppsrc/Draw/iml_source.h index 60f81c88e..54c0e6838 100644 --- a/uppsrc/Draw/iml_source.h +++ b/uppsrc/Draw/iml_source.h @@ -1,8 +1,14 @@ //#BLITZ_APPROVE +#ifdef VERSION +#undef VERSION +#endif + +#define VERSION(v) +#define PREMULTIPLIED + #define IMAGE_META(k, v) -#define PREMULTIPLIED #define IMAGE_ID(n) #define IMAGE_BEGIN_DATA #define IMAGE_END_DATA(n, c) @@ -65,30 +71,6 @@ UPP::Iml& IMAGECLASS::Iml() { #include IMAGEFILE - #ifdef IMAGEFILE_DARK - #undef IMAGE_ID - #undef IMAGE_END_DATA - #define IMAGE_ID(n) iml.AddId(0, #n); - #define IMAGE_END_DATA(n, c) }; iml.AddData(data, n, c, 1); } - #include IMAGEFILE_DARK - #endif - - #ifdef IMAGEFILE_UHD - #undef IMAGE_ID - #undef IMAGE_END_DATA - #define IMAGE_ID(n) iml.AddId(1, #n); - #define IMAGE_END_DATA(n, c) }; iml.AddData(data, n, c, 2); } - #include IMAGEFILE_UHD - #endif - - #ifdef IMAGEFILE_DARK_UHD - #undef IMAGE_ID - #undef IMAGE_END_DATA - #define IMAGE_ID(n) iml.AddId(2, #n); - #define IMAGE_END_DATA(n, c) }; iml.AddData(data, n, c, 3); } - #include IMAGEFILE_DARK_UHD - #endif - #undef IMAGE_BEGIN_DATA #undef IMAGE_END_DATA #undef IMAGE_DATA @@ -100,10 +82,13 @@ UPP::Iml& IMAGECLASS::Iml() { #undef PREMULTIPLIED #define PREMULTIPLIED iml.Premultiplied(); + #undef VERSION + #define VERSION(v) iml.Version(v); #include IMAGEFILE #undef PREMULTIPLIED #define PREMULTIPLIED - + #undef VERSION + #define VERSION(v) } return iml; } @@ -176,6 +161,9 @@ static COMBINE(IMAGECLASS, __Reg) COMBINE(IMAGECLASS, ___Reg); #undef IMAGE_META +#undef PREMULTIPLIED +#undef VERSION + #ifdef IMAGEFILE_UHD #undef IMAGEFILE_UHD #endif diff --git a/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp b/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp index 26c1e3293..18016096c 100644 --- a/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp +++ b/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp @@ -317,5 +317,16 @@ CKWISE. Flip mode values are compatible with Raster`::GetOrientation and are equal to EXIF orientation `- 1. This function is intended to flip Image to correct orientation (usually JPEG from digital camera).&] -[s2;%% &] +[s3; &] +[s4; &] +[s5;:Upp`:`:DPISmartRescale`(const Image`&`,Size`): Image [* DPISmartRescale]([@(0.0.255) c +onst] Image[@(0.0.255) `&] [*@3 src], Size [*@3 sz])&] +[s2;%% Intended to rescale `"not fitting`" icons to current GUI scaling, +using variety of `"smart`" methods (xBR upscaling, Lancosz 3, +bilienear downscaling) to maintain the acceptable appearance.&] +[s3; &] +[s4; &] +[s5;:Upp`:`:DPISmartRescaleCached`(const Image`&`,Size`): Image [* DPISmartRescaleCache +d]([@(0.0.255) const] Image[@(0.0.255) `&] [*@3 src], Size [*@3 sz])&] +[s2;%% Same as DPISmartRescale, with added caching of results.&] [s0;%% ]] \ No newline at end of file diff --git a/uppsrc/Draw/srcdoc.tpp/UhdAndDarkTheme_en-us.tpp b/uppsrc/Draw/srcdoc.tpp/UhdAndDarkTheme_en-us.tpp index bbe0de862..f7bf0ce1e 100644 --- a/uppsrc/Draw/srcdoc.tpp/UhdAndDarkTheme_en-us.tpp +++ b/uppsrc/Draw/srcdoc.tpp/UhdAndDarkTheme_en-us.tpp @@ -35,14 +35,11 @@ adjustment]&] files]&] [s0; &] [s3;:1: GUI mode detection&] -[s5; UHD mode is activated when standard GUI font is larger than -24 pixels. Dark theme mode is activated if [* IsDark]([* SColorPaper]()), +[s5; Various scaling modes are activated based standard GUI font +size. Dark theme mode is activated if [* IsDark]([* SColorPaper]()), which means that grayscale value of default background is less -than 80. Note that both modes create 4 combinations in total -`- standard resolution with light theme, standard resolution -with dark theme, UHD resolution with light theme, UHD resolution -with dark theme.&] -[s5; [* IsUHDMode() ]and [* IsDarkTheme() ]functions return respective +than 80.&] +[s5; [* GetDPIScale() ]and [* IsDarkTheme() ]functions return respective current GUI status.&] [s3;:2: Scaling GUI for actual GUI font and UHD resolution&] [s5; U`+`+ coordinates in drawing operations are always in real pixels @@ -75,11 +72,11 @@ height.] :: [s0;b42;a42; int [* DPI](int a);&] [s0;b42;a42; double [* DPI](double a);&] [s0;b42;a42; Size [* DPI](Size sz);&] -[s0;b42;a42; Size [* DPI](int cx, int cy);] -:: [s5; If UHD mode is active, returns the input argument multiplied -by 2, otherwise returns it unchanged.] -:: [s0;b42;a42; Image [* DPI](const Image`& a, const Image`& b);] -:: [s5; Returns [* b] if UHD is active, [* a] otherwise.]}}&] +[s0;b42;a42; Size [* DPI](int cx, int cy);&] +[s0;b42;a42; [* GetDPIUnScaleRatio]()&] +[s0;b42;a42; [* GetDPIUnScaleRatio]()] +:: [s5; Scales the entity by current scaling factor that is also used +to scale IML images.]}}&] [s5; Usually [* DPI ]functions are used if the value is Image related, `'Z`' functions if it is text size related.&] [s3;:3: Color adjustment&] @@ -92,24 +89,24 @@ the color with [* DarkTheme] only if dark theme mode is currently active.&] [s3;:4: Iml files&] [s5; Iml files most often contain images that are used in GUI interface. -Obviously, these images must be usually different for any of -4 GUI modes.&] -[s5; .iml should always contain images for either standard or UHD -resolution and the light theme. These images are used to define -the set of icons.&] -[s5; U`+`+ then uses smart algorithms to convert such images for -the current GUI mode. These work acceptably well in most cases.&] -[s5; Developer might decide to provide dedicated variants for any -image for any target mode which will be used instead of converted -basic icon. Such variant can be either placed into the same .iml -file or into separate .iml file.&] -[s0; The complete control of the process is available in image details +The scale for which the image is designed for is part of Image +attributes.&] +[s5; In addition to 100%, 150%, 200% and 300% variants, there is +also 600% [/ master] variant. The recommended approach is to use +this master scale and draw images without anti`-aliasing (which +is simpler to edit) `- anti`-aliasing then results from downscaling +the image to the required resolution.&] +[s5; When fetching Image from Iml, exact scale match has the first +priority, then 600% master. When not available, the most detailed +(highest scale) Image becomes the sources and it is rescaled +to the target resolution (using somewhat smart methods trying +to make it `"look acceptable`".&] +[s5; The complete control of the process is available in image details in the icon designer:&] [s0; &] -[s0;= &] [s0;= -@@image:924&1482 -(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) +@@image:1347&2746 +(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) &] [s0; &] [ {{2001:7999^ [s5; Fixed] @@ -120,13 +117,16 @@ image looks better in the dark mode without actually converting it.] :: [s5; Fixed size] :: [s5; Image is never scaled to match current mode.] -:: [s5; UHD variant] -:: [s5; Image is variant for UHD mode.] +:: [s5; 600% (master)] +:: [s5; Use this if possible...] +:: [s0;b42;a42; 100% HD&] +[s5;po 150% QHD&] +[s5;po 200% UHD&] +[s5;po 300% XHD] +:: [s5; Image is designed for specific resolution. Developer can provide +multiple variants.] :: [s5; Dark variant] :: [s5; Image is variant for Dark theme.] -:: [s5; Supersampled 3x] -:: [s5; Image is downsampled 3x before use `- this simplifies design -of antialiased images as images are drawn and stored as unaliased.] :: [s5; Export...] :: [s5; [/ This is unrelated to UHD / Dark theme mode, but if this is checked, the Image is exported as .ico and .png files. This is diff --git a/uppsrc/ide/About.cpp b/uppsrc/ide/About.cpp index cd51c5754..9b02718e5 100644 --- a/uppsrc/ide/About.cpp +++ b/uppsrc/ide/About.cpp @@ -135,8 +135,10 @@ Size SplashCtrl::MakeLogo(Ctrl& parent, Array& ctrl, bool splash) #endif if(items.GetCount()) h << classes.GetCount() << " classes, " << items.GetCount() << " items\n"; - if(IsUHDMode()) - h << "UHD "; + + int scale = GetDPIScale(); + h << decode(scale, DPI_150, "QHD ", DPI_200, "UHD ", DPI_300, "XHD ", + AsString(50 * scale) + "% "); #ifdef GUI_GTK if(Ctrl::IsXWayland()) diff --git a/uppsrc/ide/IconDes/Bar.cpp b/uppsrc/ide/IconDes/Bar.cpp index 9404217ef..738ec123e 100644 --- a/uppsrc/ide/IconDes/Bar.cpp +++ b/uppsrc/ide/IconDes/Bar.cpp @@ -113,12 +113,6 @@ void IconDes::SettingBar(Bar& bar) { using namespace IconDesKeys; Slot *c = IsCurrent() ? &Current() : NULL; - bar.Add("Show UHD/Dark syntetics", IconDesImg::ShowOther(), - [=] { show_synthetics = !show_synthetics; show_downscaled = false; SyncShow(); SetBar(); }) - .Check(show_synthetics); - bar.Add("Show downscaled", IconDesImg::ShowSmall(), - [=] { show_downscaled = !show_downscaled; show_synthetics = false; SyncShow(); SetBar(); }) - .Check(show_downscaled); bar.Add("Show secondary grid", IconDesImg::grid2(), [=] { show_grid2 = !show_grid2; Refresh(); SetBar(); }) .Check(show_grid2); @@ -340,14 +334,14 @@ void IconDes::SerializeSettings(Stream& s) SetBar(); if(version >= 2) s % ImgFile(); + bool b = false; if(version >= 3) { - bool b = false; - s % b % show_downscaled; + s % b; } if(version >= 4) s % paste_mode; if(version >= 5) - s % show_synthetics; + s % b; if(version >= 6) s % show_grid2; if(version >= 7) diff --git a/uppsrc/ide/IconDes/IconDes.cpp b/uppsrc/ide/IconDes/IconDes.cpp index 55df85a29..2bf0edef0 100644 --- a/uppsrc/ide/IconDes/IconDes.cpp +++ b/uppsrc/ide/IconDes/IconDes.cpp @@ -56,8 +56,6 @@ void IconDes::SyncShow() Slot& c = Current(); iconshow.image = c.image; iconshow.flags = c.flags; - iconshow.show_downscaled = show_downscaled; - iconshow.show_synthetics = show_synthetics; ilist.Set(2, RawToValue(MakeTuple(c.image, c.flags))); } iconshow.Refresh(); diff --git a/uppsrc/ide/IconDes/IconDes.h b/uppsrc/ide/IconDes/IconDes.h index 7a550b5e8..2ebcda780 100644 --- a/uppsrc/ide/IconDes/IconDes.h +++ b/uppsrc/ide/IconDes/IconDes.h @@ -193,8 +193,6 @@ private: bool doselection = false; bool selectrect = false; int paste_mode; - bool show_synthetics = true; - bool show_downscaled = false; bool show_grid2 = true; bool antialiased = false; int fill_type = 0; diff --git a/uppsrc/ide/IconDes/IconDes.iml b/uppsrc/ide/IconDes/IconDes.iml index 1a4f347f8..51923a2f7 100644 --- a/uppsrc/ide/IconDes/IconDes.iml +++ b/uppsrc/ide/IconDes/IconDes.iml @@ -57,8 +57,6 @@ IMAGE_ID(ResizeUp2__UHD) IMAGE_ID(ResizeUp__UHD) IMAGE_ID(ResizeDown2__UHD) IMAGE_ID(ResizeDown__UHD) -IMAGE_ID(ShowOther) -IMAGE_ID(ShowSmall) IMAGE_ID(Rescale__UHD) IMAGE_ID(LargeImage) IMAGE_ID(LargeImage__UHD) @@ -1759,26 +1757,22 @@ IMAGE_DATA(66,8,33,132,16,66,8,33,100,15,126,0,207,240,237,34,0,0,0,0,0,0,0,0,0, IMAGE_END_DATA(896, 1) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,150,237,117,194,48,12,69,51,2,3,116,128,142,197,40,217,156,158,20,104,243,225,196,142,45,231,217,232,222) -IMAGE_DATA(30,255,1,164,72,247,37,208,225,54,220,134,10,60,2,231,249,70,128,96,253,248,60,95,207,247,23,245,195,56,62,134) -IMAGE_DATA(215,95,205,250,169,118,183,254,213,99,183,190,108,127,21,161,185,163,243,207,246,88,120,143,250,219,230,208,68,253,188,54) -IMAGE_DATA(90,191,188,15,202,235,203,252,151,81,233,203,64,69,76,232,227,224,115,191,175,175,131,220,9,53,233,245,88,253,251,198) -IMAGE_DATA(205,169,255,171,61,168,95,124,102,76,219,127,83,19,174,223,247,159,86,191,185,201,207,214,175,31,178,131,189,98,249,195) -IMAGE_DATA(139,239,251,112,23,92,118,10,178,169,95,68,71,188,221,147,193,245,172,221,147,193,117,236,185,39,131,250,196,220,147,65) -IMAGE_DATA(61,82,221,147,129,61,103,221,147,129,29,185,238,201,160,156,82,247,100,144,143,149,123,50,56,143,181,123,50,72,167,150) -IMAGE_DATA(123,50,136,83,219,61,25,236,115,149,123,50,216,114,181,123,50,248,71,229,158,12,244,238,61,103,160,118,78,6,249,224) -IMAGE_DATA(80,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11) -IMAGE_DATA(254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,11,254,181) -IMAGE_DATA(224,95,11,254,181,224,95,11,254,181,224,95,11,254,181,224,95,203,167,250,239,101,135,79,246,223,195,30,159,238,191,245) -IMAGE_DATA(93,60,248,111,121,31,47,254,91,221,201,147,255,218,123,229,244,247,230,191,214,110,185,189,61,250,183,222,175,164,175,87) -IMAGE_DATA(255,86,59,150,246,244,236,191,116,79,139,126,222,253,231,238,106,213,11,255,231,247,181,204,18,255,231,118,182,126,150,240) -IMAGE_DATA(159,190,119,141,239,50,252,167,237,94,210,143,83,150,129,122,22,175,7,247,28,239,135,103,64,235,158,223,0,189,251,51) -IMAGE_DATA(25,88,253,159,214,59,214,238,83,251,90,205,217,59,53,220,167,244,182,154,179,119,106,185,143,245,183,154,179,119,106,186) -IMAGE_DATA(63,186,134,213,156,189,83,219,253,222,117,172,230,236,157,43,220,135,174,101,53,103,239,92,229,126,125,61,171,57,123,231) -IMAGE_DATA(74,247,243,107,230,212,120,243,223,18,61,204,152,67,47,123,245,50,231,89,122,217,201,139,255,22,102,176,62,45,211,202) -IMAGE_DATA(156,30,221,79,180,52,167,55,247,19,173,205,233,201,125,171,224,94,15,238,245,224,94,15,238,245,224,94,15,238,245,224) -IMAGE_DATA(94,15,238,67,252,0,20,177,194,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) -IMAGE_END_DATA(608, 3) +IMAGE_DATA(120,156,237,214,81,78,195,48,16,0,209,30,135,99,245,254,151,40,170,16,2,149,134,36,246,218,227,205,206,72,249,196) +IMAGE_DATA(241,190,85,91,62,238,183,251,109,126,143,199,215,99,243,251,182,119,7,243,123,181,119,7,243,218,178,119,7,227,219,179) +IMAGE_DATA(119,7,227,58,106,239,14,226,59,107,239,14,226,106,181,119,7,253,245,218,187,131,246,162,236,221,193,249,162,237,221,193) +IMAGE_DATA(241,70,217,187,131,253,70,219,187,131,237,102,217,187,131,191,205,182,119,7,63,81,246,238,128,183,175,188,3,218,220,29) +IMAGE_DATA(180,167,33,155,254,108,250,179,233,207,166,63,155,254,108,250,179,233,207,166,63,155,254,108,250,179,233,207,166,63,155,254) +IMAGE_DATA(108,250,179,233,207,166,63,155,254,108,250,179,233,207,166,63,155,254,108,250,179,233,207,166,63,155,254,108,250,179,233,207) +IMAGE_DATA(166,63,155,254,108,250,179,233,207,166,63,219,85,253,179,204,112,101,255,12,115,92,221,127,245,89,42,248,175,60,79,21) +IMAGE_DATA(255,85,103,170,228,63,122,174,150,243,171,249,143,154,173,245,236,138,254,209,243,245,156,91,213,63,106,198,222,51,43,251) +IMAGE_DATA(247,206,25,113,94,117,255,214,89,163,206,210,255,252,188,145,187,212,255,220,204,209,159,37,253,143,207,61,226,187,76,255) +IMAGE_DATA(99,179,247,156,231,211,183,3,250,46,85,31,237,125,170,63,126,6,88,123,127,3,120,251,51,59,136,250,63,45,123,209) +IMAGE_DATA(246,71,207,141,186,103,246,70,216,31,57,59,234,158,217,27,101,191,119,126,212,61,179,55,210,254,191,119,68,221,51,123) +IMAGE_DATA(163,237,183,222,19,117,207,236,205,176,127,247,174,168,123,102,111,150,253,235,251,162,238,153,189,153,246,191,223,217,242,55) +IMAGE_DATA(213,252,87,42,195,29,91,202,50,87,150,123,158,45,203,76,85,252,87,184,67,244,179,114,171,220,179,162,253,179,149,238) +IMAGE_DATA(89,205,254,217,106,247,172,100,191,106,218,243,105,207,167,61,159,246,124,218,243,105,207,167,61,159,246,239,250,4,229,46) +IMAGE_DATA(163,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(480, 1) IMAGE_BEGIN_DATA IMAGE_DATA(120,156,237,157,9,92,86,85,250,199,207,125,87,246,237,69,92,0,5,4,193,93,92,80,113,65,54,21,68,4,100,113) diff --git a/uppsrc/ide/IconDes/IconDes.lay b/uppsrc/ide/IconDes/IconDes.lay index c26755da1..d66732ca0 100644 --- a/uppsrc/ide/IconDes/IconDes.lay +++ b/uppsrc/ide/IconDes/IconDes.lay @@ -1,20 +1,20 @@ -LAYOUT(ImageLayout, 168, 252) +LAYOUT(ImageLayout, 168, 328) ITEM(Upp::Label, dv___0, SetLabel(t_("Name")).LeftPosZ(8, 40).TopPosZ(8, 19)) ITEM(Upp::EditString, name, LeftPosZ(52, 108).TopPosZ(8, 19)) ITEM(Upp::Label, dv___2, SetLabel(t_("Size")).LeftPosZ(8, 40).TopPosZ(32, 19)) ITEM(Upp::EditIntSpin, cx, Min(1).Max(8192).LeftPosZ(52, 44).TopPosZ(32, 19)) ITEM(Upp::Label, dv___4, SetLabel(t_("x")).LeftPosZ(100, 12).TopPosZ(32, 16)) ITEM(Upp::EditIntSpin, cy, Min(1).Max(8192).LeftPosZ(116, 44).TopPosZ(32, 19)) - ITEM(Upp::Option, fixed, SetLabel(t_("Fixed")).LeftPosZ(8, 156).TopPosZ(56, 16)) - ITEM(Upp::Option, fixed_colors, SetLabel(t_("Fixed colors")).LeftPosZ(12, 156).TopPosZ(72, 16)) - ITEM(Upp::Option, fixed_size, SetLabel(t_("Fixed size")).LeftPosZ(12, 156).TopPosZ(88, 16)) - ITEM(Upp::Option, uhd, SetLabel(t_("UHD variant")).LeftPosZ(8, 156).TopPosZ(112, 16)) - ITEM(Upp::Option, dark, SetLabel(t_("Dark variant")).LeftPosZ(8, 156).TopPosZ(128, 16)) - ITEM(Upp::Option, s3, SetLabel(t_("Supersampled 3x")).LeftPosZ(8, 156).TopPosZ(152, 16)) - ITEM(Upp::Option, exp, SetLabel(t_("Export as icon.ico and .png")).LeftPosZ(8, 160).TopPosZ(176, 16)) - ITEM(Upp::Button, ok, SetLabel(t_("OK")).LeftPosZ(28, 64).TopPosZ(220, 24)) - ITEM(Upp::Button, cancel, SetLabel(t_("Cancel")).LeftPosZ(96, 64).TopPosZ(220, 24)) - ITEM(Upp::Label, estimated_size, SetAlign(Upp::ALIGN_RIGHT).LeftPosZ(8, 156).TopPosZ(196, 19)) + ITEM(Upp::Option, fixed, SetLabel(t_("Fixed")).LeftPosZ(8, 156).TopPosZ(60, 16)) + ITEM(Upp::Option, fixed_colors, SetLabel(t_("Fixed colors")).LeftPosZ(12, 156).TopPosZ(76, 16)) + ITEM(Upp::Option, fixed_size, SetLabel(t_("Fixed size")).LeftPosZ(12, 156).TopPosZ(92, 16)) + ITEM(Upp::Option, dark, SetLabel(t_("Dark variant")).LeftPosZ(8, 156).TopPosZ(228, 16)) + ITEM(Upp::Option, exp, SetLabel(t_("Export as icon.ico and .png")).LeftPosZ(8, 160).TopPosZ(248, 16)) + ITEM(Upp::Button, ok, SetLabel(t_("OK")).LeftPosZ(28, 64).TopPosZ(296, 24)) + ITEM(Upp::Button, cancel, SetLabel(t_("Cancel")).LeftPosZ(96, 64).TopPosZ(296, 24)) + ITEM(Upp::Label, estimated_size, SetAlign(Upp::ALIGN_RIGHT).LeftPosZ(8, 156).TopPosZ(272, 19)) + ITEM(Upp::LabelBox, dv___14, SetLabel(t_("Target resolution")).LeftPosZ(8, 152).TopPosZ(112, 108)) + ITEM(Upp::Switch, scale, SetLabel(t_("600% (master)\n100% HD\n150% QHD\n200% UHD\n300% XHD")).LeftPosZ(16, 140).TopPosZ(132, 84)) END_LAYOUT LAYOUT(ImageSizeLayout, 168, 68) diff --git a/uppsrc/ide/IconDes/ImlFile.cpp b/uppsrc/ide/IconDes/ImlFile.cpp index 3069728ac..37037a616 100644 --- a/uppsrc/ide/IconDes/ImlFile.cpp +++ b/uppsrc/ide/IconDes/ImlFile.cpp @@ -46,9 +46,8 @@ void AlphaImageInfo::Serialize(Stream& stream) stream % size % hotspot % encoding; } -void ScanIML(CParser& parser, Array& out_images, - VectorMap& out_settings) -{ +void ScanIML(CParser& parser, Array& out_images, VectorMap& out_settings) +{ // This is for backward compatibility with the very old iml format String name, bid; bool exp = false; while(!parser.IsEof()) @@ -189,6 +188,12 @@ bool LoadIml(const String& data, Array& img, int& format) format = 0; try { bool premultiply = !p.Id("PREMULTIPLIED"); + int version = 0; + if(p.Id("VERSION")) { + p.Char('('); + version = p.ReadNumber(); + p.Char(')'); + } Vector name; Vector exp; while(p.Id("IMAGE_ID")) { @@ -242,10 +247,19 @@ bool LoadIml(const String& data, Array& img, int& format) for(int i = 0; i < count; i++) { ImlImage& c = img.Add(); (ImageIml &)c = m[i]; + if(version == 0) + c.flags &= ~IML_IMAGE_FLAG_QHD; c.name = name[ii]; c.exp = exp[ii++]; c.name.TrimEnd("__DARK"); c.name.TrimEnd("__UHD"); + c.name.TrimEnd("__100"); + c.name.TrimEnd("__150"); + c.name.TrimEnd("__200"); + c.name.TrimEnd("__250"); + c.name.TrimEnd("__300"); + c.name.TrimEnd("__350"); + c.name.TrimEnd("__600"); if(premultiply) c.image = Premultiply(c.image); } @@ -272,16 +286,16 @@ bool LoadIml(const String& data, Array& img, int& format) String SaveIml(const Array& iml, int format, const String& eol) { StringStream out; out << "PREMULTIPLIED" << eol; - Index names; + out << "VERSION(20260403)" << eol; Index saved_names; for(int i = 0; i < iml.GetCount(); i++) { const ImlImage& c = iml[i]; - names.FindAdd(c.name); out << "IMAGE_ID(" << c.name; - if((c.flags & (IML_IMAGE_FLAG_UHD|IML_IMAGE_FLAG_DARK)) == 0) + int scale = ImlFlagsToDPIScale(c.flags); + if(saved_names.Find(c.name) < 0) saved_names.FindAdd(c.name); - if(c.flags & IML_IMAGE_FLAG_UHD) - out << "__UHD"; + else + out << "__" << AsString(scale * 50); if(c.flags & IML_IMAGE_FLAG_DARK) out << "__DARK"; out << ")"; @@ -290,16 +304,12 @@ String SaveIml(const Array& iml, int format, const String& eol) { out << eol; } - for(String id : names) // allow UHD versions to be downscaled - if(saved_names.Find(id) < 0) - out << "IMAGE_ID(" << id << ")" << eol; - int ii = 0; while(ii < iml.GetCount()) { int bl = 0; int bn = 0; Vector bimg; - while(bl < 4096 && ii < iml.GetCount()) { + while(ii < iml.GetCount() && (bl == 0 || bl + (int)iml[ii].image.GetLength() < 65536)) { const ImlImage& c = iml[ii++]; ImageIml& m = bimg.Add(); m.image = c.image; diff --git a/uppsrc/ide/IconDes/List.cpp b/uppsrc/ide/IconDes/List.cpp index 3825ba18d..2c9cfd01d 100644 --- a/uppsrc/ide/IconDes/List.cpp +++ b/uppsrc/ide/IconDes/List.cpp @@ -13,12 +13,10 @@ String IconDes::FormatImageName(const Slot& c) if(c.flags & IML_IMAGE_FLAG_FIXED_SIZE) r << " Sz"; } - if(c.flags & IML_IMAGE_FLAG_UHD) - r << " HD"; - if(c.flags & IML_IMAGE_FLAG_DARK) - r << " Dk"; - if(c.flags & IML_IMAGE_FLAG_S3) - r << " S3"; + + int scale = ImlFlagsToDPIScale(c.flags); + r << decode(scale, DPI_100, " 100%", DPI_150, " 150%", DPI_200, " 200%", DPI_300, " 300%", ""); + if(c.exp) r << " X"; return r; @@ -58,11 +56,6 @@ void IconDes::GoTo(int q) ilist.FindSetCursor(q); } -static int sCharFilterCid(int c) -{ - return IsAlNum(c) || c == '_' ? c : 0; -} - void IconDes::PlaceDlg(TopWindow& dlg) { Rect r = ilist.GetScreenRect(); @@ -73,8 +66,9 @@ void IconDes::PlaceDlg(TopWindow& dlg) void IconDes::PrepareImageDlg(WithImageLayout& dlg) { CtrlLayoutOKCancel(dlg, "New image"); - dlg.cx <<= 16; - dlg.cy <<= 16; + dlg.cx <<= 96; + dlg.cy <<= 96; + dlg.scale <<= 0; if(IsCurrent()) { Size sz = GetImageSize(); dlg.cx <<= sz.cx; @@ -84,17 +78,31 @@ void IconDes::PrepareImageDlg(WithImageLayout& dlg) dlg.fixed <<= !!(flags & IML_IMAGE_FLAG_FIXED); dlg.fixed_colors <<= !!(flags & IML_IMAGE_FLAG_FIXED_COLORS); dlg.fixed_size <<= !!(flags & IML_IMAGE_FLAG_FIXED_SIZE); - - dlg.uhd <<= !!(flags & IML_IMAGE_FLAG_UHD); + dlg.dark <<= !!(flags & IML_IMAGE_FLAG_DARK); - dlg.s3 <<= !!(flags & IML_IMAGE_FLAG_S3); + int scale = ImlFlagsToDPIScale(flags); + dlg.scale = decode(scale, DPI_100, 1, DPI_150, 2, DPI_200, 3, DPI_300, 4, 0); for(Ctrl& q : dlg) if(dynamic_cast