#include "Draw.h" namespace Upp { // Upscale2x is based on xBR filter idea by Hyllian, google search on 'xBR filter' // should eventually lead you to a forum topic where he made the algorithm public: // http://board.byuu.org/viewtopic.php?f=10&t=2248 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); }; Size isz = src.GetSize(); ImageBuffer dst; dst.Create(2 * isz); for(int y = 0; y < isz.cy; y++) for(int x = 0; x < isz.cx; x++) { static const int8 cm[] = { // -2 -1 0 +1 +2 -1, 0, 1, 2, -1, // -2 17, 19, 3, 4, 5, // -1 16, 18, -1, 8, 6, // 0 15, 14, 13, 9, 7, // +1 -1, 12, 11, 10, -1, // +2 }; RGBA pp[40]; const int8 *q = cm; for(int dy = -2; dy <= 2; dy++) for(int dx = -2; dx <= 2; dx++) { if(*q >= 0) { int xx = x + dx; int yy = y + dy; pp[*q] = pp[*q + 20] = src[clamp(yy, 0, isz.cy - 1)][clamp(xx, 0, isz.cx - 1)]; } q++; } RGBA c = src[y][x]; RGBA *p = pp; for(int rot = 0; rot < 4; rot++) { RGBA t = c; if(d(c, p[14]) + d(c, p[4]) + d(p[19], p[16]) + d(p[19], p[1]) + 4 * d(p[18], p[3]) < d(p[18], p[13]) + d(p[18], p[17]) + d(p[3], p[8]) + d(p[3], p[0]) + 4 * d(c, p[19])) { RGBA nc = d(c, p[18]) <= d(c, p[3]) ? p[18] : p[3]; t.r = (nc.r + c.r) >> 1; t.g = (nc.g + c.g) >> 1; t.b = (nc.b + c.b) >> 1; } // 0 1 // 3 2 dst[2 * y + (rot >= 2)][2 * x + (rot == 1 || rot == 2)] = t; p += 5; } } return Image(dst); } Image Upscale2x(const Image& src) { if(IsNull(src)) return src; Size s2 = src.Get2ndSpot(); Image s; if(s2.cx > 0 || s2.cy > 0) // When 2nd spot is defined, it is likely Chameleon rescaling item (e.g. button) s = Magnify(src, 2, 2); // in that case, filtering by smart rescale methods could lead to artifacts else { Size isz = src.GetSize(); s = RecreateAlpha(Upscale2x_(GetOver(CreateImage(isz, White()), src)), Upscale2x_(GetOver(CreateImage(isz, Black()), src))); struct SFilter : ImageFilter9 { // Improve contours virtual RGBA operator()(const RGBA **mx) { RGBA s = mx[1][1]; dword l = mx[0][1].a; dword r = mx[2][1].a; dword t = mx[1][0].a; dword b = mx[1][2].a; int l1 = 110; int l2 = 230; return l * r * t * b != 0 || s.a > l1 || mx[0][1].a > l2 || mx[2][1].a > l2 || mx[1][0].a > l2 || mx[1][2].a > l2 ? s : RGBAZero(); } } ef; s = Filter(s, ef); } ImageBuffer h(s); h.SetHotSpot(src.GetHotSpot() * 2); h.Set2ndSpot(s2 * 2); return Image(h); } Image DPIRescale(const Image& src, Size sz) { if(IsNull(src)) return src; 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 * 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)) return src; Size s2 = src.Get2ndSpot(); // see above... Image m = DownSample2x(DownSample3x(src)); ImageBuffer h(m); h.SetHotSpot(s2 / 6); h.Set2ndSpot(src.Get2ndSpot() / 6); return Image(h); } int DPIScaleGlobal_ = 2; double DPIScaleGlobalF_ = 1; double IDPIScaleGlobalF_ = 1; void SetDPIScale(int scale) { Iml::ResetAll(); DPIScaleGlobal_ = scale; DPIScaleGlobalF_ = 0.5 * scale; IDPIScaleGlobalF_ = 1 / DPIScaleGlobalF_; } void SyncDPIScale() { 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); } };