#include "Draw.h" namespace Upp { int Diff(RGBA a, RGBA b) { return max(abs(a.a - b.a), max(abs(a.r - b.r), max(abs(a.b - b.b), abs(a.g - b.g)))); } struct ButtonDecomposer { Image src; Image dst; int aa; int maxdiff; RGBA color; int gdiff; int gcount; void Do() { Size sz = src.GetSize(); int a = min(5, min(sz.cx, sz.cy) / 5); maxdiff = gdiff = gcount = 0; color = SColorDisabled(); dst = src; ImageBuffer b(dst); for(int y = a; y < sz.cy - a; y++) { RGBA *p = b[y]; RGBA c1 = p[a - 1]; RGBA c2 = p[sz.cx - a + 1]; int n = sz.cx - 2 * a; p += a; for(int x = 0; x < n; x++) { RGBA c; c.r = ((n - x) * c1.r + x * c2.r) / n; c.g = ((n - x) * c1.g + x * c2.g) / n; c.b = ((n - x) * c1.b + x * c2.b) / n; c.a = ((n - x) * c1.a + x * c2.a) / n; int d = Diff(c, *p); if(d > 30) { gcount++; gdiff += d; if(d >= maxdiff) { maxdiff = d; color = *p; } } *p++ = c; } } b.SetHotSpot(Point(a, a)); dst = b; } }; Image Unglyph(const Image& m, Color& c, double& gfactor) { ButtonDecomposer b; b.src = Unmultiply(m); b.Do(); c = b.color; gfactor = (double)b.gdiff / b.gcount; return Premultiply(b.dst); } Image Unglyph(const Image& m, Color& c) { double dummy; return Unglyph(m, c, dummy); } Image Unglyph(const Image& m) { Color dummy; return Unglyph(m, dummy); } Image VertBlend(Image img1, Image img2, int y0, int y1) { Size sz = img1.GetSize(); Size sz2 = img2.GetSize(); sz.cx = min(sz.cx, sz2.cx); sz.cy = min(sz.cy, sz2.cy); ImageBuffer b(sz); for(int y = 0; y < sz.cy; y++) if(y >= y1) memcpy(b[y], img2[y], sz.cx * sizeof(RGBA)); else if(y >= y0 && y1 > y0) { int alpha = 256 * (y - y0) / (y1 - y0); RGBA *t = b[y]; const RGBA *s1 = img1[y]; const RGBA *s2 = img2[y]; const RGBA *e = s1 + sz.cx; while(s1 < e) { t->r = s1->r + ((alpha * (s2->r - s1->r)) >> 8); t->g = s1->g + ((alpha * (s2->g - s1->g)) >> 8); t->b = s1->b + ((alpha * (s2->b - s1->b)) >> 8); t->a = s1->a + ((alpha * (s2->a - s1->a)) >> 8); s1++; s2++; t++; } } else memcpy(b[y], img1[y], sz.cx * sizeof(RGBA)); b.SetHotSpot(img1.GetHotSpot()); b.Set2ndSpot(img1.Get2ndSpot()); return Image(b); } Image HorzBlend(Image img1, Image img2, int x0, int x1) { Image m = RotateAntiClockwise(VertBlend(RotateClockwise(img1), RotateClockwise(img2), x0, x1)); ImageBuffer b(m); b.SetHotSpot(img1.GetHotSpot()); b.Set2ndSpot(img1.Get2ndSpot()); return Image(b); } Image HorzSymm(Image src) { ImageBuffer b(src); Size sz = b.GetSize(); for(int y = 0; y < sz.cy; y++) { RGBA *l = b[y]; for(int x = 0; x < sz.cx / 2; x++) l[sz.cx - x - 1] = l[x]; } b.SetHotSpot(src.GetHotSpot()); b.Set2ndSpot(src.Get2ndSpot()); return Image(b); } bool EqLine(const Image& m, int l1, int l2, int x, int width) { return !memcmp(m[l1] + x, m[l2] + x, width * sizeof(RGBA)); } bool EqLine(const Image& m, int l1, int l2) { return EqLine(m, l1, l2, 0, m.GetWidth()); } bool IsSingleColor(const Image& m, const Rect& rect_) { Rect rect = rect_; rect.Intersect(m.GetSize()); if(IsNull(rect)) return true; RGBA c = m[rect.top][rect.left]; for(int y = rect.top; y < rect.bottom; y++) { const RGBA *line = m[y]; for(int x = rect.left; x < rect.right; x++) if(line[x] != c) return false; } return true; } Image RecreateAlpha(const Image& overwhite, const Image& overblack) { Size sz = overwhite.GetSize(); ASSERT(overblack.GetSize() == sz); ImageBuffer r(sz); const RGBA *ws = overwhite; const RGBA *bs = overblack; RGBA *t = r; RGBA *e = t + r.GetLength(); while(t < e) { t->a = Saturate255(bs->r - ws->r + 255); if(t->a) { t->r = Saturate255(bs->r * 255 / t->a); t->g = Saturate255(bs->g * 255 / t->a); t->b = Saturate255(bs->b * 255 / t->a); } else t->r = t->g = t->b = 0; t++; bs++; ws++; } Premultiply(r); return Image(r); } int ImageMargin(const Image& _m, int p, int dist) { Image m = Unmultiply(_m); Color c = m[p][p]; int d; Size sz = m.GetSize(); for(d = p; d >= 0; d--) if(Diff(m[d][d], c) > dist || Diff(m[sz.cx - d - 1][sz.cy - d - 1], c) > dist) break; return d + 1; } int ImageMarginV(const Image& _m, int p, int dist) { Image m = Unmultiply(_m); Size sz = m.GetSize(); Color c = m[sz.cx / 2][p]; int d; for(d = p; d >= 0; d--) if(Diff(m[sz.cx / 2][d], c) > dist || Diff(m[sz.cx / 2][sz.cy - d - 1], c) > dist) break; return d + 1; } Rect GetImageMargins(const Image& m, RGBA margin_color) { Rect r; Size isz = m.GetSize(); for(int pass = 0; pass < 2; pass++) { int& y = pass ? r.bottom : r.top; for(y = 0; y < isz.cy; y++) { const RGBA *s = m[pass ? isz.cy - y - 1 : y]; for(int x = 0; x < isz.cx; x++) if(*s++ != margin_color) goto foundy; } foundy: int& x = pass ? r.right : r.left; for(x = 0; x < isz.cx; x++) { const RGBA *s = m[0] + (pass ? isz.cx - x - 1 : x); for(int y = 0; y < isz.cy; y++) { if(*s != margin_color) goto foundx; s += isz.cx; } } foundx:; } return r; } ChPartMaker::ChPartMaker(const Image& m) { image = m; border = SColorShadow(); bg = Null; ResetShape(); } void ChPartMaker::ResetShape() { t = b = l = r = true; tl = tr = bl = br = 0; } Image ChPartMaker::Make() const { Size sz = image.GetSize(); ASSERT(sz.cx >= 6 && sz.cy >= 6); Image h = image; ImageBuffer ib(h); for(int x = 0; x < sz.cx; x++) { if(t) ib[0][x] = x >= tl && x < sz.cx - tr ? border : bg; if(b) ib[sz.cy - 1][x] = x >= bl && x < sz.cx - br ? border : bg; } for(int y = 0; y < sz.cy; y++) { if(l) ib[y][0] = y >= tl && y < sz.cy - bl ? border : bg; if(r) ib[y][sz.cx - 1] = y >= tr && y < sz.cy - br ? border : bg; } if(tl == 2) ib[1][1] = border; if(tr == 2) ib[1][sz.cx - 2] = border; if(bl == 2) ib[sz.cy - 2][1] = border; if(br == 2) ib[sz.cy - 2][sz.cx - 2] = border; int q = max(max(tl, tr), max(br, bl)); ib.SetHotSpot(Point(q, q)); ib.Set2ndSpot(Point(sz.cx - q - 1, sz.cy - q - 1)); return Image(ib); } double Difference(const Image& a, const Image& b) { if(a.GetSize() != b.GetSize()) return DBL_MAX; double diff = 0; const RGBA *p = ~a; const RGBA *e = a.end(); const RGBA *q = ~b; while(p < e) { diff += sqr(p->a - q->a) + sqr(p->r - q->r) + sqr(p->g - q->g) + sqr(p->b - q->b); p++; q++; } return sqrt(diff / a.GetLength()); } }