ultimatepp/uppsrc/ide/IconDes/Image.cpp
2025-12-25 23:36:22 +01:00

529 lines
11 KiB
C++

#include "IconDes.h"
void IconDes::Interpolate()
{
if(!IsCurrent())
return;
FinishPaste();
SaveUndo();
Slot& c = Current();
c.base_image = c.image;
::InterpolateImage(c.image, c.image.GetSize());
MaskSelection();
}
bool IconDes::BeginTransform()
{
SaveUndo();
Refresh();
SyncShow();
if(!IsPasting()) {
if(SelectionRect() == GetImageSize())
return false;
Move();
}
return true;
}
void IconDes::KeyMove(int dx, int dy)
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
c.pastepos.x += dx;
c.pastepos.y += dy;
MakePaste();
}
else {
Image h = c.image;
c.image = CreateImage(h.GetSize(), Null);
UPP::Copy(c.image, Point(dx, dy), h, h.GetSize(), true);
}
Sync();
}
void IconDes::MirrorX()
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
MirrorHorz(c.paste_image, c.paste_image.GetSize());
MakePaste();
}
else
MirrorHorz(c.image, c.image.GetSize());
SyncShow();
}
void IconDes::SymmX()
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
if(c.paste_image.GetLength() > 1024 * 1024)
return;
Image m = c.paste_image;
Size sz = m.GetSize();
MirrorHorz(m, m.GetSize());
Image h = CreateImage(Size(2 * sz.cx, sz.cy), Null);
UPP::Copy(h, Point(0, 0), c.paste_image, sz);
UPP::Copy(h, Point(sz.cx, 0), m, sz);
c.paste_image = h;
MakePaste();
}
else {
Size sz = c.image.GetSize();
if(sz.cx < 2)
return;
UPP::Copy(c.image, Point(sz.cx - sz.cx / 2, 0), c.image, Size(sz.cx / 2, sz.cy));
MirrorHorz(c.image, RectC(sz.cx - sz.cx / 2, 0, sz.cx / 2, sz.cy));
}
SyncShow();
}
void IconDes::MirrorY()
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
MirrorVert(c.paste_image, c.paste_image.GetSize());
MakePaste();
}
else
MirrorVert(c.image, c.image.GetSize());
SyncShow();
}
void IconDes::SymmY()
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
if(c.paste_image.GetLength() > 1024 * 1024)
return;
Image m = c.paste_image;
Size sz = m.GetSize();
MirrorVert(m, m.GetSize());
Image h = CreateImage(Size(sz.cx, 2 * sz.cy), Null);
UPP::Copy(h, Point(0, 0), c.paste_image, sz);
UPP::Copy(h, Point(0, sz.cy), m, sz);
c.paste_image = h;
MakePaste();
}
else {
Size sz = c.image.GetSize();
if(sz.cy < 2)
return;
UPP::Copy(c.image, Point(0, sz.cy - sz.cy / 2), c.image, Size(sz.cx, sz.cy / 2));
MirrorVert(c.image, RectC(0, sz.cy - sz.cy / 2, sz.cx, sz.cy / 2));
}
SyncShow();
}
void IconDes::MirrorD(bool symm)
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
MirrorDiag(c.paste_image, c.paste_image.GetSize(), symm);
MakePaste();
}
else
MirrorDiag(c.image, c.image.GetSize(), symm);
SyncShow();
}
void IconDes::Rotate()
{
if(!IsCurrent())
return;
Slot& c = Current();
if(BeginTransform()) {
c.paste_image = RotateClockwise(c.paste_image);
MakePaste();
}
else {
c.image = RotateClockwise(c.image);
SetSelect(255);
}
SyncShow();
}
void IconDes::SmoothRescale()
{
if(!IsCurrent())
return;
WithRescaleLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Rescale");
dlg.cx <<= dlg.cy <<= dlg.method <<= dlg.Breaker();
Slot& c = Current();
BeginTransform();
Image bk = IsPasting() ? c.paste_image : c.image;
dlg.cx <<= bk.GetWidth();
dlg.cy <<= bk.GetHeight();
dlg.keep <<= true;
dlg.method.Add(Null, "Rylek");
dlg.method.Add(FILTER_NEAREST, "Nearest");
dlg.method.Add(FILTER_BILINEAR, "Bilinear");
dlg.method.Add(FILTER_BSPLINE, "Bspline");
dlg.method.Add(FILTER_COSTELLO, "Costello");
dlg.method.Add(FILTER_BICUBIC_MITCHELL, "Bicubic Mitchell");
dlg.method.Add(FILTER_BICUBIC_CATMULLROM, "Bicubic Catmull Rom");
dlg.method.Add(FILTER_LANCZOS2, "Lanczos 2");
dlg.method.Add(FILTER_LANCZOS3, "Lanczos 3");
dlg.method.Add(FILTER_LANCZOS4, "Lanczos 4");
// dlg.method.Add(FILTER_LANCZOS5, "Lanczos 5");
for(;;) {
Size sz(minmax((int)~dlg.cx, 1, 9999), minmax((int)~dlg.cy, 1, 9999));
Image m;
if(~dlg.method == FILTER_NEAREST) {
Size isz = bk.GetSize();
ImageBuffer t(sz);
for(int x = 0; x < sz.cx; x++)
for(int y = 0; y < sz.cy; y++)
t[y][x] = bk[y * isz.cy / sz.cy][x * isz.cx / sz.cx];
m = t;
}
else
m = RescaleFilter(bk, sz, ~dlg.method);
if(IsPasting()) {
c.paste_image = m;
MakePaste();
}
else {
c.image = m;
Refresh();
}
SyncImage();
SyncShow();
switch(dlg.Run()) {
case IDCANCEL:
if(IsPasting()) {
c.paste_image = bk;
MakePaste();
}
else {
c.image = bk;
Refresh();
}
SyncImage();
SyncShow();
return;
case IDOK:
SyncList();
return;
}
if(dlg.keep) {
if(dlg.cx.HasFocus() && bk.GetWidth() > 0)
dlg.cy <<= (int)~dlg.cx * bk.GetHeight() / bk.GetWidth();
if(dlg.cy.HasFocus() && bk.GetHeight() > 0)
dlg.cx <<= (int)~dlg.cy * bk.GetWidth() / bk.GetHeight();
}
}
}
Image IconDes::ImageStart()
{
if(!IsCurrent())
return CreateImage(Size(1, 1), Black);
SaveUndo();
Refresh();
SyncShow();
Slot& c = Current();
if(!IsPasting())
c.base_image = c.image;
return IsPasting() ? c.paste_image : c.image;
}
void IconDes::ImageSet(const Image& m)
{
if(!IsCurrent())
return;
Slot& c = Current();
if(IsPasting()) {
c.paste_image = m;
MakePaste();
}
else {
c.image = m;
MaskSelection();
}
Refresh();
SyncShow();
}
void IconDes::BlurSharpen()
{
WithSharpenLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Blur/Sharpen");
PlaceDlg(dlg);
Couple(dlg, dlg.level, dlg.slider2, 1);
Couple(dlg, dlg.radius, dlg.slider, 10, 1);
Image bk = ImageStart();
for(;;) {
Image m = bk;
for(int q = 0; q < (int)~dlg.radius; q++)
m = Sharpen(m, -int(256 * (double)~dlg.level));
ImageSet(m);
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::Colorize()
{
WithImageDblLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Colorize");
PlaceDlg(dlg);
Couple(dlg, dlg.level, dlg.slider, 1, 1);
Image bk = ImageStart();
for(;;) {
RGBA c = rgbactrl.GetColor();
int l = int(255 * Nvl(dlg.level, 1.0));
ImageSet(ForEachPixelStraight(bk, [&](RGBA& t) {
int a = t.a;
t.a = 255;
t = Blend(t, c, l);
t.a = a;
}));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::FreeRotate()
{
WithFreeRotateLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Rotate");
PlaceDlg(dlg);
Couple(dlg, dlg.angle, dlg.slider, 360);
Image bk = ImageStart();
Size tsz = bk.GetSize();
Image src = Magnify(bk, 3, 3);
for(;;) {
Image h = DownSample3x(UPP::Rotate(src, (int)~dlg.angle * 10));
Size sz = h.GetSize();
ImageSet(Crop(h, (sz.cx - tsz.cx) / 2, (sz.cy - tsz.cy) / 2, tsz.cx, tsz.cy));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::Chroma()
{
WithImageDblLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Chroma");
PlaceDlg(dlg);
Couple(dlg, dlg.level, dlg.slider, 2, 0);
Image bk = ImageStart();
for(;;) {
ImageSet(UPP::Grayscale(bk, 256 - (int)(minmax((double)~dlg.level, 0.0, 4.0) * 255)));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::Couple(TopWindow& dlg, EditDouble& level, SliderCtrl& slider, double max, double init, double offset)
{
level.Max(max);
level <<= init;
slider.MinMax(0, 1000);
slider <<= init * 1000 / max;
slider << [=, &dlg, &level, &slider] { level <<= (int)~slider / 1000.0 * max + offset; dlg.Break(); };
level << [=, &dlg, &level, &slider] { slider <<= Nvl(int(((double)~level - offset) * 1000 / max), 500); dlg.Break(); };
}
void IconDes::Couple(TopWindow& dlg, EditInt& level, SliderCtrl& slider, int max, int init)
{
level.Max(max);
level <<= init;
slider.MinMax(0, max);
slider <<= init;
slider << [=, &dlg, &level, &slider] { level <<= (int)~slider; dlg.Break(); };
level << [=, &dlg, &level, &slider] { slider <<= Nvl(int((double)~level), init); dlg.Break(); };
}
void IconDes::Contrast()
{
WithImageDblLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Contrast");
PlaceDlg(dlg);
Couple(dlg, dlg.level, dlg.slider, 2, 1);
Image bk = ImageStart();
for(;;) {
ImageSet(UPP::Contrast(bk, (int)(minmax((double)~dlg.level, 0.0, 4.0) * 255)));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::Alpha()
{
WithImageDblLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Alpha");
PlaceDlg(dlg);
Couple(dlg, dlg.level, dlg.slider, 2, 1);
Image bk = ImageStart();
for(;;) {
int a = (int)(minmax((double)~dlg.level, 0.0, 2.0) * 256);
ImageSet(ForEachPixelStraight(bk, [&](RGBA& t) {
t.a = Saturate255((t.a * a) >> 8);
}));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::RemoveAlpha()
{
WithRemoveAlphaLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Remove Alpha");
PlaceDlg(dlg);
Couple(dlg, dlg.thres, dlg.slider, 255, 128);
Image bk = ImageStart();
for(;;) {
int thres = ~dlg.thres;
ImageSet(ForEachPixel(bk, [&](RGBA& t) {
if(t.a != 255) {
if(t.a < thres)
t = RGBAZero();
else {
t = Unmultiply(t);
t.a = 255;
t = Premultiply(t);
}
}
}));
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::Colors()
{
WithImgColorLayout<TopWindow> dlg;
CtrlLayoutOKCancel(dlg, "Alpha");
PlaceDlg(dlg);
dlg.r_mul <<= dlg.g_mul <<= dlg.b_mul <<= dlg.a_mul <<= 1;
dlg.r_add <<= dlg.g_add <<= dlg.b_add <<= dlg.a_add <<= 0;
dlg.all = true;
dlg.r_mul <<= dlg.g_mul <<= dlg.b_mul <<= dlg.a_mul <<=
dlg.r_add <<= dlg.g_add <<= dlg.b_add <<= dlg.a_add <<=
dlg.all <<= dlg.Breaker();
Image bk = ImageStart();
for(;;) {
bool all = dlg.all;
dlg.g_mul.Enable(!all);
dlg.g_add.Enable(!all);
dlg.b_mul.Enable(!all);
dlg.b_add.Enable(!all);
if(all) {
dlg.g_mul <<= dlg.b_mul <<= ~dlg.r_mul;
dlg.g_add <<= dlg.b_add <<= ~dlg.r_add;
}
ImageBuffer ib(bk.GetSize());
RGBA *t = ib;
const RGBA *s = bk;
const RGBA *e = bk + bk.GetLength();
int r_mul = int(256 * (double)~dlg.r_mul);
int r_add = int(256 * (double)~dlg.r_add);
int g_mul = int(256 * (double)~dlg.g_mul);
int g_add = int(256 * (double)~dlg.g_add);
int b_mul = int(256 * (double)~dlg.b_mul);
int b_add = int(256 * (double)~dlg.b_add);
int a_mul = int(256 * (double)~dlg.a_mul);
int a_add = int(256 * (double)~dlg.a_add);
while(s < e) {
Unmultiply(t, t, 1);
t->r = Saturate255(((r_mul * s->r) >> 8) + r_add);
t->g = Saturate255(((g_mul * s->g) >> 8) + g_add);
t->b = Saturate255(((b_mul * s->b) >> 8) + b_add);
t->a = Saturate255(((a_mul * s->a) >> 8) + a_add);
Premultiply(t, t, 1);
s++;
t++;
}
ImageSet(ib);
switch(dlg.Run()) {
case IDCANCEL:
ImageSet(bk);
return;
case IDOK:
return;
}
}
}
void IconDes::RestoreAlpha()
{
if(!IsCurrent())
return;
Slot& c = Current();
auto Restore = [&](Image& m) {
ImageBuffer ib(m);
int maxg = 0;
for(RGBA& rgba : ib)
maxg = max(Grayscale(rgba), maxg);
if(maxg)
for(RGBA& rgba : ib) {
rgba.a = 255 - 255 * Grayscale(rgba) / maxg;
rgba.r = rgba.g = rgba.b = 0;
}
m = ib;
};
if(BeginTransform()) {
Restore(c.paste_image);
MakePaste();
}
else
Restore(c.image);
SyncShow();
}