diff --git a/src/BrightContDlg.cpp b/src/BrightContDlg.cpp --- a/src/BrightContDlg.cpp +++ b/src/BrightContDlg.cpp @@ -1,352 +1,352 @@ #include #include "Image.h" #include "lgi/common/Bitmap.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Palette.h" #include "lgi/common/ScrollBar.h" void ChangeBrightnessContrast( LSurface *pDC, double Brightness, // -1 -> 1 double Contrast, // -1 -> 1 Progress *Prog = 0) { int Lut[256], n=0; for (double i=0; i<1.0; i+=1.0/256.0, n++) { double Val; /* apply brightness */ if (Brightness < 0.0) Val = i * (1.0 + Brightness); else Val = i + ((1.0 - i) * Brightness); /* apply contrast */ double nvalue; if (Contrast < 0.0) { if (i > 0.5) nvalue = 1.0 - Val; else nvalue = Val; if (nvalue < 0.0) nvalue = 0.0; nvalue = 0.5 * pow (nvalue * 2.0 , (double) (1.0 + Contrast)); if (i > 0.5) Val = 1.0 - nvalue; else Val = nvalue; } else { if (i > 0.5) nvalue = 1.0 - Val; else nvalue = Val; if (nvalue < 0.0) nvalue = 0.0; double power = (Contrast == 1.0) ? 127 : 1.0 / (1.0 - Contrast); nvalue = 0.5 * pow (2.0 * nvalue, power); if (i > 0.5) Val = 1.0 - nvalue; else Val = nvalue; } if (Val < 0.0) { Lut[n] = 0; } else if (Val > 1.0) { Lut[n] = 255; } else { Lut[n] = (int) (255 * Val); } } if (Prog) { Prog->SetRange(pDC->Y()); Prog->Value(0); } if (pDC->GetBits() <= 8) { LPalette *p = pDC->Palette(); if (!p) { p = new LPalette(0, 256); if (p) { p->CreateGreyScale(); pDC->Palette(p, true); } } if (p) { for (int i=0; iGetSize(); i++) { GdcRGB *c = (*p)[i]; if (c) { c->r = Lut[c->r]; c->g = Lut[c->g]; c->b = Lut[c->b]; } } } } else { for (int y=0; yY(); y++) { switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *p = (System24BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { p->r = Lut[p->r]; p->g = Lut[p->g]; p->b = Lut[p->b]; p++; } break; } case System32BitColourSpace: { System32BitPixel *p = (System32BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { p->r = Lut[p->r]; p->g = Lut[p->g]; p->b = Lut[p->b]; p++; } break; } } if (Prog && (y % 10 == 0)) { Prog->Value(y); } } } } class BrightnessContrast : public LDialog { ImgWnd *Parent; LScrollBar *BrightS, *ContS; LBitmap *Preview; LSurface *Thumb; void UpdatePreview() { if (Parent && Preview) { LSurface *pDC = Parent->GetDC(); int Bright = GetCtrlValue(IDC_BRIGHT_E); int Cont = GetCtrlValue(IDC_CONT_E); if (pDC && !Thumb) { int MaxDim = MAX(pDC->X(), pDC->Y()); double Scale = 250.0 / (double)MaxDim; Thumb = new LMemDC; if (Thumb && Thumb->Create ( (int) (pDC->X()*Scale), (int) (pDC->Y()*Scale), pDC->GetColourSpace() )) { for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { COLOUR c = pDC->Get ( (int) (x / Scale), (int) (y / Scale) ); Thumb->Colour(c); Thumb->Set(x, y); } } } } if (Thumb) { LMemDC *New = new LMemDC(Thumb); if (New) { ChangeBrightnessContrast(New, ((double)Bright) / 50, ((double)Cont) / 50); Preview->SetDC(New); } } } } public: BrightnessContrast(ImgWnd *parent) { Thumb = 0; BrightS = ContS = 0; SetParent(Parent = parent); if (LoadFromResource(IDD_BRIGHT_CONT)) { MoveToCenter(); BrightS = dynamic_cast(FindControl(IDC_BRIGHT_S)); ContS = dynamic_cast(FindControl(IDC_CONT_S)); Preview = dynamic_cast(FindControl(IDC_PREVIEW)); if (BrightS) { BrightS->SetPage(5); BrightS->SetLimits(0, 100); BrightS->Value(50); SetCtrlValue(IDC_BRIGHT_E, 0); } if (ContS) { ContS->SetPage(5); ContS->SetLimits(0, 100); ContS->Value(50); SetCtrlValue(IDC_CONT_E, 0); } } UpdatePreview(); } void OnCreate() { LTableLayout *l = dynamic_cast(FindControl(IDC_LAYOUT)); if (l) { LRect r = l->GetUsedArea(); LRect a = GetPos(); a.x2 = a.x1 + r.X() + 20 + LAppInst->GetMetric(LGI_MET_DECOR_X); a.y2 = a.y1 + r.Y() + 20 + LAppInst->GetMetric(LGI_MET_DECOR_Y); SetPos(a); MoveToCenter(); } } int OnNotify(LViewI *Ctrl, LNotification n) { static bool Processing = false; switch (Ctrl->GetId()) { case IDC_BRIGHT_S: { if (!Processing) { Processing = true; SetCtrlValue(IDC_BRIGHT_E, BrightS->Value() - 50); UpdatePreview(); Processing = false; } break; } case IDC_BRIGHT_E: { if (!Processing) { Processing = true; BrightS->Value(GetCtrlValue(IDC_BRIGHT_E) + 50); UpdatePreview(); Processing = false; } break; } case IDC_CONT_S: { if (!Processing) { Processing = true; SetCtrlValue(IDC_CONT_E, ContS->Value() - 50); UpdatePreview(); Processing = false; } break; } case IDC_CONT_E: { if (!Processing) { Processing = true; ContS->Value(GetCtrlValue(IDC_CONT_E) + 50); UpdatePreview(); Processing = false; } break; } case IDOK: { if (Parent && Parent->GetDC()) { int Bright = GetCtrlValue(IDC_BRIGHT_E); int Cont = GetCtrlValue(IDC_CONT_E); LProgressDlg Dlg(this); Dlg.SetDescription("Applying filter..."); Dlg.SetRange(Parent->GetDC()->Y()); Parent->BeforeAction(); Parent->BeforeUpdate(); ChangeBrightnessContrast(Parent->GetDC(), ((double)Bright) / 50, ((double)Cont) / 50, Dlg.ItemAt(0)); Dlg.Value(0); Dlg.SetRange(1); Dlg.SetDescription("Creating undo..."); Parent->Update(); Parent->AfterAction(); } // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId()); break; } } return 0; } }; void DoBrightnessContrast(ImgWnd *Parent) { - BrightnessContrast Dlg(Parent); - Dlg.DoModal(); + auto Dlg = new BrightnessContrast(Parent); + Dlg->DoModal(NULL); } diff --git a/src/ColourCtrl.cpp b/src/ColourCtrl.cpp --- a/src/ColourCtrl.cpp +++ b/src/ColourCtrl.cpp @@ -1,460 +1,464 @@ #include #include "Image.h" #include "lgi/common/Palette.h" #include "lgi/common/Menu.h" ////////////////////////////////////////////////////////////////////////////////////////// // Colour control #define COLOUR_CTRL_X 32 #define COLOUR_CTRL_Y TBS_Y #ifdef WIN32 char *LColourCtrl::Class() { return "IMAGE_ColourControl"; } uint32_t LColourCtrl::Style() { return WS_CHILD | WS_TABSTOP | WS_VISIBLE; } #endif LColourCtrl::LColourCtrl(ImgWnd *notify, int id, int x, int y, int msg) : ResObject(Res_Custom) { Name("LColourCtrl"); SetId(id); SetNotify(notify); Message = msg; Ws = notify; LRect r(x, y, x+36, y+18); SetPos(r); c.Rgb(255, 255, 255); pDC = NULL; ColourPos.ZOff(0, 0); Border = 0; Clicked = false; DrawDown = false; Edit = true; Remap = false; #if defined WIN32 LWindowsClass *c = LWindowsClass::Create(Class()); if (c) { c->Register(); } #elif defined BEOS GetBView()->SetFlags(GetBView()->Flags() | B_NAVIGABLE); #endif } void LColourCtrl::OnFocus(bool f) { Invalidate(); } int LColourCtrl::GetBorder() { return Border; } void LColourCtrl::SetBorder(int b) { Border = b; } int LColourCtrl::GetEdit() { return Edit; } void LColourCtrl::SetEdit(bool e) { Edit = e; LView::Invalidate(); } LColour LColourCtrl::Colour() { return c; } void LColourCtrl::Colour(LColour Col) { c = Col; LView::Invalidate(&ColourPos); } LPalette *LColourCtrl::Palette() { return Pal; } void LColourCtrl::Palette(LPalette *p) { LPalette *n = new LPalette(p); Pal.Reset(n); } void LColourCtrl::operator =(LColour col) { Colour(col); } LColourCtrl::operator LColour() { return Colour(); } void LColourCtrl::FromDC(LSurface *pDC) { if (!pDC) return; int c24 = c.c24(); LPalette *p = pDC->Palette(); Pal.Reset(p ? new LPalette(p) : NULL); if (pDC->GetBits() == 8) c.Set(Pal->MatchRgb(c24), 8, Pal); } void LColourCtrl::ToDC(LSurface *pDC, bool Remap) { if (pDC) { if (Pal) { if (Remap && pDC->GetBits() <= 8) { // TODO: remap code } pDC->Palette(new LPalette(Pal)); } } } LMessage::Result LColourCtrl::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case COL_CTRL_SET: { (*this) = LColour(Msg->A(), Msg->B(), Pal); break; } case COL_CTRL_GET: { return c.c24(); break; } case COL_CTRL_GET_BITS: { return 24; } case COL_CTRL_SET_PAL: { if (Msg->A()) { Pal.Reset(new LPalette((LPalette*) Msg->A())); } if (Ws) { ToDC(Ws->GetDC(), Msg->B()); Ws->Update(); } break; } case COL_CTRL_GET_PAL: { if (Ws) { FromDC(Ws->GetDC()); } return (LMessage::Result) Pal.Get(); break; } } return LView::OnEvent(Msg); } void LColourCtrl::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); if (Border) { LWideBorder(pDC, r, (DrawDown) ? DefaultSunkenEdge : DefaultRaisedEdge); } else if (DrawDown) { LThinBorder(pDC, r, DefaultSunkenEdge); } else { pDC->Colour(L_MED); pDC->Rectangle(&r); r.Inset(1, 1); } pDC->Colour(L_MED); pDC->Rectangle(&r); LRect n = r; r.Inset(3, 3); if (Edit) { r.x2 -= 7; } if (DrawDown) { r.Offset(1, 1); n.Offset(1, 1); } pDC->Colour(0); pDC->Box(&r); r.Inset(1, 1); ColourPos = r; if (c.IsTransparent()) { pDC->Colour(L_MED); pDC->Rectangle(&ColourPos); pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Line(ColourPos.x1, ColourPos.y1, ColourPos.x2, ColourPos.y2); pDC->Line(ColourPos.x1, ColourPos.y2, ColourPos.x2, ColourPos.y1); } else { pDC->Colour(c); pDC->Rectangle(&ColourPos); } if (Edit) { int TRIANGLE = Y() / 4; // int Off = ((DrawDown) ? 1 : 0); LPoint p[3]; p[0].x = r.x2 + 4; p[0].y = r.y1 + (r.Y() / 2); p[1].x = p[0].x + TRIANGLE; p[1].y = p[0].y + TRIANGLE; p[2].x = p[0].x + TRIANGLE; p[2].y = p[0].y - TRIANGLE; pDC->Colour(0); pDC->Polygon(3, p); } if (Focus()) { n.Inset(1, 1); #ifdef WIN32 RECT rc = n; DrawFocusRect(pDC->Handle(), &rc); #else uint Old = pDC->LineStyle(0xAAAAAAAA); pDC->Colour(L_LOW); pDC->Box(&n); pDC->LineStyle(Old); #endif } } bool LColourCtrl::OnKey(LKey &k) { switch (k.c16) { case ' ': { int Old = DrawDown; DrawDown = k.Down(); Invalidate(); if (Old) { OnNotify(this, LNotifyValueChanged); } return true; break; } } return false; } void LColourCtrl::OnMouseClick(LMouse &m) { if (Edit) { bool Old = DrawDown; if (m.Down()) { Focus(true); } if (m.Left()) { Clicked = m.Down(); Capture(m.Down()); LRect r(0, 0, X()-1, Y()-1); if (Old && r.Overlap(m.x, m.y)) { if (m.Left()) { OnNotify(this, LNotifyValueChanged); } } DrawDown = m.Down(); if (Old != DrawDown) { Invalidate(); } } else { auto RClick = new LSubMenu; if (RClick) { COLOUR Preset[] = { Rgb32(0, 0, 0), Rgb32(255, 255, 255), Rgb32(255, 0, 0), Rgb32(0, 255, 0), Rgb32(0, 0, 255), Rgb32(255, 255, 0), Rgb32(255, 0, 255), Rgb32(0, 255, 255), Rgb32(128, 128, 128), Rgb32(192, 192, 192)}; const char *PresetName[] = { "Black", "White", "Red", "Green", "Blue", "Yellow", "Pink", "Aqua", "Dark Grey", "Light Grey"}; for (int i=0; i<10; i++) { char s[256]; sprintf(s, "%s (%i, %i, %i)", PresetName[i], R32(Preset[i]), G32(Preset[i]), B32(Preset[i])); RClick->AppendItem(s, 1000+i, true); } RClick->AppendSeparator(); RClick->AppendItem("Transparent", 1, true); if (GetMouse(m, true)) { int p = RClick->Float(this, m.x, m.y); if (p >= 1000 && p < 1010) { Colour(LColour(Preset[p-1000], 32)); Invalidate(); } else if (p == 1) { Colour(LColour()); Invalidate(); } } DeleteObj(RClick); } } } } void LColourCtrl::OnMouseEnter(LMouse &m) { if (Clicked) { DrawDown = true; Invalidate(); } } void LColourCtrl::OnMouseExit(LMouse &m) { if (Clicked) { DrawDown = false; Invalidate(); } } int LColourCtrl::OnNotify(LViewI *Host, LNotification n) { // open colour dialog here if (!Edit) return 0; - ColourDlg Dlg(Host); - if (Dlg.DoModal() == IDOK) + auto Dlg = new ColourDlg(Host); + Dlg->DoModal([this, Dlg](auto dlg, auto code) { - c = Dlg.c; - if (Dlg.Palette) + if (code == IDOK) { - Remap = Dlg.RemapPalette; - Pal.Reset(new LPalette(Dlg.Palette)); - Invalidate(); + c = Dlg->c; + if (Dlg->Palette) + { + Remap = Dlg->RemapPalette; + Pal.Reset(new LPalette(Dlg->Palette)); + Invalidate(); + } } - } + delete dlg; + }); return 0; } bool LColourCtrl::OnLayout(LViewLayoutInfo &Inf) { int em = GetFont()->GetHeight(); auto css = GetCss(); auto par = GetParent(); if (!Inf.Width.Min) { LCss::Len w; if (css) w = css->Width(); if (w.IsValid()) { Inf.Width.Min = Inf.Width.Max = w.ToPx(par ? par->X() : 500, GetFont()); } else { Inf.Width.Min = em * 4; Inf.Width.Max = em * 4; } } else { LCss::Len h; if (css) h = css->Height(); if (h.IsValid()) { Inf.Height.Min = Inf.Height.Max = h.ToPx(par ? par->Y() : 500, GetFont()); } else { Inf.Height.Min = em * 1.5; Inf.Height.Max = em * 1.5; } } return true; } DeclFactory(LColourCtrl); \ No newline at end of file diff --git a/src/Compare.cpp b/src/Compare.cpp --- a/src/Compare.cpp +++ b/src/Compare.cpp @@ -1,871 +1,874 @@ #include "Image.h" #include "lgi/common/Edit.h" #include "lgi/common/Palette.h" #include "lgi/common/Thread.h" #define DIFF_LARGE_8BIT (10) #define DIFF_LARGE_16BIT (DIFF_LARGE_8BIT << 8) //#define DIFF_CENTER (1<<7) #define OPT_CompareLeft "CmpLeft" #define OPT_CompareRight "CmpRight" enum CmpCtrls { IDC_A_NAME = 100, IDC_B_NAME, IDC_C_NAME, IDC_A_VIEW, IDC_B_VIEW, IDC_C_VIEW, }; #ifdef DIFF_CENTER LArray RDiff, GDiff, BDiff; #endif struct CompareStats { uint64 LowDiff; uint64 HighDiff; uint64 Total; CompareStats() { LowDiff = 0; HighDiff = 0; Total = 0; } }; template void CompareRgb(LSurface *A, LSurface *B, uint8_t *c, LPoint size, int threshold, CompareStats &Stat) { if (!A || !B || !c) return; Px *a = (Px*) (*A)[size.y]; Px *b = (Px*) (*B)[size.y]; Px *e = a + size.x; int32 Diff, Mask, Value; while (a < e) { Value = (int32)a->r - b->r; #ifdef DIFF_CENTER RDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff = (Value + Mask) ^ Mask; Value = (int32)a->g - b->g; #ifdef DIFF_CENTER GDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; Value = (int32)a->b - b->b; #ifdef DIFF_CENTER BDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; if (Diff >= threshold) { Stat.HighDiff++; *c++ = 2; } else if (Diff) { Stat.LowDiff++; *c++ = 1; } else { *c++ = 0; } a++; b++; } } template void CompareRgba(LSurface *A, LSurface *B, uint8_t *c, LPoint size, int threshold, CompareStats &Stat) { Px *a = (Px*) (*A)[size.y]; Px *b = (Px*) (*B)[size.y]; Px *e = a + size.x; int32 Diff, Mask, Value; while (a < e) { Value = (int32)a->r - b->r; #ifdef DIFF_CENTER RDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff = (Value + Mask) ^ Mask; Value = (int32)a->g - b->g; #ifdef DIFF_CENTER GDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; Value = (int32)a->b - b->b; #ifdef DIFF_CENTER BDiff[DIFF_CENTER+Value]++; #endif Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; Value = (int32)a->a - b->a; Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; if (Diff >= threshold) { Stat.HighDiff++; *c++ = 2; } else if (Diff) { Stat.LowDiff++; *c++ = 1; } else { *c++ = 0; } a++; b++; } } LAutoPtr CreateDiff(LViewI *Parent, LSurface *A, LSurface *B, CompareStats &Stat) { LAutoPtr C; Stat.Total = A->X() * A->Y(); int Cx = MIN(A->X(), B->X()), Cy = MIN(A->Y(), B->Y()); if (A->GetColourSpace() != B->GetColourSpace()) { LStringPipe p; p.Print("The bit depths of the images are different: %i (left), %i (right).", A->GetBits(), B->GetBits()); LAutoString a(p.NewStr()); LgiMsg(Parent, "%s", "Image Compare", MB_OK, a.Get()); } else if (C.Reset(new ImgMemDC(Cx, Cy, CsIndex8)) && (*C)[0]) { uchar Pal[] = {0, 0, 0, 0xc0, 0xc0, 0xc0, 0xff, 0, 0}; C->Palette(new LPalette(Pal, 3)); for (int y=0; yGetColourSpace()) { case CsIndex8: { LPalette *apal = A->Palette(); LPalette *bpal = B->Palette(); if (apal && bpal) { GdcRGB *ap = (*apal)[0]; GdcRGB *bp = (*bpal)[0]; uint8_t *a = (*A)[y]; uint8_t *b = (*B)[y]; uint8_t *e = a + Cx; while (a < e) { Value = (int32)ap[*a].r - bp[*b].r; Mask = Value >> 31; Diff = (Value + Mask) ^ Mask; Value = (int32)ap[*a].g - bp[*b].g; Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; Value = (int32)ap[*a].b - bp[*b].b; Mask = Value >> 31; Diff += (Value + Mask) ^ Mask; if (Diff >= DIFF_LARGE_8BIT) { Stat.HighDiff++; *c++ = 2; } else if (Diff) { Stat.LowDiff++; *c++ = 1; } else { *c++ = 0; } a++; b++; } } else LAssert(!"No palette?"); break; } #define CompareCaseRgb(type, threshold) \ case Cs##type: \ CompareRgb(A, B, c, LPoint(Cx, y), threshold, Stat); \ break CompareCaseRgb(Rgb24, DIFF_LARGE_8BIT); CompareCaseRgb(Bgr24, DIFF_LARGE_8BIT); CompareCaseRgb(Rgbx32, DIFF_LARGE_8BIT); CompareCaseRgb(Bgrx32, DIFF_LARGE_8BIT); CompareCaseRgb(Xrgb32, DIFF_LARGE_8BIT); CompareCaseRgb(Xbgr32, DIFF_LARGE_8BIT); CompareCaseRgb(Rgb48, DIFF_LARGE_16BIT); CompareCaseRgb(Bgr48, DIFF_LARGE_16BIT); #define CompareCaseRgba(type, threshold) \ case Cs##type: \ CompareRgba(A, B, c, LPoint(Cx, y), threshold, Stat); \ break CompareCaseRgba(Rgba32, DIFF_LARGE_8BIT); CompareCaseRgba(Bgra32, DIFF_LARGE_8BIT); CompareCaseRgba(Argb32, DIFF_LARGE_8BIT); CompareCaseRgba(Abgr32, DIFF_LARGE_8BIT); CompareCaseRgba(Rgba64, DIFF_LARGE_16BIT); CompareCaseRgba(Bgra64, DIFF_LARGE_16BIT); default: { LAssert(!"Impl me."); break; } } } } #ifdef DIFF_CENTER int Len = max(RDiff.Length(), GDiff.Length()); Len = max(Len, BDiff.Length()); int Start = Len-1, End = 0; for (int i=0; i Doc; LMessage::Param Param; ThreadLoader(LView *owner, LAutoString file, LMessage::Param param) : LThread("ThreadLoader") { Owner = owner; File = file; Param = param; Run(); } ~ThreadLoader() { while (!IsExited()) LSleep(10); } int Main() { if (Doc.Reset(new ImgDocument)) Doc->Load(Owner, File); Owner->PostEvent(M_LOAD, 0, (LMessage::Param) this); return 0; } }; class CompareWnd : public LWindow, public ImageViewTarget { ImgWnd *App; LEdit *AName, *BName, *CName; LAutoPtr A, B, C; ImgView *AView, *BView, *CView; LArray Threads; LStatusBar *Status; LStatusPane *Pane[3]; bool DraggingView; LPoint DocPos; public: CompareWnd(ImgWnd *app, const char *FileA = NULL, const char *FileB = NULL) { App = app; AName = BName = CName = NULL; DraggingView = false; LRect p(0, 0, 1200, 1000); SetPos(p); MoveToCenter(); Name("i.Mage Comparison"); if (Attach(0)) { AddView(Status = new LStatusBar); Status->AppendPane(Pane[0] = new LStatusPane); Status->AppendPane(Pane[1] = new LStatusPane); Status->AppendPane(Pane[2] = new LStatusPane); if (FileA) { LAutoString a(NewStr(FileA)); Threads.Add(new ThreadLoader(this, a, 0)); } if (FileB) { LAutoString b(NewStr(FileB)); Threads.Add(new ThreadLoader(this, b, 1)); } AddView(AName = new LEdit(IDC_A_NAME, 0, 0, 100, LSysFont->GetHeight() + 8, FileA)); AddView(BName = new LEdit(IDC_B_NAME, 0, 0, 100, LSysFont->GetHeight() + 8, FileB)); AddView(CName = new LEdit(IDC_C_NAME, 0, 0, 100, LSysFont->GetHeight() + 8, NULL)); CName->Sunken(false); CName->Enabled(false); AddView(AView = new ImgView(this)); AView->SetId(IDC_A_VIEW); AddView(BView = new ImgView(this)); BView->SetId(IDC_B_VIEW); AddView(CView = new ImgView(this)); CView->SetId(IDC_C_VIEW); #if 0 AView->SetSampleMode(SuperView::SampleAverage); BView->SetSampleMode(SuperView::SampleAverage); #endif CView->SetSampleMode(LZoomView::SampleMax); AttachChildren(); Visible(true); OnPosChange(); } } ~CompareWnd() { Threads.DeleteObjects(); } void OnPaint(LSurface *pDC) { pDC->Colour(L_WORKSPACE); pDC->Rectangle(); } LMessage::Param OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_LOAD: { ThreadLoader *t = (ThreadLoader*) Msg->B(); if (t) { if (t->Param == 0) { A.Reset(t->Doc->ReleaseDC()); AView->SetSurface(A, false); } else if (t->Param == 1) { B.Reset(t->Doc->ReleaseDC()); BView->SetSurface(B, false); } Threads.Delete(t); DeleteObj(t); } if (A && B) { CompareStats Stat; LAutoPtr pDC = CreateDiff(this, A, B, Stat); if (pDC) { if (C.Reset(pDC.Release())) { CView->SetSurface(C, false); LgiMsg( this, "LowDiff: " LPrintfInt64 "\n" "HighDiff: " LPrintfInt64 "\n" "Total: " LPrintfInt64 "\n", "Compare", MB_OK, Stat.LowDiff, Stat.HighDiff, Stat.Total); } } } break; } } return LWindow::OnEvent(Msg); } void OnPosChange() { LWindow::OnPosChange(); if (AName && BName && AView && BView && CView) { LRect c = GetClient(); if (Status) { c.y2 = Status->GetPos().y1 - 1; } int width = (c.X() - 10) / 3; LRect ar = c; ar.x2 = ar.x1 + width - 1; LRect cr = c; cr.x1 = ar.x2 + 6; cr.x2 = cr.x1 + width - 1; LRect br = c; br.x1 = br.x2 - width; Pane[0]->SetWidth(cr.x1); Pane[1]->SetWidth(br.x1 - cr.x1); Pane[2]->SetWidth(cr.X()); LRect name = ar; name.y2 = name.y1 + AName->Y() - 1; AName->SetPos(name); ar.y1 = name.y2 + 1; AView->SetPos(ar); name = br; name.y2 = name.y1 + BName->Y() - 1; BName->SetPos(name); br.y1 = name.y2 + 1; BView->SetPos(br); name = cr; name.y2 = name.y1 + CName->Y() - 1; CName->SetPos(name); cr.y1 = name.y2 + 1; CView->SetPos(cr); } } void OnViewportChange() { if (CView && CName) { LZoomView::ViewportInfo i = CView->GetViewport(); char s[256]; int ch = sprintf_s(s, sizeof(s), "Scroll: %i,%i ", i.Sx, i.Sy); if (i.Zoom < 0) ch += sprintf_s(s+ch, sizeof(s)-ch, "Zoom: 1/%i", 1 - i.Zoom); else ch += sprintf_s(s+ch, sizeof(s)-ch, "Zoom: %ix", i.Zoom + 1); CName->Name(s); } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_A_VIEW: { LZoomView::ViewportInfo i = AView->GetViewport(); BView->SetViewport(i); CView->SetViewport(i); OnViewportChange(); break; } case IDC_B_VIEW: { LZoomView::ViewportInfo i = BView->GetViewport(); AView->SetViewport(i); CView->SetViewport(i); OnViewportChange(); break; } case IDC_C_VIEW: { LZoomView::ViewportInfo i = CView->GetViewport(); AView->SetViewport(i); BView->SetViewport(i); OnViewportChange(); break; } } return LWindow::OnNotify(Ctrl, n); } #undef DefOption #define DefOption(_type, _name, _opt, _def) \ _type _name() { LVariant v = _def; App->GetOptions()->GetValue(_opt, v); return v.CastInt32(); } \ void _name(_type i) { LVariant v; App->GetOptions()->SetValue(_opt, v = i); } DefOption(int, DisplayGrid, OPT_DisplayGrid, true); DefOption(int, GridSize, OPT_GridSize, 8); DefOption(int, DisplayTile, OPT_DisplayTile, true); DefOption(int, TileType, OPT_TileType, 0); DefOption(int, TileX, OPT_TileX, 16); DefOption(int, TileY, OPT_TileY, 16); void UserKey(ImgKey &k) { } void UserMouseClick(ImgMouse &m) { m.view->Capture(DraggingView = m.Down()); if (m.Down()) { DocPos.x = m.x; DocPos.y = m.y; m.view->Focus(true); } } void UserMouseEnter(ImgMouse &m) { } void UserMouseExit(ImgMouse &m) { } int ZoomToFactor(int Zoom) { return Zoom < 0 ? 1 - Zoom : Zoom + 1; } template void PixToStr15(char *s, Px *p, LRgba64 *Diff) { sprintf(s, "%i,%i,%i (%x,%x,%x)", G5bitTo8bit(p->r), G5bitTo8bit(p->g), G5bitTo8bit(p->b), p->r, p->g, p->b); Diff->r = p->r; Diff->g = p->g; Diff->b = p->b; Diff->a = -1; } template void PixToStr16(char *s, Px *p, LRgba64 *Diff) { sprintf(s, "%i,%i,%i (%x,%x,%x)", G5bitTo8bit(p->r), G6bitTo8bit(p->g), G5bitTo8bit(p->b), p->r, p->g, p->b); Diff->r = p->r; Diff->g = p->g; Diff->b = p->b; Diff->a = -1; } template void PixToStrRgb(char *s, Px *p, LRgba64 *Diff) { sprintf(s, "%i,%i,%i (%x,%x,%x)", p->r, p->g, p->b, p->r, p->g, p->b); Diff->r = p->r; Diff->g = p->g; Diff->b = p->b; Diff->a = -1; } template void PixToStrRgba(char *s, Px *p, LRgba64 *Diff) { sprintf(s, "%i,%i,%i,%i (%x,%x,%x,%x)", p->r, p->g, p->b, p->a, p->r, p->g, p->b, p->a); Diff->r = p->r; Diff->g = p->g; Diff->b = p->b; Diff->a = p->a; } LAutoString DescribePixel(LSurface *pDC, LPoint Pos, LRgba64 *Diff) { char s[256] = "No Data"; int ch = 0; switch (pDC->GetColourSpace()) { case CsIndex8: { COLOUR c = pDC->Get(Pos.x, Pos.y); ch = sprintf_s(s, sizeof(s), "%i (%.2x)", c, c); break; } #define DescribeCase(type, method) \ case Cs##type: \ { \ L##type *p = (L##type*)((*pDC)[Pos.y]); \ if (p) method(s, p + Pos.x, Diff); \ break; \ } DescribeCase(Rgb15, PixToStr15) DescribeCase(Bgr15, PixToStr15) DescribeCase(Rgb16, PixToStr16) DescribeCase(Bgr16, PixToStr16) DescribeCase(Rgb24, PixToStrRgb) DescribeCase(Bgr24, PixToStrRgb) DescribeCase(Rgbx32, PixToStrRgb) DescribeCase(Bgrx32, PixToStrRgb) DescribeCase(Xrgb32, PixToStrRgb) DescribeCase(Xbgr32, PixToStrRgb) DescribeCase(Rgb48, PixToStrRgb) DescribeCase(Bgr48, PixToStrRgb) DescribeCase(Rgba32, PixToStrRgba) DescribeCase(Bgra32, PixToStrRgba) DescribeCase(Argb32, PixToStrRgba) DescribeCase(Abgr32, PixToStrRgba) DescribeCase(Rgba64, PixToStrRgba) DescribeCase(Bgra64, PixToStrRgba) default: LAssert(0); break; } return LAutoString(NewStr(s)); } void UserMouseMove(ImgMouse &m) { if (DraggingView) { LZoomView::ViewportInfo vp = AView->GetViewport(); int Factor = ZoomToFactor(vp.Zoom); if (vp.Zoom < 0) { // scaling down vp.Sx = DocPos.x - (Factor * m.ViewPt.x); vp.Sy = DocPos.y - (Factor * m.ViewPt.y); } else if (vp.Zoom > 0) { // scaling up vp.Sx = DocPos.x - (m.ViewPt.x / Factor); vp.Sy = DocPos.y - (m.ViewPt.y / Factor); } else { // 1:1 vp.Sx = DocPos.x - m.ViewPt.x; vp.Sy = DocPos.y - m.ViewPt.y; } AView->SetViewport(vp); BView->SetViewport(vp); CView->SetViewport(vp); OnViewportChange(); } if (AView && BView) { LSurface *a = AView->GetSurface(); LSurface *b = BView->GetSurface(); if (a && b) { LRgba64 ap, bp; ZeroObj(ap); ZeroObj(bp); LAutoString Apix = DescribePixel(a, LPoint(m.x, m.y), &ap); Pane[0]->Name(Apix); LAutoString Bpix = DescribePixel(b, LPoint(m.x, m.y), &bp); Pane[2]->Name(Bpix); int Channels = LColourSpaceChannels(a->GetColourSpace()); int TileX = App->TileX(); int TileY = App->TileY(); int Tx = m.x / TileX; int Ty = m.y / TileY; int Tile = Ty * TileX + Tx; char s[256]; int diffr = bp.r - ap.r; int diffg = bp.g - ap.g; int diffb = bp.b - ap.b; // int diffa = bp.a - ap.a; #define PercentDiff(c) \ diff##c, (ap.c ? (double)abs(diff##c) * 100 / ap.c : (diff##c ? 100.0 : 0.0)) int ch = sprintf_s(s, sizeof(s), "Mouse: %i, %i Tile: %i (%i, %i) Diff: %i(%.1f%%),%i(%.1f%%),%i(%.1f%%)", m.x, m.y, Tile, Tx, Ty, PercentDiff(r), PercentDiff(g), PercentDiff(b)); if (Channels > 3) ch += sprintf_s(s+ch, sizeof(s)-ch, ",%i(%.1f%%)", PercentDiff(b)); Pane[1]->Name(s); } } } void DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Src) { if (pDC->GetBits() == 32 || pDC->GetBits() == 64) { App->DrawBackground(View, pDC, Offset, Src); } } void DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Where) { } ImgTool *GetCurrentTool() { return NULL; } void SetStatusText(const char *Text, int Pane = 0) { } }; class FileCompareSelect : public LDialog { ImgWnd *App; public: LAutoString FileA, FileB; FileCompareSelect(ImgWnd *parent, const char *a, const char *b) { SetParent(App = parent); if (LoadFromResource(IDD_COMPARE_SELECT)) { LVariant Av, Bv; if (!a) { if (App->GetOptions()->GetValue(OPT_CompareLeft, Av)) a = Av.Str(); } SetCtrlName(IDC_FILEA, a); if (!b) { if (App->GetOptions()->GetValue(OPT_CompareRight, Bv)) b = Bv.Str(); } SetCtrlName(IDC_FILEB, b); } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_SELECTA: case IDC_SELECTB: { bool IsA = Ctrl->GetId() == IDC_SELECTA; int CtrlId = IsA ? IDC_FILEA : IDC_FILEB; char Path[MAX_PATH_LEN]; strcpy_s(Path, sizeof(Path), GetCtrlName(CtrlId)); LTrimDir(Path); - LFileSelect s; - s.Parent(this); - s.InitialDir(Path); - if (s.Open()) + auto s = new LFileSelect; + s->Parent(this); + s->InitialDir(Path); + s->Open([this, CtrlId](auto s, auto ok) { - SetCtrlName(CtrlId, s.Name()); - } + if (ok) + SetCtrlName(CtrlId, s->Name()); + delete s; + }); break; } case IDOK: { LVariant v; auto f = GetCtrlName(IDC_FILEA); FileA.Reset(NewStr(f)); App->GetOptions()->SetValue(OPT_CompareLeft, v = f); f = GetCtrlName(IDC_FILEB); FileB.Reset(NewStr(f)); App->GetOptions()->SetValue(OPT_CompareRight, v = f); EndModal(true); break; } case IDCANCEL: { EndModal(false); break; } } return LDialog::OnNotify(Ctrl, n); } }; void ImageCompare(ImgWnd *App, const char *FileA, const char *FileB) { - FileCompareSelect Dlg(App, FileA, FileB); if (!LFileExists(FileA) || !LFileExists(FileB)) { - if (!Dlg.DoModal()) + auto Dlg = new FileCompareSelect(App, FileA, FileB); + Dlg->DoModal([Dlg, App](auto d, auto code) { - return; - } - - FileA = Dlg.FileA; - FileB = Dlg.FileB; + if (code) + new CompareWnd(App, Dlg->FileA, Dlg->FileB); + delete d; + }); } - - new CompareWnd(App, FileA, FileB); + else + { + new CompareWnd(App, FileA, FileB); + } } diff --git a/src/Filters/GaussianBlur/GaussianBlurDlg.cpp b/src/Filters/GaussianBlur/GaussianBlurDlg.cpp --- a/src/Filters/GaussianBlur/GaussianBlurDlg.cpp +++ b/src/Filters/GaussianBlur/GaussianBlurDlg.cpp @@ -1,432 +1,432 @@ #include #include "../../Image.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Edit.h" #include "lgi/common/Bitmap.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/Palette.h" #define SCALE 10 #define DEFAULT_RADIUS 5 System32BitPixel *PixelAt(LSurface *Bmp, int x, int y) { if (y >= 0 && yY()) { uchar *Scan = (*Bmp)[y]; return (System32BitPixel*) (Scan + (x<<2)); } else { int Line = (NativeInt)(*Bmp)[1] - (NativeInt)(*Bmp)[0]; uchar *Scan = (*Bmp)[0]; return (System32BitPixel*) (Scan + (y * Line) + (x<<2)); } } void _blur_x(System32BitPixel *Dest, LSurface *Src, int y, int *k, int Size, bool Alpha) { int Rad = Size / 2; for (int x=0; xX(); x++) { int r = 0; int g = 0; int b = 0; int a = 0; int n = 0; System32BitPixel *s = PixelAt(Src, x-Rad, y); if (Alpha) { for (int i=0; i= 0 && j < Src->X()) { a += s->a * k[i]; n += k[i]; } } } else { for (int i=0; i= 0 && j < Src->X()) { r += s->r * k[i]; g += s->g * k[i]; b += s->b * k[i]; a += s->a * k[i]; n += k[i]; } } } System32BitPixel *d = Dest + x; if (n) { d->a = a / n; if (Alpha) { d->r = 0; d->g = 0; d->b = 0; } else { d->r = r / n; d->g = g / n; d->b = b / n; } } else { d->a = 0; } } } void _blur_y(System32BitPixel *Buf, LSurface *Src, int x, int *k, int Size, bool Alpha) { int Rad = Size / 2; for (int y=0; yY(); y++) { int r = 0; int g = 0; int b = 0; int a = 0; int n = 0; int LineStep = (NativeInt)(*Src)[1] - (NativeInt)(*Src)[0]; System32BitPixel *s = PixelAt(Src, x, y-Rad); if (Alpha) { for (int i=0; i= 0 && j < Src->Y()) { a += s->a * k[i]; n += k[i]; } } } else { for (int i=0; i= 0 && j < Src->Y()) { r += s->r * k[i]; g += s->g * k[i]; b += s->b * k[i]; a += s->a * k[i]; n += k[i]; } } } System32BitPixel *d = Buf + y; if (n) { d->a = a / n; if (Alpha) { d->r = 0; d->g = 0; d->b = 0; } else { d->r = r / n; d->g = g / n; d->b = b / n; } } else { d->a = 0; } } } bool GaussianBlur(LSurface *Dest, LSurface *Source, double Radius, Progress *Prog) { if (!Source || !Dest) return false; bool Status = false; bool OwnDc = false; LSurface *pDC = 0; if (Source->GetBits() == 32) { pDC = Source; } else { pDC = new LMemDC; if (pDC && pDC->Create(Source->X(), Source->Y(), System32BitColourSpace)) { pDC->Blt(0, 0, Source); OwnDc = true; } else { DeleteObj(pDC); } } if (pDC && Dest->Create(pDC->X(), pDC->Y(), System32BitColourSpace)) { double e = 2.7182818284590452353602874; int Bx = (int) ceil(Radius) * 2 + 1; int Kx[1000]; int By = (int) ceil(Radius) * 2 + 1; int Ky[1000]; memset(Kx, 0, sizeof(Kx)); memset(Ky, 0, sizeof(Ky)); // Generate lookup { #define Gaussian(x, stdev) ( \ pow \ ( \ e, \ -pow((double)x, 2) \ / \ (2 * pow(stdev, 2)) \ ) \ ) \ / \ ( \ stdev * sqrt(2.0 * LGI_PI) \ ) int i; for (i=0; iX(), Dest->Y()); System32BitPixel *Buf = new System32BitPixel[BufLen]; if (Buf) { for (int y=0; yY(); y++) { // Do x blur into the buffer _blur_x(Buf, pDC, y, Kx, Bx, false); // Copy buf into dest memcpy(PixelAt(Dest, 0, y), Buf, sizeof(System32BitPixel)*Dest->X()); } int LineStep = (NativeInt)(*Dest)[1] - (NativeInt)(*Dest)[0]; for (int x=0; xX(); x++) { // Do y blur into buffer _blur_y(Buf, Dest, x, Ky, By, false); // Copy the buffer into the dest... System32BitPixel *p = PixelAt(Dest, x, 0); for (int y=0; yY(); y++, ((char*&)p) += LineStep) { #define Copy(c) p->c = Buf[y].c; Copy(r); Copy(g); Copy(b); Copy(a); } } delete [] Buf; Status = true; } } if (OwnDc) { DeleteObj(pDC); } return Status; } class GaussianBlurDlg : public LDialog { ImgWnd *App; LScrollBar *RadiusS; LEdit *RadiusE; LSurface *Preview; LBitmap *Bmp; bool Working; public: GaussianBlurDlg(ImgWnd *a) { Working = true; Preview = 0; App = a; SetParent(App); if (LoadFromResource(IDD_GAUSSIAN_BLUR)) { MoveToCenter(); RadiusS = dynamic_cast(FindControl(IDC_RADIUS_S)); if (RadiusS) { RadiusS->SetLimits(10, SCALE * 40); RadiusS->SetPage(SCALE); RadiusS->Value(DEFAULT_RADIUS * SCALE); } RadiusE = dynamic_cast(FindControl(IDC_RADIUS_E)); if (RadiusE) { RadiusE->Value(DEFAULT_RADIUS); } Bmp = dynamic_cast(FindControl(IDC_GAUSSIAN_PREVIEW)); if (Bmp && App->GetDC()) { LRect c = Bmp->GetClient(); Preview = new LMemDC; if (Preview->Create(c.X(), c.Y(), App->GetDC()->GetColourSpace()) ) { LPalette *Pal = App->GetDC()->Palette(); if (Pal) { Preview->Palette(new LPalette(Pal), true); } Preview->Blt(0, 0, App->GetDC()); } } Working = false; Update(DEFAULT_RADIUS); } } ~GaussianBlurDlg() { DeleteObj(Preview); } void Update(double Radius) { if (Bmp) { int Start = LCurrentTime(); LMemDC *pBlur = new LMemDC; if (pBlur) { if (GaussianBlur(pBlur, Preview, Radius)) { int Time = LCurrentTime() - Start; Bmp->SetDC(pBlur); char s[256]; double Seconds = (double)Time / 1000; int Pixels = pBlur->X()*pBlur->Y(); double Rate = (double)Pixels / Seconds; int DocPixels = App->GetDC()->X() * App->GetDC()->Y(); double DocTime = DocPixels / Rate; sprintf(s, "Est. document time: %.1f seconds (%i K pixels/s).", DocTime, (int)(Pixels / Seconds / 1024.0)); SetCtrlName(IDC_INFO, s); } else { DeleteObj(pBlur); } } } } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_RADIUS_S: { if (!Working) { Working = true; char s[64]; double r = (double)RadiusS->Value() / SCALE; sprintf(s, "%.1f", r); RadiusE->Name(s); Update(r); Working = false; } break; } case IDC_RADIUS_E: { if (!Working) { Working = true; double r = atof(RadiusE->Name()); RadiusS->Value((uint64) (r * SCALE)); Update(r); Working = false; } break; } case IDOK: { double r = atof(RadiusE->Name()); LProgressDlg Dlg(this); LAutoPtr NewDC(new ImgMemDC); if (NewDC) { if (GaussianBlur(NewDC, App->GetDC(), r, Dlg.ItemAt(0))) { App->SetDC(NewDC); } else { break; } } else break; // Fall through } case IDCANCEL: { EndModal(c->GetId()); break; } } return 0; } }; void DoGaussianBlur(ImgWnd *Parent) { - GaussianBlurDlg Dlg(Parent); - Dlg.DoModal(); + auto Dlg = new GaussianBlurDlg(Parent); + Dlg->DoModal(NULL); } diff --git a/src/Filters/Levels/Levels.cpp b/src/Filters/Levels/Levels.cpp --- a/src/Filters/Levels/Levels.cpp +++ b/src/Filters/Levels/Levels.cpp @@ -1,242 +1,247 @@ #include "lgi/common/Lgi.h" #include "../../Image.h" #include "resdefs.h" #include "lgi/common/Bitmap.h" #include "lgi/common/Slider.h" class LevelsDlg : public LDialog { ImgWnd *App; LBitmap *Bmp; LArray Hist; LSlider *Shad, *High; LAutoPtr Graph; LAutoPtr Doc; public: LevelsDlg(ImgWnd *par) { SetParent(App = par); if (LoadFromResource(IDD_LEVELS)) { MoveToCenter(); GetViewById(IDC_HIST, Bmp); if (GetViewById(IDC_SHAD, Shad)) { Shad->SetLimits(0, 255); Shad->Value(0); } if (GetViewById(IDC_HIGH, High)) { High->SetLimits(0, 255); High->Value(255); } SetCtrlName(IDC_CLIP_SHADOWS, "0.02"); SetCtrlName(IDC_CLIP_HIGHLIGHTS, "0.02"); Doc.Reset(new LMemDC(App->GetDoc())); GenHist(); } } void GenHist() { LSurface *pDC = App->GetDoc(); if (pDC && High && Shad) { Hist.Length(256); int *h = &Hist[0]; memset(h, 0, sizeof(*h) * Hist.Length()); for (int y=0; yY(); y++) { switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *p = (System24BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { h[p->r]++; h[p->g]++; h[p->b]++; p++; } break; } case System32BitColourSpace: { System32BitPixel *p = (System32BitPixel*) (*pDC)[y]; System32BitPixel *e = p + pDC->X(); while (p < e) { h[p->r]++; h[p->g]++; h[p->b]++; p++; } break; } } } if (Graph.Reset(new LMemDC) && Graph->Create(Hist.Length(), Bmp->Y(), GdcD->GetColourSpace())) { Graph->Colour(LColour(L_WORKSPACE)); Graph->Rectangle(); //int x = Graph->X() - 1; int y = Graph->Y() - 1; int i, Max = 0; for (i=0; iColour(LColour(0, 0, 0)); for (i=0; iLine(i, y, i, y-(Hist[i]*y/Max)); } LMemDC *Tmp = new LMemDC(Graph); Tmp->Colour(LColour(255, 0, 0)); Tmp->Line(Shad->Value(), 0, Shad->Value(), Tmp->Y()); Tmp->Line(High->Value(), 0, High->Value(), Tmp->Y()); Bmp->SetDC(Tmp); } } } void Update() { LSurface *pDC = App->GetDoc(); if (pDC && High && Shad) { int lut[256]; int s = Shad->Value(); int h = High->Value(); if (s > h) { int t = s; s = h; h = t; } int size = h - s + 1; for (int i=0; i<256; i++) { if (i < s) lut[i] = 0; else if (i > h) lut[i] = 255; else lut[i] = (i - s) * 255 / size; } for (int y=0; yY(); y++) { switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *s = (System24BitPixel*) (*Doc)[y]; System24BitPixel *d = (System24BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { d->r = lut[s->r]; d->g = lut[s->g]; d->b = lut[s->b]; s++; d++; } break; } case System32BitColourSpace: { System32BitPixel *s = (System32BitPixel*) (*Doc)[y]; System32BitPixel *d = (System32BitPixel*) (*pDC)[y]; System32BitPixel *e = d + pDC->X(); while (d < e) { d->r = lut[s->r]; d->g = lut[s->g]; d->b = lut[s->b]; d->a = lut[s->a]; s++; d++; } break; } } } App->Update(0, true); } } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_SHAD: case IDC_HIGH: { if (Graph) { LMemDC *Tmp = new LMemDC(Graph); Tmp->Colour(LColour(255, 0, 0)); Tmp->Line(Shad->Value(), 0, Shad->Value(), Tmp->Y()); Tmp->Line(High->Value(), 0, High->Value(), Tmp->Y()); Bmp->SetDC(Tmp); Update(); } break; } case IDOK: { App->GetDoc()->Blt(0, 0, Doc); App->BeforeAction(); App->BeforeUpdate(); Update(); App->Update(0); App->AfterAction(); EndModal(true); break; } case IDCANCEL: { App->GetDoc()->Blt(0, 0, Doc); App->Update(0); EndModal(false); break; } } return 0; } }; -bool DoLevels(ImgWnd *App) +void DoLevels(ImgWnd *App, std::function Callback) { - LevelsDlg d(App); - return d.DoModal(); + auto d = new LevelsDlg(App); + d->DoModal([Callback](auto dlg, auto code) + { + if (Callback) + Callback(code); + delete dlg; + }); } diff --git a/src/GridDlg.cpp b/src/GridDlg.cpp --- a/src/GridDlg.cpp +++ b/src/GridDlg.cpp @@ -1,54 +1,52 @@ #include #include "Image.h" ////////////////////////////////////////////////////////////////////////////////////////////// // Grid Dialog GridDlg::GridDlg(ImgWnd *parent) { SetParent(App = parent); if (LoadFromResource(IDD_GRID)) { SetCtrlValue(IDC_GRID, App->DisplayGrid()); SetCtrlValue(IDC_GRID_SIZE, App->GridSize()); SetCtrlValue(IDC_TILE, App->DisplayTile()); SetCtrlValue(IDC_TABLE, App->TileType()); SetCtrlValue(IDC_TILE_X, App->TileX()); SetCtrlValue(IDC_TILE_Y, App->TileY()); MoveToMouse(); MoveOnScreen(); - - DoModal(); } } GridDlg::~GridDlg() { } int GridDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: { App->DisplayGrid(GetCtrlValue(IDC_GRID)); App->GridSize(GetCtrlValue(IDC_GRID_SIZE)); App->DisplayTile(GetCtrlValue(IDC_TILE)); App->TileType(GetCtrlValue(IDC_TABLE)); App->TileX(GetCtrlValue(IDC_TILE_X)); App->TileY(GetCtrlValue(IDC_TILE_Y)); // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId()); break; } } return 0; } diff --git a/src/Image.h b/src/Image.h --- a/src/Image.h +++ b/src/Image.h @@ -1,683 +1,683 @@ /*hdr ** FILE: Image.h ** AUTHOR: Matthew Allen ** DATE: 1/8/97 ** DESCRIPTION: Image paint application ** ** Copyright (C) 1997-1998 Matthew Allen ** fret@memecode.com */ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/ToolTabBar.h" #include "lgi/common/DocApp.h" #include "lgi/common/Icc.h" #include "lgi/common/OptionsFile.h" #include "lgi/common/Path.h" #include "lgi/common/ZoomView.h" #include "lgi/common/Box.h" #include "lgi/common/Scripting.h" #include "../src/common/Coding/ScriptingPriv.h" #include "resdefs.h" extern char AppName[]; //////////////////////////////////////////////////////////////////////////////////////////// // Defines #define OPT_ErrorMsg "ErrorMsg" #define OPT_Cancel "Cancel" #define OPT_ToolsOpen "ToolOpen" #define OPT_BackgroundColour "BgCol" #define OPT_Lang "CurLang" // Default tool button sizes #define TBS_X 19 #define TBS_Y 18 // Ver #define IMAGE_VER "1.11" // window messages #define IDM_EDITPALETTE 108 #define IDM_NEWWINDOW 109 #define IDM_FULLSCREEN 130 #define IDM_FITTOIMAGE 131 #define IDM_INFO 132 #define IDM_TOGGLEPALETTE 133 #define IDM_VIEWPALETTE 134 #define IDM_TRANSFORM 135 #define IDM_DIST 136 #define IDM_PROFILE 137 #define IDM_CHANGE_CD 140 #define IDM_LOAD_PAL 143 #define IDM_SAVE_PAL 144 #define IDM_EDIT_PAL 145 #define IDM_RGB_PAL 146 #define IDM_BGR_PAL 147 #define IDM_1BIT 150 #define IDM_4BIT 151 #define IDM_8BIT 152 #define IDM_24BIT 153 #define IDM_SCAN_NONE 160 #define IDM_SCAN_BYTE 161 #define IDM_SCAN_SHORT 162 #define IDM_SCAN_DWORD 163 #define IDM_Y_NORMAL 170 #define IDM_Y_FLIP 171 #define IDM_FREEMEM 192 #define IDM_TEXTEDIT 200 #define IDM_CUT 203 // not used #define IDM_FIND 206 #define IDM_GLYPHS 207 #define IDM_NEWFONTEDITOR 208 #define IDM_IMPORT 209 #define IDM_EXPORT 209 #define IDM_LOAD_FONT 210 #define IDM_PROPERTIES 211 #define IDM_ZOOM_X 212 #define IDM_ZOOM_Y 213 #define IDM_VIEW_X 214 #define IDM_VIEW_Y 215 // Image messages enum ImageEvents { IDM_SCALE_BASE = M_USER + 2000, IDM_LANG_BASE = M_USER + 3000, IDM_SCRIPTS_BASE = M_USER + 4000, M_OUTPUT = M_USER + 5000, M_LOAD, }; /////////////////////////////////////// // misc #define DEFAULT_X 500 #define DEFAULT_Y 400 #define STATUS_NORMAL 0 #define STATUS_COLOURINFO 1 #define STATUS_POSINFO 2 #define STATUS_DOCINFO 3 #define STATUS_MAX 4 #define RECENT_FILES 10 #define VIEW_PULSE_RATE 100 // classes #define IDM_OV_DRIVE 100 #define IDM_OV_START 200 #define IDM_OV_END 299 #define IDM_OV_CLOSE 300 #define IDM_OV_EXIT 301 #define OV_SELECTED 0x0001 #define OV_CURSOR 0x0002 // Events #define COL_CTRL_SET (M_USER + 600) #define COL_CTRL_GET (M_USER + 601) #define COL_CTRL_GET_BITS (M_USER + 603) #define COL_CTRL_SET_PAL (M_USER + 604) #define COL_CTRL_GET_PAL (M_USER + 605) #define GR_MIN_SPACER 1 #define GR_SPACER_WID 5 #define GREY_SQUARE_SHIFT 5 // Tool defines #define IDM_TOOL_EVENT 2000 #define IDM_TOOL_SET_WORKSPACE (IDM_TOOL_EVENT + 0) #define IEV_SET_FORE (IDM_TOOL_EVENT + 1) #define IEV_SET_BACK (IDM_TOOL_EVENT + 2) #define IEV_RETRACT (IDM_TOOL_EVENT + 3) #define IEV_UPDATE_DC (IDM_TOOL_EVENT + 4) #define IEV_SET_OP (IDM_TOOL_EVENT + 5) #define TOOL_PRIMITIVES 0 #define TOOL_BRUSH (TOOL_PRIMITIVES + 0) #define TOOL_SELECT_BRUSH (TOOL_PRIMITIVES + 1) #define TOOL_EYE_DROPPER (TOOL_PRIMITIVES + 2) #define TOOL_ZOOM (TOOL_PRIMITIVES + 3) #define TOOL_TEXT (TOOL_PRIMITIVES + 4) #define TOOL_FLOOD (TOOL_PRIMITIVES + 5) #define TOOL_FREEHAND (TOOL_PRIMITIVES + 6) #define TOOL_LINE (TOOL_PRIMITIVES + 7) #define TOOL_ELLIPSE (TOOL_PRIMITIVES + 8) #define TOOL_RECTANGLE (TOOL_PRIMITIVES + 9) #define TOOL_POLYGON (TOOL_PRIMITIVES + 10) #define TOOL_ERASE (TOOL_PRIMITIVES + 11) #define TOOLSET_MAX (TOOL_PRIMITIVES + 12) // #define TOOL_AIRBRUSH (TOOL_PRIMITIVES + 5) #define TOOL_OPTIONS (TOOL_PRIMITIVES + 14) #define TOOL_GUIDELINES (TOOL_OPTIONS + 0) #define TOOL_FILL (TOOL_OPTIONS + 1) #define TOOL_GRID (TOOL_OPTIONS + 2) #define TOOL_GRIDINC (TOOL_OPTIONS + 3) #define TOOL_TRANSPARENT_PASTE (TOOL_OPTIONS + 4) #define TOOL_DRAW_ALPHA (TOOL_OPTIONS + 5) #define TOOL_OTHER (TOOL_OPTIONS + 6) #define TOOL_UNDO (TOOL_OTHER + 0) #define TOOL_REDO (TOOL_OTHER + 1) #define TOOL_PALETTE (TOOL_OTHER + 2) #define TOOL_MAX (TOOL_OTHER + 3) #define IMGT_NONE 0 #define IMGT_LINE 1 #define IMGT_RECTANGLE 2 #define IMGT_CIRCLE 3 #define IMGT_SQUARE 4 #define IMGT_ELLIPSE 5 // This controls the speed of scrolling when the mouse is // being dragged outside the bounds of the current window. #define IMAGE_SELECTION_MS 100 //////////////////////////////////////////////////////////////////////////////////////////// #define IDC_TOOLS 800 //////////////////////////////////////////////////////////////////////////////////////////// // Options #define OPT_DisplayGrid "DspGrid" #define OPT_GridSize "GridSize" #define OPT_DisplayTile "DspTile" #define OPT_TileType "TileType" #define OPT_TileX "Tx" #define OPT_TileY "Ty" //////////////////////////////////////////////////////////////////////////////////////////// // Other includes #include "lgi/common/GdcTools.h" #include "ImageCommon.h" //////////////////////////////////////////////////////////////////////////////////////////// // Classes class ImageApp; class ImgView; class ImgDocument; class ImgTool; class LColourCtrl; class ImgWnd; //////////////////////////////////////////////////////////////////////////////////// // UI Headers #include "UiPaletteView.h" #include "lgi/common/ProgressStatusPane.h" class ImgMemDC : public LMemDC { public: ImgMemDC(int x = 0, int y = 0, LColourSpace cs = CsNone) : LMemDC(x, y, cs) {} ImgMemDC(LSurface *pDC) : LMemDC(pDC) {} ~ImgMemDC(); bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); }; // Generic Document class ImgDocument : public LBase { protected: LAutoPtr pDC; LAutoPtr ColourProfile; LArray Views; Progress *Meter; bool Dirty; int ZoomViewIdx; public: LXmlTag Props; ImgDocument(ImgMemDC *new_dc = NULL); ~ImgDocument(); ImgMemDC *GetDC() { return pDC; } ImgMemDC *ReleaseDC() { return pDC.Release(); } void SetDC(LAutoPtr pDC); void AddView(ImgView *v); void RemoveView(ImgView *v); void Update(LRect *Where = NULL); int GetZoom(); void SetZoom(int z); LIccProfile *GetColourProfile() { return ColourProfile; } void SetColourProfile(LAutoPtr Cp) { ColourProfile = Cp; } bool IsDirty() { return Dirty; } const char *Name(); bool Name(const char *p); Progress *GetProgress() { return Meter; } void SetProgress(Progress *Prg) { Meter = Prg; } void VerifyExtension(char *FileName, char *Type); bool Save(LView *Parent, const char *Name, char *Type = 0, LDom *Quiet = 0); bool Load(LView *Parent, const char *Name, char *Type = 0); }; struct ImgKey : public LKey { LPointF pt; ImgView *view; ImgKey(LKey &k, ImgView *v) { ((LKey&)*this) = k; view = v; } }; struct ImgMouse : public LMouse { LPointF pt; LPoint ViewPt; ImgView *view; ImgMouse(LMouse &m, ImgView *v) { ((LMouse&)*this) = m; ViewPt.x = m.x; ViewPt.y = m.y; view = v; } ImgMouse(const ImgMouse &m) : LMouse(m) { pt.x = m.pt.x; pt.y = m.pt.y; ViewPt = m.ViewPt; view = m.view; } }; // Virtual base class for image view events and settings class ImageViewTarget : public LZoomViewCallback { public: virtual ~ImageViewTarget() {} virtual void UserKey(ImgKey &k) = 0; virtual void UserMouseClick(ImgMouse &m) = 0; virtual void UserMouseEnter(ImgMouse &m) = 0; virtual void UserMouseExit(ImgMouse &m) = 0; virtual void UserMouseMove(ImgMouse &m) = 0; virtual ImgTool *GetCurrentTool() = 0; }; // Bitmap View Type class ImgView : public LZoomView { protected: ImageViewTarget *App; LMouse Last; int MouseFlags; int ScrollX, ScrollY; public: ImgView(ImageViewTarget *p) : LZoomView(p) { App = p; ScrollX = 0; ScrollY = 0; } ~ImgView() { } void OnMouseMove(LMouse &m); void OnMouseClick(LMouse &m); void OnMouseEnter(LMouse &m); void OnMouseExit(LMouse &m); bool OnKey(LKey &k); void OnPulse(); ImageViewTarget *GetApp() { return App; } }; class ImgEvent { // Image palette info LPalette *Palette; // Image delta info LAutoPtr Data; int Length; public: ImgEvent(LSurface *From, LSurface *To, LRect &Bounds); virtual ~ImgEvent(); bool Apply(LSurface *pDC); uint Sizeof(); }; // The main window #ifdef MainWnd #undef MainWnd #endif #define MainWnd ((ImgWnd*)LAppInst->AppWnd) class ImgWnd : public LDocApp, public LDom, public ImageViewTarget { protected: class ImgWndPrivate *d = NULL; // Doc ImgDocument Doc; // Views ------------------------------------- LBox *Splitter = NULL; // contains... ImgView *Zoom = NULL; ImgView *Normal = NULL; //-------------------------------------------- LStatusBar *Status = NULL; // contains... LStatusPane *StatusInfo[STATUS_MAX]; LProgressStatusPane *Meter = NULL; //-------------------------------------------- LSubMenu *File = NULL; LSubMenu *RecentFilesMenu = NULL; LSubMenu *Edit = NULL; LSubMenu *Image = NULL; LSubMenu *BrushMenu = NULL; LSubMenu *ToolsMenu = NULL; LSubMenu *Adjust = NULL; LSubMenu *Macro = NULL; LSubMenu *Help = NULL; //-------------------------------------------- LToolBar *Commands = NULL; // contains... LCommand ImageProperties; LCommand FileOpen; LCommand FileSave; // Separator LCommand Cut; LCommand Copy; LCommand Paste; // Separator LCommand OptGuideLines; LCommand OptFillPrimitives; LCommand OptShowGrid; LCommand GridSizeInc; LCommand OptTransparentPaste; // Separator LCommand EnableUndo; LCommand DiscardUndo; LCommand Undo; LCommand Redo; //-------------------------------------------- LPaletteUI *Palette = NULL; //-------------------------------------------- class ImgTools *Toolbar = NULL; // contains... LCommand Cmd[TOOL_MAX]; //-------------------------------------------- // Tools int CurrentTool = 0; ImgTool *Current = NULL; ImgTool *Tools[TOOL_MAX] = {}; //-------------------------------------------- // Options and Brush int Operator = 0; bool UseFore = true; bool FillPrim = true; bool ShowGrid = true; int AlphaLevel = 255; LAutoPtr BrushMap; void PostPasteBrush(LSurface *pBrush); // Stuff int OpLevel = 0; bool QuitWhenDone = false; ImgView *ClickedView = NULL; bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); // Temporary variables LPoint MouseStart; LPoint LastOffset; // Undo bool UndoEnabled = true; int UndoPos = 0; int CaptureUndoLevel; // if 0 { capture the undo info } List UndoQueue; LSurface *LastFrame = NULL; LRect LastUpdate; // UI void SetupUi(); void OnDcChange(); void OnUndoQueueChange(); void PushUndoEvent(ImgEvent *u); void EmptyUndoQueue(); void DrawOnAlpha(); void UpdateBackgroundMenu(); public: ImgWnd(); ~ImgWnd(); // Stuff void StartOp(); void EndOp(); bool OnRequestClose(bool OsShuttingDown); ImgTool *GetCurrentTool() { return Current; } void RunScript(LViewI *Parent, char *Script, const char *FileName, bool Debug); bool OpenHelp(const char *File = 0); void UpdateScriptsMenu(); // Properties int Op(); void Op(int o); LSurface *Brush(); void Brush(LAutoPtr Ptr); LSurface *GetDoc(); void SetDoc(LSurface *s); Progress *GetProgress() { return Doc.GetProgress(); } // Colour ImgCol Fore(); void Fore(ImgCol c); ImgCol Back(); void Back(ImgCol c); bool GetColour(ImgCol &c, bool Fore = true); COLOUR GetColour32(bool Fore = true); void DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Src); void DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Src); bool TransparentPaste(); void TransparentPaste(bool b); bool UseForeColour(); void UseForeColour(bool b); int Alpha(); void Alpha(int i); bool FillPrimitives(); void FillPrimitives(bool b); bool CrossHairs(); void CrossHairs(bool b); #define DefOption(_type, _name, _opt, _def) \ _type _name() { LVariant v = _def; GetOptions()->GetValue(_opt, v); return v.CastInt32(); } \ void _name(_type i) { LVariant v; GetOptions()->SetValue(_opt, v = i); OnSettingChange(); } DefOption(int, DisplayGrid, OPT_DisplayGrid, true); DefOption(int, GridSize, OPT_GridSize, 8); DefOption(int, DisplayTile, OPT_DisplayTile, true); DefOption(int, TileType, OPT_TileType, 0); DefOption(int, TileX, OPT_TileX, 16); DefOption(int, TileY, OPT_TileY, 16); // --------------------------------------------------------------------- // Methods void SetStatusText(const char *Text, int Pane = 0); void ZoomCenter(int x, int y); void ZoomTo(LRect r); // An action is a logical group of things that the user // would expect to get undone together virtual void BeforeAction(); // BeforeUpdate is before the tool modifies the DC // selects in all the required colours and paint options virtual void BeforeUpdate(LRect *a = NULL, bool PaletteChange = false); // Update is after the tool has modified the DC virtual void Update(LRect *a = NULL, bool Now = false); // After all related updates have taken place virtual void AfterAction(); // --------------------------------------------------------------------- // Tool Events void UserKey(ImgKey &k); void UserMouseClick(ImgMouse &m); void UserMouseEnter(ImgMouse &m); void UserMouseExit(ImgMouse &m); void UserMouseMove(ImgMouse &m); // --------------------------------------------------------------------- // Methods LView *GetClickedView() { return ClickedView; } ImgMemDC *GetDC(); LAutoPtr ReleaseDC(); bool SetDC(LAutoPtr pDC); void AttachTool(int i = -1); void DetachTool(); bool SerializeFile(LAutoPtr &pDC, const char *FileName, bool Write); void OffsetImage(int x, int y); LDom *GetQuiet(); void SetQuiet(LDom *b); bool Empty(); bool OpenFile(const char *FileName, bool Ro); bool SaveFile(const char *FileName); bool SerializeOptions(LOptionsFile *Options, bool Write); void GetFileTypes(LFileSelect *Dlg, bool Write); // --------------------------------------------------------------------- // Window int OnNotify(LViewI *Ctrl, LNotification n); LMessage::Result OnEvent(LMessage *Msg); int OnCommand(int Cmd, int Event, OsView Handle); void OnReceiveFiles(LArray &Files); void OnCreate(); bool OnMouseWheel(double Lines); void OnSettingChange(); bool OnKey(LKey &k); }; /// The image <-> script glue class class ImageScriptContext : public LScriptContext { ImgWnd *App; LBrush *Brush; LHashTbl,LPath*> Paths; LHashTbl,LSurface*> Bitmaps; LViewI *Parent; LFont *Fnt; LScriptEngine *Engine; char *GetIncludeFile(char *FileName); LPath *GetPath(char *name); void SetEngine(LScriptEngine *Eng); public: ImageScriptContext(ImgWnd *app, LViewI *parent); ~ImageScriptContext(); GHostFunc *GetCommands(); bool Create(LScriptArguments &Args); bool GetDocument(LScriptArguments &Args); bool SetDocument(LScriptArguments &Args); bool Load(LScriptArguments &Args); bool Save(LScriptArguments &Args); bool Rgb(LScriptArguments &Args); bool Rgba(LScriptArguments &Args); bool LinearGradient(LScriptArguments &Args); bool RadialGradient(LScriptArguments &Args); bool AddStop(LScriptArguments &Args); bool NewPath(LScriptArguments &Args); bool Rect(LScriptArguments &Args); bool RoundRect(LScriptArguments &Args); bool Circle(LScriptArguments &Args); bool MoveTo(LScriptArguments &Args); bool LineTo(LScriptArguments &Args); bool QuadTo(LScriptArguments &Args); bool CubicTo(LScriptArguments &Args); bool FillPath(LScriptArguments &Args); bool ErasePath(LScriptArguments &Args); bool PathWidth(LScriptArguments &Args); bool PathHeight(LScriptArguments &Args); bool MsgBox(LScriptArguments &Args); bool Font(LScriptArguments &Args); bool Text(LScriptArguments &Args); bool FontAscent(LScriptArguments &Args); bool FontHeight(LScriptArguments &Args); bool Blur(LScriptArguments &Args); bool SetFillRule(LScriptArguments &Args); bool SetDepth(LScriptArguments &Args); bool Resample(LScriptArguments &Args); bool Crop(LScriptArguments &Args); bool Difference(LScriptArguments &Args); bool Normalize(LScriptArguments &Args); bool Blt(LScriptArguments &Args); }; // UI #include "ColourCtrl.h" #include "ImageProperties.h" #include "GridDlg.h" #include "ColourDlg.h" #include "OperatorDlg.h" #include "TextDlg.h" #include "ReduceDlg.h" #include "lgi/common/Panel.h" class ImgTools : public LPanel { ImgWnd *App; int ToolsY; public: ImgTools(ImgWnd *app, int x, int y); void OnCreate(); void OnPaint(LSurface *pDC); }; extern void ImageCompare(ImgWnd *App, const char *FileA = NULL, const char *FileB = NULL); extern bool GaussianBlur(LSurface *Dest, LSurface *Source, double Radius, Progress *Prog = 0); extern bool IsPreMulAlpha(LSurface *pDC); extern void PreToPostMulAlpha(LSurface *pDC); extern void PostToPreMulAlpha(LSurface *pDC); extern void CreateScriptWindow(class ImgWnd *App, const char *ScriptFile = 0); -extern bool DoLevels(ImgWnd *App); +extern void DoLevels(ImgWnd *App, std::function Callback); extern void HslSplit(ImgWnd *App); extern void OpenHueGraph(ImgWnd *App, LSurface *pDC); extern bool IsHighDpi(); // Tool stuff #include "ImageTool.h" diff --git a/src/ImageApp.cpp b/src/ImageApp.cpp --- a/src/ImageApp.cpp +++ b/src/ImageApp.cpp @@ -1,4850 +1,4883 @@ /* ** FILE: ImageApp.cpp ** AUTHOR: Matthew Allen ** DATE: 9/6/98 ** DESCRIPTION: Image ** ** Copyright (C) 1998-1999, Matthew Allen ** fret@memecode.com */ #include #include "Image.h" #include "lgi/common/Token.h" #include "lgi/common/About.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/HashDom.h" #include "lgi/common/Jpeg.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/List.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Palette.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #include "resource.h" #include "IccUi.h" char AppName[] = "i.Mage"; char HelpFile[] = "index.html"; char OptionsFile[] = "image.r"; double ScalingFactors[] = { 0.15, 0.25, 0.37, 0.5, 0.75, 1.25, 1.5, 2.0, 3.0, 0 }; ///////////////////////////////////////////////////////////////////// class LJpegOptions : public LDialog { LScrollBar *Scroll; GdcJpeg *Jpeg; LSurface *pDC; void Update() { char Temp[256]; LMakePath(Temp, sizeof(Temp), LGetSystemPath(LSP_TEMP), "~jpeg.tmp"); LFile f; if (f.Open(Temp, O_WRITE)) { if (Jpeg->_Write(&f, pDC, (int)Quality, SubSample, LPoint(0, 0))) { int64 Size = f.GetSize(); if (Size >= 0) { char s[256]; LFormatSize(s, sizeof(s), Size); SetCtrlName(IDC_FILE_SIZE, s); } f.Close(); FileDev->Delete(Temp, false); } } } public: int64 Quality; LPoint Dpi; GdcJpeg::SubSampleMode SubSample; LJpegOptions(GdcJpeg *jpeg, LSurface *pdc) { Jpeg = jpeg; pDC = pdc; Dpi.x = Dpi.y = 600; LVariant Parent; if (Jpeg->Props->GetValue(LGI_FILTER_PARENT_WND, Parent) && Parent.Type == GV_GVIEW) { SetParent((LView*)Parent.Value.Ptr); } else { MoveToCenter(); } Scroll = 0; Quality = 50; SubSample = GdcJpeg::Sample_2x2_1x1_1x1; if (LoadFromResource(IDD_JPEG)) { if (GetViewById(IDC_QUALITY, Scroll)) { Scroll->SetPage(5); Scroll->SetLimits(0, 100); Scroll->Value(Quality); } SetCtrlValue(IDC_TABLE, SubSample); SetCtrlValue(IDC_X, Dpi.x); SetCtrlValue(IDC_Y, Dpi.y); } } void OnCreate() { Update(); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_QUALITY: { Quality = c->Value(); Update(); break; } case IDC_SUB1: case IDC_SUB2: case IDC_SUB3: case IDC_SUB4: { SubSample = (GdcJpeg::SubSampleMode)GetCtrlValue(IDC_TABLE); Update(); break; } case IDOK: { Quality = Scroll ? Scroll->Value() : -1; SubSample = (GdcJpeg::SubSampleMode)GetCtrlValue(IDC_TABLE); Dpi.x = (int)GetCtrlValue(IDC_X); Dpi.y = (int)GetCtrlValue(IDC_Y); // fall thru } case IDCANCEL: { EndModal(c->GetId()); break; } } return 0; } }; ///////////////////////////////////////////////////////////////////////////////////// bool IsPreMulAlpha(LSurface *pDC) { if (!pDC) return false; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->r > p->a || p->g > p->a || p->b > p->a) return false; p++; } } break; } } return true; } void PreToPostMulAlpha(LSurface *pDC) { if (!pDC) return; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->a) { p->r = (p->r * 255) / p->a; p->g = (p->g * 255) / p->a; p->b = (p->b * 255) / p->a; } p++; } } break; } case System24BitColourSpace: { if (pDC->AlphaDC()) { for (int y=0; yY(); y++) { System24BitPixel *p = (System24BitPixel*)((*pDC)[y]); uint8_t *a = (uint8_t*)((*pDC->AlphaDC())[y]); System24BitPixel *e = p + pDC->X(); while (p < e) { if (*a) { p->r = (p->r * 255) / *a; p->g = (p->g * 255) / *a; p->b = (p->b * 255) / *a; } p++; a++; } } } break; } } } void PostToPreMulAlpha(LSurface *pDC) { if (!pDC) return; switch (pDC->GetColourSpace()) { default: LAssert(0); break; case System32BitColourSpace: { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)((*pDC)[y]); System32BitPixel *e = p + pDC->X(); while (p < e) { p->r = (p->r * p->a) / 255; p->g = (p->g * p->a) / 255; p->b = (p->b * p->a) / 255; p++; } } break; } case System24BitColourSpace: { if (pDC->AlphaDC()) { for (int y=0; yY(); y++) { System24BitPixel *p = (System24BitPixel*)((*pDC)[y]); uint8_t *a = (uint8_t*)((*pDC->AlphaDC())[y]); System24BitPixel *e = p + pDC->X(); while (p < e) { p->r = (p->r * *a) / 255; p->g = (p->g * *a) / 255; p->b = (p->b * *a) / 255; p++; a++; } } } break; } } } int IcoCmp(LRect *a, LRect *b) { if (a->y2 < b->y1 || a->y1 > b->y2) { // Not overlapping Y, sort by Y return a->y1 - b->y1; } else { // Overlapping Y, sort by X return a->x1 - b->x1; } } bool CompactIcons(ImgWnd *App) { LSurface *pDC = App->GetDC(); int Tx = App->TileX(); int Ty = App->TileY(); if (!pDC) return false; LMemDC Done; Done.Create(pDC->X(), pDC->Y(), CsIndex8); Done.Colour(0); Done.Rectangle(); Done.Colour(255); bool Status = true; COLOUR k = pDC->Get(0,0); LArray Icons; int MaxX = 0, MaxY = 0; int FirstOversize = -1; for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if (Done.Get(x, y)) continue; if (pDC->Get(x, y) == k) continue; LRect &r = Icons.New(); r.x1 = r.x2 = x; r.y1 = r.y2 = y; while (true) { LRect a = r; int i; // Top for (i = a.x1 - 1; a.y1 > 0 && i <= a.x2 + 1; i++) { if (k != pDC->Get(i, a.y1 - 1) || k != pDC->Get(i, a.y1 - 2)) { a.y1--; break; } } // Bottom for (i = a.x1 - 1; a.y2 < (pDC->Y() - 1) && i <= a.x2 + 1; i++) { if (k != pDC->Get(i, a.y2 + 1) || k != pDC->Get(i, a.y2 + 2)) { a.y2++; break; } } // Left for (i = a.y1 - 1; a.x1 > 0 && i <= a.y2 + 1; i++) { if (k != pDC->Get(a.x1 - 1, i) || k != pDC->Get(a.x1 - 2, i)) { a.x1--; break; } } // Right for (i = a.y1 - 1; a.x2 < (pDC->X() - 1) && i <= a.y2 + 1; i++) { if (k != pDC->Get(a.x2 + 1, i) || k != pDC->Get(a.x2 + 2, i)) { a.x2++; break; } } if (a == r) break; r = a; } MaxX = MAX(MaxX, r.X()); MaxY = MAX(MaxY, r.Y()); Done.Rectangle(&r); if ((r.X() > Tx || r.Y() > Ty) && FirstOversize < 0) { FirstOversize = Icons.Length()-1; } } } if (Icons.Length() == 0) { LgiMsg(App, "No icons found.", AppName); } else if (LgiMsg(App, "Found %i icons, do you want to compact them into a %ix%i grid? (Max size: %ix%i, Oversize: %s)", AppName, MB_YESNO, Icons.Length(), Tx, Ty, MaxX, MaxY, FirstOversize >= 0 ? Icons[FirstOversize].GetStr() : "None") == IDYES) { Icons.Sort(IcoCmp); App->BeforeAction(); App->BeforeUpdate(); int Px = 0; int Py = 0; for (unsigned i=0; iGetColourSpace()); Tmp.Blt(0, 0, pDC, &r); pDC->Colour(k); pDC->Rectangle(&r); pDC->Blt(Px, Py, &Tmp); Px += Tx; if (Px + Tx >= pDC->X()) { Px = 0; Py += Ty; } // App->Invalidate(); // LYield(); #else pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Box(&r); #endif } App->Update(); App->AfterAction(); Status = true; } return Status; } ////////////////////////////////////////////////////////////////////////////// LColourSpace AllCs[] = { // CsIndex1, // CsIndex4, CsIndex8, // CsAlpha8, // CsArgb15, CsRgb15, // CsAbgr15, CsBgr15, CsRgb16, CsBgr16, CsRgb24, CsBgr24, CsRgbx32, CsBgrx32, CsXrgb32, CsXbgr32, CsRgba32, CsBgra32, CsArgb32, CsAbgr32, CsBgr48, CsRgb48, CsBgra64, CsRgba64, CsAbgr64, CsArgb64, CsNone, }; int x = 16, y = 16; int GetAlphaIndex(LColourSpace Cs) { for (int i=0; i<4; i++) { int Comp = (Cs >> ((4 - 1 - i) * 8)) & 0xff; LComponentType Type = (LComponentType) (uint8_t)(Comp >> 4); if (Type == CtAlpha) { return i; } } return -1; } void RgbaMark(LSurface *d) { #if 0 uint8 *p = (*d)[0]; int bytes = d->GetBits() >> 3; if (p) { if (bytes == 3 || bytes == 4) { LColourSpace Cs = d->GetColourSpace(); int AlphaIdx = GetAlphaIndex(Cs); for (int i=0; i 4) { int words = bytes >> 1; for (int i=0; iGetColourSpace() == CsIndex8) { LPalette *p = new LPalette; p->CreateCube(); d->Palette(p); } bool HasAlpha = GetAlphaIndex(d->GetColourSpace()) >= 0; for (int x=0; xX(); x++) { int n = x * 255 / d->X(); if (HasAlpha && Op == GDC_ALPHA) d->Colour(Rgba32(255, 0, 0, n), 32); else d->Colour(Rgb24(255, 255-n, 255-n), 24); d->VLine(x, 0, d->Y()-1); } RgbaMark(d); } void Blue(LSurface *d) { if (d->GetColourSpace() == CsIndex8) { LPalette *p = new LPalette; p->CreateCube(); d->Palette(p); } for (int y=0; yY(); y++) { int n = y * 255 / d->Y(); d->Colour(Rgb24(255-n, 255-n, 255), 24); d->HLine(0, d->X()-1, y); } RgbaMark(d); } LAutoPtr CreateTest(int Op, LColourSpace in, LColourSpace out) { LAutoPtr Src, Dst; if (Src.Reset(new LMemDC) && Src->Create(x, y, in, LSurface::SurfaceRequireExactCs)) { Red(Src, Op); } if (Src.Get() && Dst.Reset(new LMemDC) && Dst->Create(x, y, out, LSurface::SurfaceRequireExactCs)) { LSurface *d = Dst; LSurface *s = Src; Blue(d); d->Op(Op); #if 1 d->Blt(0, 0, s); #endif } return Dst; } class ImgItem : public LListItem { int Op; LColourSpace In, Out; LDisplayString Txt; LAutoPtr Img; bool Debug; public: ImgItem(int op, LColourSpace in, LColourSpace out, const char *txt) : Txt(LSysFont, txt) { Op = op; In = in; Out = out; Debug = false; Img = CreateTest(op, in, out); } void OnMeasure(LPoint *Info) { Info->x = (Img ? Img->X() : 0) + (LTableLayout::CellSpacing << 1) + Txt.X(); Info->y = MAX(Img ? Img->Y() : 0, Txt.Y()); } void OnPaint(LItem::ItemPaintCtx &Ctx) { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&Ctx); int Cx = Ctx.x1; if (Img) { Ctx.pDC->Blt(Ctx.x1, Ctx.y1, Img); Cx += Img->X() + LTableLayout::CellSpacing; } Txt.GetFont()->Colour(Ctx.Fore, Ctx.Back); Txt.GetFont()->Transparent(true); Txt.Draw(Ctx.pDC, Cx, Ctx.y1 + ((Ctx.Y()-Txt.Y())>>1)); } void OnMouseClick(LMouse &m) { if (m.Double()) { Debug = true; Img = CreateTest(Op, In, Out); Update(); } } }; class CsTestThread : public LThread { LList *Lst; bool Loop; public: CsTestThread(LList *lst) : LThread("CsTestThread") { Lst = lst; Loop = true; Run(); } ~CsTestThread() { Loop = false; while (!IsExited()) LSleep(1); } int Main() { int Ops[] = {GDC_SET, GDC_ALPHA}; for (int o = 0; o < CountOf(Ops); o++) { List a; for (int in_cs = 0; AllCs[in_cs]; in_cs++) { for (int out_cs = 0; Loop && AllCs[out_cs]; out_cs++) { LColourSpace InCs = AllCs[in_cs]; LColourSpace OutCs = AllCs[out_cs]; char Txt[256]; sprintf_s(Txt, sizeof(Txt), "%i:%s->%s", Ops[o], LColourSpaceToString(InCs), LColourSpaceToString(OutCs)); a.Insert(new ImgItem(Ops[o], InCs, OutCs, Txt)); } } Lst->Insert(a); } return 0; } }; class ColourTest : public LWindow { LList *Lst; LAutoPtr Thread; public: ColourTest() { LRect r(0, 0, (int) (GdcD->X()*0.8), (int) (GdcD->Y()*0.8)); SetPos(r); MoveToCenter(); if (Attach(0)) { AddView(Lst = new LList(100, 0, 0, 100, 100)); Lst->SetMode(LListColumns); Lst->AddColumn("Test", 200); Lst->SetPourLargest(true); AttachChildren(); Visible(true); Thread.Reset(new CsTestThread(Lst)); } } }; ////////////////////////////////////////////////////////////////////////////// extern void RegisterFileTypes(LView *Parent); void EnumAvailableFormats(LFileSelect *Select, int Cap) { int ExtLength = 0; LString::Array Ext; LArray Formats; for (int i=0; true; i++) { auto f = LFilterFactory::NewAt(i); if (f) { if (f->GetCapabilites() & Cap) { LVariant e; if (f->GetValue("Extension", e)) { auto t = e.LStr().SplitDelimit(","); for (auto s: t) { ExtLength += s.Length() + 3; Ext.Add(s); } } Formats.Add(f.Release()); } } else { break; } } char *ExtStr = NewStr("", ExtLength+1); if (ExtStr) { int Ch = 0; for (auto e: Ext) { Ch += sprintf_s(ExtStr+Ch, ExtLength-Ch, "%s*.%s", (*ExtStr) ? ";" : "", e.Get()); } Select->Type("Graphics", ExtStr, 0); DeleteArray(ExtStr); } for (auto f: Formats) { char ExtStr[256] = ""; LVariant e; LVariant n = "Format"; f->GetValue("Type", n); if (f->GetValue("Extension", e)) { LToken t(e.Str(), ","); int Ch = 0; for (unsigned n=0; nType(n.Str(), ExtStr, 1); } Select->Type("All files", LGI_ALL_FILES, 0); Formats.DeleteObjects(); } ////////////////////////////////////////////////////////////////////////////// /* void ImgView::SetScrollPos(int x, int y) { LLayout::SetScrollPos(x, y); ScrollX = (int) (((HScroll) ? HScroll->Value() : 0) * GetBlockSize()); ScrollY = (int) (((VScroll) ? VScroll->Value() : 0) * GetBlockSize()); } */ ////////////////////////////////////////////////////////////////////////////// enum BackgroundType { BgBlack, BgGrey, BgWhite, }; class ImgWndPrivate : public LVmDebuggerCallback { public: ImgWnd *App; SystemFunctions SysContext; ImageScriptContext Context; BackgroundType Background; bool CmdLineDone; LDom *Quiet; LArray ToolScripts; LMouse LastMousePos; ImgWndPrivate(ImgWnd *app) : Context(app, app) { App = app; Quiet = 0; Background = BgGrey; CmdLineDone = 0; } LVmDebugger *AttachVm(LVirtualMachine *Vm, LCompiledCode *Code, const char *Assembly) { return new LVmDebuggerWnd(App, this, Vm, Code, Assembly); } bool CompileScript(LAutoPtr &Output, const char *FileName, const char *Source) { LCompiler c; return c.Compile(Output, &SysContext, &Context, FileName, Source, NULL); } }; #ifdef WIN32 LDocApp::LIcon Icon = IDI_APP; #else LDocApp::LIcon Icon = "image-icon.png"; #endif ImgWnd::ImgWnd() : LDocApp(AppName, Icon) { LAppInst->AppWnd = this; d = new ImgWndPrivate(this); // Tool stuff ZeroObj(StatusInfo); CurrentTool = 0; Current = 0; ZeroObj(Tools); #ifdef MAC LgiGetResObj(false, "image"); #endif #ifdef WINDOWS #else SetIcon("icon64.png"); #endif // Startup UI if (_Create()) { LVariant v; GetOptions()->GetValue(OPT_BackgroundColour, v); d->Background = (BackgroundType)v.CastInt32(); #if 0 LAutoPtr t(new ImgMemDC(90, 100, 32)); t->Colour(LColour(255, 0, 0)); t->Rectangle(0, 0, 30, 100); t->Colour(LColour(0, 255, 0)); t->Rectangle(31, 0, 60, 100); t->Colour(LColour(0, 0, 255)); t->Rectangle(61, 0, 90, 100); SetDC(t); AttachTool(); #endif } else { LgiMsg(this, LLoadString(IDS_ERROR_UI), AppName); LExitApp(); } } ImgWnd::~ImgWnd() { SetDirty(false); Doc.RemoveView(Zoom); Doc.RemoveView(Normal); DetachTool(); LAppInst->AppWnd = 0; if (Toolbar) { LVariant v; GetOptions()->SetValue(OPT_ToolsOpen, v = Toolbar->Open()); } DeleteObj(Zoom); DeleteObj(Normal); DeleteObj(Palette); UndoQueue.DeleteObjects(); DeleteObj(LastFrame); LAutoPtr Null; SetDC(Null); for (int i=0; iGetValue(OPT_Lang, LangId)) { // Set the language to load... LAppInst->SetConfig("language", LangId.Str()); } #ifdef MAC SetWindow(this); #else DropTarget(true); #endif SetupUi(); AttachTool(); } bool ImgWnd::SerializeOptions(LOptionsFile *Options, bool Write) { if (Write) { LVariant v; Options->SetValue("EnabledUndo", v = EnableUndo.Value()); Options->SetValue("FillObjects", v = FillPrimitives()); Options->SetValue("TransparentPaste", v = TransparentPaste()); if (Palette) { LPalette *Pal = Palette->GetPalette(); ImgCol c; // Fore Col Palette->GetFore(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; } Options->SetValue("ForeColour", v = (int)c.c); Options->SetValue("ForeColourBits", v = c.Bits); // Back Col Palette->GetBack(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; } Options->SetValue("BackColour", v = (int)c.c); Options->SetValue("BackColourBits", v = c.Bits); } Options->SetValue("Operator", v = Op()); Options->SetValue("Alpha", v = Alpha()); if (Splitter) { Options->SetValue("SplitterPos", v = Splitter->Value()); } } else { LVariant i; if (Options->GetValue("Operator", i)) { Op(i.CastInt32()); } if (Options->GetValue("Alpha", i)) { Alpha(i.CastInt32()); } if (GetOptions()->GetValue("FillObjects", i)) { FillPrim = i.CastBool(); } if (GetOptions()->GetValue("TransparentPaste", i)) { TransparentPaste(i.CastBool()); } if (GetOptions()->GetValue("Alpha", i)) { Alpha(i.CastInt32()); } if (GetOptions()->GetValue("EnabledUndo", i)) { UndoEnabled = i.CastBool(); if (!UndoEnabled) { CaptureUndoLevel++; } } } return true; } void ImgWnd::AttachTool(int i) { DetachTool(); if (!Tools) return; LMouse m; Splitter->GetMouse(m); LView *Over = 0; if (Normal && Normal->GetPos().Overlap(m.x, m.y)) { Over = Normal; } else if (Zoom && Zoom->GetPos().Overlap(m.x, m.y)) { Over = Zoom; } if (Current) { if (Over) { Over->GetMouse(m); Over->OnMouseExit(m); } Current->Detach(); Current = NULL; } if (i >= TOOL_BRUSH && i < TOOL_MAX) { Current = Tools[i]; } else { Current = Tools[CurrentTool]; i = CurrentTool; } if (Current) { CurrentTool = i; Current->AttachTo(this); if (Over) { Over->GetMouse(m); Over->OnMouseEnter(m); } } if (Cmd[CurrentTool].ToolButton) { Cmd[CurrentTool].ToolButton->Value(true); } } void ImgWnd::DetachTool() { if (Current) { Current->OnDetach(); Current = NULL; } } bool IsHighDpi() { static auto Dpi = LScreenDpi(); #if 0 return true; // Testing... #else return Dpi.x >= 110; #endif } void ImgWnd::SetupUi() { RecentFilesMenu = NULL; if (!LgiGetResObj(true)) { return; } Tools[TOOL_BRUSH] = new ToolBrush(GetOptions()); Tools[TOOL_SELECT_BRUSH] = new ToolSelectBrush(GetOptions()); Tools[TOOL_EYE_DROPPER] = new ToolEyeDropper(GetOptions()); Tools[TOOL_ZOOM] = new ToolZoom(GetOptions()); Tools[TOOL_TEXT] = new ToolText(GetOptions()); Tools[TOOL_FLOOD] = new ToolFlood(GetOptions()); Tools[TOOL_FREEHAND] = new ToolFreehand(GetOptions()); Tools[TOOL_LINE] = new ToolLine(GetOptions()); Tools[TOOL_ELLIPSE] = new ToolEllipse(GetOptions()); Tools[TOOL_RECTANGLE] = new ToolRectangle(GetOptions()); Tools[TOOL_POLYGON] = new ToolPolygon(GetOptions()); Tools[TOOL_ERASE] = new ToolErase(GetOptions()); if (_LoadMenu("IDM_MENU")) { Menu->RemoveItem(0); auto i = Menu->FindItem(IDM_RECENT_NONE); if (i) { Set(i->GetParent()); } i = Menu->FindItem(IDM_SCALE_NONE); if (i) { auto ScaleMenu = i->GetParent(); if (ScaleMenu) { ScaleMenu->Empty(); int n=0; for (double *Scale=ScalingFactors; *Scale > 0.01; Scale++) { char s[256]; sprintf_s(s, sizeof(s), "%i%%", (int)(*Scale*100)); ScaleMenu->AppendItem(s, IDM_SCALE_BASE+n++, true); } } } EnableUndo.MenuItem = Menu->FindItem(IDM_ENABLE_UNDO); i = Menu->FindItem(IDM_NO_LANG); if (i) { auto Langs = i->GetParent(); if (Langs) { LResources *r = LgiGetResObj(); if (r) { LArray *l = r->GetLanguages(); if (l) { Langs->Empty(); for (int i=0; iLength(); i++) { LLanguage *k = LFindLang((*l)[i]); if (k) { Langs->AppendItem(k->Name, IDM_LANG_BASE + i, true); } } } } } } if ((i = Menu->FindItem(IDM_SCRIPTING))) { if ((ToolsMenu = i->GetParent())) { ToolsMenu->AppendSeparator(); UpdateScriptsMenu(); } } } if (IsHighDpi()) Commands = LgiLoadToolbar(this, "commands40.png", 40, 40); else Commands = LgiLoadToolbar(this, "commands20.png", TBS_X, TBS_Y); if (Commands) { Commands->Attach(this); enum AppIcons { ICON_NEW = 0, ICON_OPEN, ICON_SAVE, ICON_CUT, ICON_COPY, ICON_PASTE, ICON_GUIDELINES, ICON_FILL, ICON_GRID, ICON_GRIDINC, ICON_TRANSPARENT, ICON_ENABLEUNDO, ICON_UNDO, ICON_REDO, ICON_FLIPX, ICON_FLIPY, ICON_ROTCLOCK, ICON_ROTANTICLOCK, ICON_SCALE, ICON_MAX, }; ImageProperties.ToolButton = Commands->AppendButton(LLoadString(IDS_NEW), IDM_NEW, TBT_PUSH, true, ICON_NEW); FileOpen.ToolButton = Commands->AppendButton(LLoadString(IDS_OPEN), IDM_OPEN, TBT_PUSH, true, ICON_OPEN); FileSave.ToolButton = Commands->AppendButton(LLoadString(IDS_SAVE), IDM_SAVE, TBT_PUSH, true, ICON_SAVE); Commands->AppendSeparator(); Cut.ToolButton = Commands->AppendButton(LLoadString(IDS_CUT), IDM_CUT, TBT_PUSH, false, ICON_CUT); Copy.ToolButton = Commands->AppendButton(LLoadString(IDS_COPY), IDM_COPY_BRUSH, TBT_PUSH, true, ICON_COPY); Paste.ToolButton = Commands->AppendButton(LLoadString(IDS_PASTE), IDM_PASTE_BRUSH, TBT_PUSH, true, ICON_PASTE); Commands->AppendSeparator(); // OptGuideLines.ToolButton = Commands->AppendButton(LLoadString(IDS_GUIDELINES), TOOL_GUIDELINES, TBT_TOGGLE, false); OptFillPrimitives.ToolButton = Commands->AppendButton(LLoadString(IDS_FILLOBJECTS), TOOL_FILL, TBT_TOGGLE, true, ICON_FILL); OptShowGrid.ToolButton = Commands->AppendButton(LLoadString(IDS_GRID), TOOL_GRID, TBT_TOGGLE, true, ICON_GRID); OptShowGrid.ToolButton->SetNeedsRightClick(true); GridSizeInc.ToolButton = Commands->AppendButton(LLoadString(IDS_GRIDINCREMENT), TOOL_GRIDINC, TBT_PUSH, true, ICON_GRIDINC); GridSizeInc.ToolButton->SetNeedsRightClick(true); OptTransparentPaste.ToolButton = Commands->AppendButton(LLoadString(IDS_USETRANSPARENTPASTE), TOOL_TRANSPARENT_PASTE, TBT_TOGGLE, true, ICON_TRANSPARENT); // Commands->AppendButton(LLoadString(IDS_DRAWONALPHA), TOOL_DRAW_ALPHA, TBT_TOGGLE); Commands->AppendSeparator(); EnableUndo.ToolButton = Commands->AppendButton(LLoadString(IDS_ENABLEUNDO), IDM_ENABLE_UNDO, TBT_TOGGLE, true, ICON_ENABLEUNDO); Undo.ToolButton = Commands->AppendButton(LLoadString(IDS_UNDO), IDM_UNDO, TBT_PUSH, false, ICON_UNDO); Redo.ToolButton = Commands->AppendButton(LLoadString(IDS_REDO), IDM_REDO, TBT_PUSH, false, ICON_REDO); Commands->AppendSeparator(); Commands->AppendButton(LLoadString(IDS_FLIPX), IDM_FLIP_X, TBT_PUSH, true, ICON_FLIPX); Commands->AppendButton(LLoadString(IDS_FLIPY), IDM_FLIP_Y, TBT_PUSH, true, ICON_FLIPY); Commands->AppendButton(LLoadString(IDS_ROTATECLOCKWISE), IDM_ROTATE_90, TBT_PUSH, true, ICON_ROTCLOCK); Commands->AppendButton(LLoadString(IDS_ROTATEANTICLOCKWISE), IDM_ROTATE_270, TBT_PUSH, true, ICON_ROTANTICLOCK); Commands->AppendSeparator(); int n=0; for (double *Scale=ScalingFactors; *Scale > 0.01; Scale++, n++) { char s[256]; sprintf_s(s, sizeof(s), LLoadString(IDS_SCALE), (int)(*Scale*100)); Commands->AppendButton(s, IDM_SCALE_BASE + n, TBT_PUSH, true, ICON_SCALE + n); } OptFillPrimitives.Value(FillPrim); OptShowGrid.Value(ShowGrid); EnableUndo.Value(UndoEnabled); Commands->Customizable(GetOptions(), "Toolbar"); } Status = new LStatusBar; if (Status) { Status->Attach(this); StatusInfo[0] = Status->AppendPane("", 2); Status->AppendPane(Meter = new LProgressStatusPane); Doc.SetProgress(Meter); #define CreateStatusPane(i, width) \ { \ auto &p = StatusInfo[i]; \ if (p = Status->AppendPane("", width)) \ p->Sunken(true); \ } CreateStatusPane(STATUS_COLOURINFO, 400); CreateStatusPane(STATUS_POSINFO, 150); CreateStatusPane(STATUS_DOCINFO, 100); } PourAll(); auto Dpi = LScreenDpi(); int ToolX = IsHighDpi() ? 350 : 200; int ToolY = 0; Toolbar = new ImgTools(this, ToolX + 20, Commands ? Commands->Y() : 26); if (Toolbar) { Toolbar->Attach(this); LToolTabBar *t = new LToolTabBar(IDC_TOOLS); if (t) { t->SetNotify(this); t->Raised(false); t->HasBorder(false); t->Visible(false); t->IsVertical(true); auto ImgName = IsHighDpi() ? "tools40.png" : "tools20.png"; auto FileName = LFindFile(ImgName); if (FileName && t->SetBitmap(FileName, IsHighDpi() ? 40 : TBS_X, IsHighDpi() ? 40 : TBS_Y)) { for (int i=TOOL_BRUSH; iAppendControl(Cmd[i].ToolButton); } LRect r = Toolbar->GetClient(); r.Inset(1, 1); r.x1 += 16; r.x2 += ToolX + 21; r.y2 = r.y1 + (IsHighDpi() ? 500 : 270) - 1; ToolY += r.Y(); LRegion c(r); t->Pour(c); t->Attach(Toolbar); t->SetPos(r); LVariant ToolsOpen = false; GetOptions()->GetValue(OPT_ToolsOpen, ToolsOpen); Toolbar->Open(ToolsOpen.CastBool()); } else { DeleteObj(t); } } } Palette = new LPaletteUI(this); if (Palette) { LRect r(16, ToolY + 8, ToolX, ToolY + 220); Palette->SetPos(r); Palette->SetId(TOOL_PALETTE); Palette->Attach(Toolbar); Palette->Raised(false); { LRegion c(r); Palette->Pour(c); } LVariant col, bits; if (GetOptions()->GetValue("ForeColour", col) && GetOptions()->GetValue("ForeColourBits", bits)) { ImgCol c; c.c = col.CastInt32(); c.Bits = bits.CastInt32(); Palette->SetFore(c); } if (GetOptions()->GetValue("BackColour", col) && GetOptions()->GetValue("BackColourBits", bits)) { ImgCol c; c.c = col.CastInt32(); c.Bits = bits.CastInt32(); Palette->SetBack(c); } } Splitter = new LBox; if (Splitter) { Splitter->GetCss(true)->Padding("5px"); Splitter->GetCss()->Border("1px Outset"); /* Splitter->AddView(Zoom = new ImgView(this), false); if (Zoom) { Doc.AddView(Zoom); Zoom->SetNotify(this); } */ Splitter->AddView(Normal = new ImgView(this), false); if (Normal) { Normal->GetCss(true)->Border("1px Inset"); Doc.AddView(Normal); Normal->SetNotify(this); } /* LVariant i; if (GetOptions()->GetValue("SplitterPos", i)) { Splitter->Value(i.CastInt32()); } */ Splitter->Attach(this); } DropTarget(true); OptShowGrid.Value(DisplayGrid() != 0); PourAll(); Visible(true); } void ImgWnd::UpdateScriptsMenu() { char p[MAX_PATH_LEN]; if (ToolsMenu && LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p))) { LMakePath(p, sizeof(p), p, "Scripts"); d->ToolScripts.Length(0); LMenuItem *it; while ( ToolsMenu->Length() && (it = ToolsMenu->ItemAt(ToolsMenu->Length()-1))) { if (it->Separator() || it->Id() == IDM_SCRIPTING) break; it->Remove(); DeleteObj(it); } LDirectory dir; for (int b = dir.First(p); b; b = dir.Next()) { if (!dir.IsDir()) { char *e = LGetExtension(dir.GetName()); if (e && !_stricmp(e, "script")) { dir.Path(p, sizeof(p)); ToolsMenu->AppendItem(dir.GetName(), IDM_SCRIPTS_BASE+d->ToolScripts.Length(), true); d->ToolScripts.New().Reset(NewStr(p)); } } } if (d->ToolScripts.Length() == 0) ToolsMenu->AppendItem("(no scripts)", -1, false); } } void ImgWnd::PostPasteBrush(LSurface *pBrush) { if (Doc.GetDC()) { LPalette *Pal = Doc.GetDC()->Palette(); if (Doc.GetDC()->GetBits() <= 8 && pBrush->GetBits() <= 8) { RemapDC(pBrush, Pal); } else if (Doc.GetDC()->GetBits() != pBrush->GetBits()) { LReduceBitDepth(pBrush, Doc.GetDC()->GetBits(), Pal); } } if (OptTransparentPaste.Value()) { LSurface *pMask = pBrush->AlphaDC(); if (!pMask) { pBrush->HasAlpha(true); pMask = pBrush->AlphaDC(); } if (pMask) { uint Masks[] = { 0, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF}; uint Mask = Masks[(pBrush->GetBits() + 7) / 8]; ImgCol c = Back(); pMask->Colour(0xff); pMask->Rectangle(); pMask->Colour(0x00); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if ((pBrush->Get(x, y) & Mask) == (c.c & Mask)) { pMask->Set(x, y); } } } } } } bool ImgWnd::OpenHelp(const char *File) { auto Exe = LGetExePath(); bool Done = false; const char *Sub[] = { "./Help", "./Code/Help", "../Help", "../Code/Help", 0 }; for (int i = 0; Sub[i]; i++) { char p[300]; LMakePath(p, sizeof(p), Exe, Sub[i]); LMakePath(p, sizeof(p), p, File ? File : (char*)"index.html"); char *Hash = strrchr(p, '#'); if (Hash) *Hash = 0; if (LFileExists(p)) { if (Hash) *Hash = '#'; LExecute(p, NULL, Exe); Done = true; break; } } if (!Done) { LgiMsg(this, LLoadString(IDS_ERROR_NO_HELP), AppName, MB_OK); return false; } return true; } LMessage::Result ImgWnd::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_DESCRIBE: { char *Text = (char*) Msg->B(); if (Text) { SetStatusText(Text, STATUS_NORMAL); } break; } } return LDocApp::OnEvent(Msg); } bool ImgWnd::OnKey(LKey &k) { switch (k.vkey) { case LK_ESCAPE: { if (Current) Current->Key(k); return true; } } return false; } bool ImgWnd::OnMouseWheel(double Lines) { LMouse m; GetMouse(m); auto v = WindowFromPoint(m.x, m.y); if (v) { ImgView *view = dynamic_cast(v); if (view) { view->OnMouseWheel(Lines); } } return true; } void ImgWnd::DrawOnAlpha() { if (!Doc.GetDC()) return; DetachTool(); if (!Doc.GetDC()->HasAlpha()) { Doc.GetDC()->HasAlpha(true); } bool Doa = Doc.GetDC()->DrawOnAlpha(); Doc.GetDC()->DrawOnAlpha(!Doa); Update(); LAutoPtr Null; Brush(Null); AttachTool(); } class ColourItem : public LListItem { LColour Col; public: ColourItem(char *Name, LColour &c) { Col = c; SetText(Name, 1); char s[256]; sprintf_s(s, sizeof(s), "%02X %02X %02X", Col.r(), Col.g(), Col.b()); SetText(s, 2); } void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) { if (i == 0) { Ctx.pDC->Colour(Col); Ctx.pDC->Rectangle(&Ctx); } else { LListItem::OnPaintColumn(Ctx, i, c); } } }; class ShowSysColours : public LWindow { LList *Lst; public: ShowSysColours() { struct Colour { char *Name; int Def; } Cols[] = { #ifdef WIN32 {"CTLCOLOR_MSGBOX", CTLCOLOR_MSGBOX}, {"CTLCOLOR_EDIT", CTLCOLOR_EDIT}, {"CTLCOLOR_LISTBOX", CTLCOLOR_LISTBOX}, {"CTLCOLOR_BTN", CTLCOLOR_BTN}, {"CTLCOLOR_DLG", CTLCOLOR_DLG}, {"CTLCOLOR_SCROLLBAR", CTLCOLOR_SCROLLBAR}, {"CTLCOLOR_STATIC", CTLCOLOR_STATIC}, {"CTLCOLOR_MAX", CTLCOLOR_MAX}, {"COLOR_SCROLLBAR", COLOR_SCROLLBAR}, {"COLOR_BACKGROUND", COLOR_BACKGROUND}, {"COLOR_ACTIVECAPTION", COLOR_ACTIVECAPTION}, {"COLOR_INACTIVECAPTION", COLOR_INACTIVECAPTION}, {"COLOR_MENU", COLOR_MENU}, {"COLOR_WINDOW", COLOR_WINDOW}, {"COLOR_WINDOWFRAME", COLOR_WINDOWFRAME}, {"COLOR_MENUTEXT", COLOR_MENUTEXT}, {"COLOR_WINDOWTEXT", COLOR_WINDOWTEXT}, {"COLOR_CAPTIONTEXT", COLOR_CAPTIONTEXT}, {"COLOR_ACTIVEBORDER", COLOR_ACTIVEBORDER}, {"COLOR_INACTIVEBORDER", COLOR_INACTIVEBORDER}, {"COLOR_APPWORKSPACE", COLOR_APPWORKSPACE}, {"COLOR_HIGHLIGHT", COLOR_HIGHLIGHT}, {"COLOR_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT}, {"COLOR_BTNFACE", COLOR_BTNFACE}, {"COLOR_BTNSHADOW", COLOR_BTNSHADOW}, {"COLOR_GRAYTEXT", COLOR_GRAYTEXT}, {"COLOR_BTNTEXT", COLOR_BTNTEXT}, {"COLOR_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT}, {"COLOR_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT}, {"COLOR_3DDKSHADOW", COLOR_3DDKSHADOW}, {"COLOR_3DLIGHT", COLOR_3DLIGHT}, {"COLOR_INFOTEXT", COLOR_INFOTEXT}, {"COLOR_INFOBK", COLOR_INFOBK}, {"COLOR_HOTLIGHT", COLOR_HOTLIGHT}, {"COLOR_GRADIENTACTIVECAPTION", COLOR_GRADIENTACTIVECAPTION}, {"COLOR_GRADIENTINACTIVECAPTION", COLOR_GRADIENTINACTIVECAPTION}, {"COLOR_MENUHILIGHT", COLOR_MENUHILIGHT}, {"COLOR_MENUBAR", COLOR_MENUBAR}, #endif {0, 0} }; AddView(Lst = new LList(100, 0, 0, 100, 100)); Lst->SetPourLargest(true); Lst->AddColumn("Colour", 100); Lst->AddColumn("Name", 300); Lst->AddColumn("Hex", 150); Lst->Sunken(false); LRect r(0, 0, 1000, 900); SetPos(r); MoveToCenter(); if (Attach(0)) { for (Colour *c = Cols; c->Name; c++) { #ifdef WIN32 LColour col(GetSysColor(c->Def), 24); Lst->Insert(new ColourItem(c->Name, col)); #endif } Visible(true); } } }; void ImgWnd::UpdateBackgroundMenu() { auto m = GetMenu(); auto mi = m->FindItem(IDM_BG_BLACK); if (mi) mi->Checked(d->Background == BgBlack); mi = m->FindItem(IDM_BG_GREY); if (mi) mi->Checked(d->Background == BgGrey); mi = m->FindItem(IDM_BG_WHITE); if (mi) mi->Checked(d->Background == BgWhite); LVariant v; GetOptions()->SetValue(OPT_BackgroundColour, v = (int)d->Background); } int ImgWnd::OnCommand(int Cmd, int Event, OsView Handle) { switch (Cmd) { case IDM_SYS_COLOURS: { new ShowSysColours(); break; } case IDM_MAKE_OPAQUE: { ImgMemDC *pDC = GetDC(); if (pDC) { if (pDC->GetColourSpace() == System32BitColourSpace) { for (int y=0; yY(); y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; System32BitPixel *e = p + pDC->X(); while (p < e) { p->a = 255; p++; } } Update(); } else if (pDC->HasAlpha()) { pDC->HasAlpha(false); } } break; } case IDM_SCRIPTING: { CreateScriptWindow(this); break; } case IDM_COMPARE_IMAGES: { ImageCompare(this, GetCurFile()); break; } case IDM_COLOURPROF_VIEW: { if (Doc.GetColourProfile()) { new LIccProfileUi(Doc.GetColourProfile()); } else { LgiMsg(this, LLoadString(IDS_NO_COL_PROF), AppName); } break; } case IDM_COLOURPROF_LOAD: { if (!Doc.GetColourProfile()) { LAutoPtr c(new LIccProfile); Doc.SetColourProfile(c); } if (Doc.GetColourProfile()) { - LFileSelect s; - s.Parent(this); - s.Type("ICC Profile", "*.icc"); - if (s.Open()) + auto s = new LFileSelect; + s->Parent(this); + s->Type("ICC Profile", "*.icc"); + s->Open([this](auto s, auto ok) { - Doc.GetColourProfile()->Open(s.Name()); - } + if (ok) + Doc.GetColourProfile()->Open(s->Name()); + delete s; + }); } break; } case IDM_COLOURPROF_SAVE: { if (Doc.GetColourProfile()) { - LFileSelect s; - s.Parent(this); - s.Type("ICC Profile", "*.icc"); - if (s.Save()) + auto s = new LFileSelect; + s->Parent(this); + s->Type("ICC Profile", "*.icc"); + s->Save([this](auto s, auto ok) { - Doc.GetColourProfile()->Save(s.Name()); - } + if (ok) + Doc.GetColourProfile()->Save(s->Name()); + delete s; + }); } else { LgiMsg(this, LLoadString(IDS_NO_COL_PROF), AppName); } break; } case IDM_BG_BLACK: { d->Background = BgBlack; UpdateBackgroundMenu(); Update(); break; } case IDM_BG_GREY: { d->Background = BgGrey; UpdateBackgroundMenu(); Update(); break; } case IDM_BG_WHITE: { d->Background = BgWhite; UpdateBackgroundMenu(); Update(); break; } case IDM_GRID_SETTINGS: { - GridDlg Dlg(this); + auto Dlg = new GridDlg(this); + Dlg->DoModal(NULL); break; } case IDM_FLATTEN: { LSurface *pDC = GetDC(); if (pDC) { ImgMemDC Temp; if (Temp.Create(pDC->X(), pDC->Y(), pDC->GetColourSpace())) { if (pDC->Palette()) { Temp.Palette(new LPalette(pDC->Palette())); } LRect r(0, 0, pDC->X()-1, pDC->Y()-1); LPoint off(0, 0); DrawBackground(NULL, &Temp, off, &r); Temp.Op(GDC_ALPHA); Temp.Blt(0, 0, pDC); Temp.Op(GDC_SET); pDC->HasAlpha(false); pDC->Op(GDC_SET); pDC->Blt(0, 0, &Temp); Update(); } } break; } case IDM_TEST_COLOUR: { new ColourTest; break; } case 5000: case 5001: case 5002: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); int Min = MIN(pDC->X(), pDC->Y()); LAutoPtr Crop(new ImgMemDC(Min, Min, pDC->GetColourSpace())); LRect a(0, 0, Min, Min); if (Crop) { switch (Cmd) { case 5001: { if (pDC->X() > pDC->Y()) { a.Offset((pDC->X() - Min) / 2, 0); } else { a.Offset(0, (pDC->Y() - Min) / 2); } break; } case 5002: { if (pDC->X() > pDC->Y()) { a.Offset(pDC->X() - Min, 0); } else { a.Offset(0, pDC->Y() - Min); } break; } } Crop->Blt(0, 0, pDC, &a); LAutoPtr pNew(new ImgMemDC(76, 76, Crop->GetColourSpace())); if (pDC && pNew && ResampleDC(pNew, Crop, 0, Doc.GetProgress())) { SetDC(pNew); } } Update(); AttachTool(); } break; } case IDM_DISCARD_UNDO: { EmptyUndoQueue(); break; } case IDM_ENABLE_UNDO: { if (!Handle) { // This is for the menu item. EnableUndo.Value(!EnableUndo.Value()); } if (EnableUndo.Value()) { CaptureUndoLevel = 0; UndoEnabled = true; } else { CaptureUndoLevel = 1; UndoEnabled = false; } break; } case TOOL_BRUSH: case TOOL_SELECT_BRUSH: case TOOL_EYE_DROPPER: case TOOL_ZOOM: case TOOL_TEXT: // case TOOL_AIRBRUSH: case TOOL_FLOOD: case TOOL_FREEHAND: case TOOL_LINE: case TOOL_ELLIPSE: case TOOL_RECTANGLE: case TOOL_POLYGON: case TOOL_ERASE: { AttachTool(Cmd); break; } case TOOL_DRAW_ALPHA: { DrawOnAlpha(); break; } case IDM_DRAWONALPHA: { DrawOnAlpha(); break; } case IDM_UNDO: case IDM_REDO: { bool Redo = Cmd == IDM_REDO; CaptureUndoLevel++; if (UndoQueue.Length() > 0 && Doc.GetDC()) { if ( (Redo && UndoPos < UndoQueue.Length() - 1) || (!Redo && UndoPos >= 0)) { if (Redo) UndoPos += 1; ImgEvent *u = UndoQueue.ItemAt(limit(UndoPos, 0, UndoQueue.Length()-1)); if (u) { u->Apply(Doc.GetDC()); if (!Redo) UndoPos += -1; Update(); LPalette *Pal = Doc.GetDC()->Palette(); if (Pal) { Palette->SetPalette(Pal); } OnUndoQueueChange(); } } } CaptureUndoLevel--; break; } case IDM_NEW: { EmptyUndoQueue(); CaptureUndoLevel++; DetachTool(); - class ImageProperties Dlg(this); - if (Dlg.DoModal()) + auto Dlg = new ImagePropertiesDlg(this); + Dlg->DoModal([this, Dlg](auto dialog, auto code) { + LAutoPtr dlg(dialog); + if (!code) + return; + LAutoPtr pDC = ReleaseDC(); - - if (pDC && Dlg.KeepData) + if (pDC && Dlg->KeepData) { // do any resizing - if (Dlg.ScaleOption == IMGPROP_SCALE_RESAMPLE) + if (Dlg->ScaleOption == IMGPROP_SCALE_RESAMPLE) { - if (Dlg.x != pDC->X() || - Dlg.y != pDC->Y()) + if (Dlg->x != pDC->X() || + Dlg->y != pDC->Y()) { // change res - LAutoPtr pNew(new ImgMemDC(Dlg.x, Dlg.y, pDC->GetColourSpace())); + LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, pDC->GetColourSpace())); if (pNew) { if (pDC->Palette()) pNew->Palette(new LPalette( pDC->Palette() )); ResampleDC(pNew, pDC, 0, Meter); if (Meter) { Meter->Value(0); } pDC = pNew; } } } else { - LAutoPtr pNew(new ImgMemDC(Dlg.x, Dlg.y, pDC->GetColourSpace())); + LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, pDC->GetColourSpace())); if (pNew) { LAssert(pDC); if (!pDC) - break; + return; // blank - pNew->Colour(Dlg.Background); + pNew->Colour(Dlg->Background); pNew->Rectangle(); // copy data int Nx = pNew->X() - pDC->X(); int Ny = pNew->Y() - pDC->Y(); - switch (Dlg.AlignType) + switch (Dlg->AlignType) { default: case IDC_LEFT_TOP: { pNew->Blt(0, 0, pDC); break; } case IDC_CENTER_TOP: { pNew->Blt(Nx/2, 0, pDC); break; } case IDC_RIGHT_TOP: { pNew->Blt(Nx, 0, pDC); break; } case IDC_LEFT_CENTER: { pNew->Blt(0, Ny/2, pDC); break; } case IDC_CENTERED: { pNew->Blt(Nx/2, Ny/2, pDC); break; } case IDC_RIGHT_CENTER: { pNew->Blt(Nx, Ny/2, pDC); break; } case IDC_LEFT_BOTTOM: { pNew->Blt(0, Ny, pDC); break; } case IDC_CENTER_BOTTOM: { pNew->Blt(Nx/2, Ny, pDC); break; } case IDC_RIGHT_BOTTOM: { pNew->Blt(Nx, Ny, pDC); break; } } pDC = pNew; } } // do any bit conversion if (pDC && - pDC->GetBits() != Dlg.Bits) + pDC->GetBits() != Dlg->Bits) { if (pDC->GetBits() > 8 && - Dlg.Bits <= 8) + Dlg->Bits <= 8) { // reduce colour depth from // true colour to paletted - LReduceDlg Reduce(this); - if (Reduce.PalType >= 0) + auto Reduce = new LReduceDlg(this); + Reduce->DoModal([this, Dlg, Reduce, pDC](auto dlg, auto code) { - LReduceBitDepth(pDC, Dlg.Bits, Reduce.Palette, &Reduce); - } + if (code) + { + if (Reduce->PalType >= 0) + LReduceBitDepth(pDC, Dlg->Bits, Reduce->Palette, Reduce); + else + LAssert(!"What now?"); + } + delete dlg; + }); } else { - LAutoPtr pNew(new ImgMemDC(Dlg.x, Dlg.y, LBitsToColourSpace(Dlg.Bits))); + LAutoPtr pNew(new ImgMemDC(Dlg->x, Dlg->y, LBitsToColourSpace(Dlg->Bits))); if (pNew) { if (pDC->GetBits() > 24) { - if (!Dlg.Background.IsValid()) + if (!Dlg->Background.IsValid()) { LRect r(0, 0, pNew->X()-1, pNew->Y()-1); LPoint off(0, 0); DrawBackground(NULL, pNew, off, &r); } else { - pNew->Colour(Dlg.Background); + pNew->Colour(Dlg->Background); pNew->Rectangle(); } pNew->Op(GDC_ALPHA); } pNew->Blt(0, 0, pDC); pDC = pNew; } } } } else { - LAutoPtr Dc(new ImgMemDC(Dlg.x, Dlg.y, LBitsToColourSpace(Dlg.Bits))); + LAutoPtr Dc(new ImgMemDC(Dlg->x, Dlg->y, LBitsToColourSpace(Dlg->Bits))); if (Dc) { // blank - Dc->Colour(Dlg.Background); + Dc->Colour(Dlg->Background); Dc->Rectangle(); SetDC(Dc); } } if (pDC) { if (pDC->GetBits() <= 8) { LPalette *p = 0; LPalette *OldPal = pDC->Palette(); if (OldPal) { - if (Dlg.Palette) + if (Dlg->Palette) { - if (Dlg.RemapPalette && - *Dlg.Palette != *OldPal) + if (Dlg->RemapPalette && + *Dlg->Palette != *OldPal) { // Remap to the new palette uchar Lut[256]; for (int i=0; iMatchRgb(Rgb24(Rgb->r, Rgb->g, Rgb->b)); + Lut[i] = Dlg->Palette->MatchRgb(Rgb24(Rgb->r, Rgb->g, Rgb->b)); } else { Lut[i] = 0; } } for (int y=0; yY(); y++) { uchar *Start = (*pDC)[y]; if (Start) { uchar *End = Start + pDC->X(); while (Start < End) { //GdcRGB *Old = (*OldPal)[*Start]; //GdcRGB *New = (*Dlg.Palette)[Lut[*Start]]; *Start = Lut[*Start]; Start++; } } } - p = Dlg.Palette; - Dlg.Palette = 0; + p = Dlg->Palette; + Dlg->Palette = 0; } } } else { - if (Dlg.KeepData && + if (Dlg->KeepData && pDC && pDC->Palette()) { p = new LPalette(pDC->Palette()); } - else if (Dlg.Palette) + else if (Dlg->Palette) { - p = new LPalette(Dlg.Palette); + p = new LPalette(Dlg->Palette); } else { p = new LPalette; if (p && p->SetSize()) { GdcRGB *rgb = (*p)[0]; rgb->r = rgb->g = rgb->b = 0; rgb++; for (int i=1; i<256; i++, rgb++) { rgb->r = rgb->g = rgb->b = 0xFF; } } } } if (p) { pDC->Palette(p, true); } } SetDC(pDC); } - if (!Dlg.KeepData) + if (!Dlg->KeepData) { SetCurFile(0); } - } + }); AttachTool(); CaptureUndoLevel--; break; } case IDM_EXIT: { LCloseApp(); break; } case IDM_OFFSET: { if (GetDC()) { - LInput Dlg(this, "0,0", LLoadString(IDS_ENTEROFFSET), LLoadString(IDS_OFFSETDOCUMENT)); - if (Dlg.DoModal() == IDOK && - Dlg.GetStr()) + auto Dlg = new LInput(this, "0,0", LLoadString(IDS_ENTEROFFSET), LLoadString(IDS_OFFSETDOCUMENT)); + Dlg->DoModal([this,Dlg](auto dlg, auto code) { - LString::Array a = Dlg.GetStr().SplitDelimit(", "); - if (a.Length() == 2) - OffsetImage(a[0].Int(), a[1].Int()); - else - LgiMsg(this, "Not in the format '##,##'", "Error"); - } + if (code == IDOK) + { + LString::Array a = Dlg->GetStr().SplitDelimit(", "); + if (a.Length() == 2) + OffsetImage(a[0].Int(), a[1].Int()); + else + LgiMsg(this, "Not in the format '##,##'", "Error"); + } + delete dlg; + }); } break; } case IDM_OFFSET_HALF: { if (GetDC()) { OffsetImage(GetDC()->X()/2, GetDC()->Y()/2); } break; } case IDM_OFFSET_BACK: { if (LastOffset.x != -1 && LastOffset.y != -1) { OffsetImage(-LastOffset.x, -LastOffset.y); } break; } case IDM_BRIGHT_CONT: { extern void DoBrightnessContrast(ImgWnd *Parent); DoBrightnessContrast(this); break; } case IDM_ADJUST_LEVELS: { - DoLevels(this); + DoLevels(this, NULL); break; } case IDM_GAUSSIAN_BLUR: { extern void DoGaussianBlur(ImgWnd *Parent); DoGaussianBlur(this); break; } case IDM_HUE_GRAPH: { OpenHueGraph(this, Doc.GetDC()); break; } case IDM_COPY: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LClipBoard Clip(this); Clip.Bitmap(pDC, true); AttachTool(); } break; } case IDM_PASTE: { if (GetDC()) { // Paste as the brush, as we already have a document LClipBoard Clip(this); LSurface *pDC = Clip.Bitmap(); if (pDC) { BrushMap.Reset(pDC); PostPasteBrush(BrushMap); AttachTool(TOOL_BRUSH); } } else { // Paste as the document LClipBoard Clip(this); Clip.Bitmap([this](auto bmp, auto msg) { if (bmp) { LAutoPtr MemDC(new ImgMemDC(bmp)); SetDC(MemDC); } else if (msg) { LgiTrace("%s:%i - clipboard err: %s\n", _FL, msg.Get()); } }); } break; } case IDM_COPY_CODE: { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LClipBoard Clip(this); Clip.Empty(); char Str[1024] = ""; int Ch = 0; LStringPipe Buf; int Len = ((pDC->X() * pDC->GetBits()) + 31) / 32; for (int y=0; yY(); y++) { ulong *Ptr = (ulong*) (*pDC)[y]; if (Ptr) { for (int i=0; i= 800) { Buf.Push(Str); Buf.Push("\r\n"); Str[0] = 0; Ch = 0; } } } } if (strlen(Str) > 0) { Buf.Push(Str); Buf.Push("\r\n"); Str[0] = 0; } char *Final = Buf.NewStr(); if (Final) { Clip.Text(Final); DeleteArray(Final); } AttachTool(); } break; } case IDM_PASTE_CODE: { LClipBoard c(this); LAutoString Txt(c.Text()); if (Txt) { LArray a; LToken Lines(Txt, "\r\n"); unsigned MaxX = 0; for (unsigned y=0; y Bmp(new ImgMemDC); int Height = (a.Length() + (MaxX - 1)) / MaxX; if (Bmp->Create(MaxX, Height, System32BitColourSpace)) { Bmp->Colour(0); Bmp->Rectangle(); for (unsigned i=0; iColour ( LColour ( (c >> 16) & 0xff, (c >> 8) & 0xff, (c) & 0xff ) ); Bmp->Set ( i % MaxX, i / MaxX ); } DetachTool(); if (!GetDC()) { SetDC(Bmp); } else { LAutoPtr Tmp; Tmp.Reset(Bmp.Release()); Brush(Tmp); } Update(); AttachTool(); } } break; } case IDM_COPY_BRUSH: { LSurface *pBrush = Brush(); if (pBrush) { LSurface *pDC = GetDC(); if (pDC && pDC->Palette()) { LPalette *Pal = new LPalette(pDC->Palette()); if (Pal) { pBrush->Palette(Pal); } } LClipBoard Clip(this); Clip.Empty(); if (!Clip.Bitmap(pBrush)) LgiMsg(this, "Failed to paste bitmap to clipboard.", AppName); } break; } case IDM_PASTE_BRUSH: { LClipBoard Clip(this); Clip.Bitmap([this](auto pDC, auto Msg) { if (pDC) { BrushMap = pDC; PostPasteBrush(BrushMap); AttachTool(TOOL_BRUSH); } else { LgiMsg(this, Msg, AppName); } }); break; } case IDM_BRUSH_TO_DOC: { LSurface *pDC = GetDC(); LSurface *pBrush = Brush(); if (pDC && pBrush) { DetachTool(); if (pDC->Create(pBrush->X(), pBrush->Y(), pBrush->GetColourSpace())) { if (pBrush->Palette()) { pDC->Palette(new LPalette(pBrush->Palette())); } if (pBrush->HasAlpha()) { pDC->HasAlpha(true); pDC->AlphaDC()->Blt(0, 0, pBrush->AlphaDC()); } pDC->Blt(0, 0, pBrush); LAutoPtr n; Brush(n); } OnDcChange(); Update(); AttachTool(); } break; } case IDM_OPEN_BRUSH: { - LFileSelect Select; - - Select.Parent(this); - EnumAvailableFormats(&Select, FILTER_CAP_READ); - - if (Select.Open()) + auto Select = new LFileSelect; + + Select->Parent(this); + EnumAvailableFormats(Select, FILTER_CAP_READ); + + Select->Open([this](auto s, auto ok) { - if (SerializeFile(BrushMap, Select.Name(), false)) + if (ok) { - PostPasteBrush(BrushMap); - AttachTool(TOOL_BRUSH); + if (SerializeFile(BrushMap, s->Name(), false)) + { + PostPasteBrush(BrushMap); + AttachTool(TOOL_BRUSH); + } + else LgiMsg(this, "Failed to open file '%s'", AppName, MB_OK, s->Name()); } - else LgiMsg(this, "Failed to open file '%s'", AppName, MB_OK, Select.Name()); - } + delete s; + }); break; } case IDM_SAVE_BRUSH: { if (BrushMap) { - LFileSelect Select; - - Select.Parent(this); - EnumAvailableFormats(&Select, FILTER_CAP_WRITE); - - if (Select.Save()) + auto Select = new LFileSelect; + + Select->Parent(this); + EnumAvailableFormats(Select, FILTER_CAP_WRITE); + + Select->Save([this](auto s, auto ok) { - SerializeFile(BrushMap, Select.Name(), true); - } + if (ok) + SerializeFile(BrushMap, s->Name(), true); + delete s; + }); } break; } case IDM_REGISTER_TYPES: { RegisterFileTypes(this); break; } case IDM_INVERT: { DetachTool(); if (InvertDC(Doc.GetDC())) { Update(); } AttachTool(); break; } case IDM_GREY: { DetachTool(); LAutoPtr NewDC(new ImgMemDC); if (NewDC) { if (GreyScaleDC(NewDC, Doc.GetDC())) { Doc.SetDC(NewDC); OnDcChange(); Update(); } } AttachTool(); break; } case IDM_ROTATE_90: { if (GetDC()) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 90); if (pDC->AlphaDC()) { RotateDC(pDC->AlphaDC(), 90); } SetDC(pDC); } break; } case IDM_ROTATE_180: { if (GetDC()) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 180); if (pDC->AlphaDC()) RotateDC(pDC->AlphaDC(), 180); SetDC(pDC); } break; } case IDM_ROTATE_270: { LSurface *pDC = GetDC(); if (pDC) { LAutoPtr pDC = ReleaseDC(); RotateDC(pDC, 270); if (pDC->AlphaDC()) RotateDC(pDC->AlphaDC(), 270); SetDC(pDC); } break; } /* case IDM_ROTATE_FREE: { DetachTool(); if (InvertDC(Doc.pDC)) { Update(); } AttachTool(); break; } */ case IDM_FLIP_X: { DetachTool(); LSurface *pDC = Doc.GetDC(); if (pDC) { FlipXDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipXDC(pDC); } Update(); } AttachTool(); break; } case IDM_FLIP_Y: { DetachTool(); LSurface *pDC = Doc.GetDC(); if (pDC) { FlipYDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipYDC(pDC); } Update(); } AttachTool(); break; } case IDM_ROTATE_BRUSH_90: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 90); AttachTool(); } break; } case IDM_ROTATE_BRUSH_180: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 180); AttachTool(); } break; } case IDM_ROTATE_BRUSH_270: { LSurface *pDC = Brush(); if (pDC) { DetachTool(); RotateDC(pDC, 270); AttachTool(); } break; } case IDM_FLIP_BRUSH_X: { DetachTool(); LSurface *pDC = Brush(); if (pDC) { FlipXDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipXDC(pDC); } Update(); } AttachTool(); break; } case IDM_FLIP_BRUSH_Y: { DetachTool(); LSurface *pDC = Brush(); if (pDC) { FlipYDC(pDC); pDC = pDC->AlphaDC(); if (pDC) { FlipYDC(pDC); } Update(); } AttachTool(); break; } case IDM_HELP: { OpenHelp(); break; } case IDM_ABOUT: { LAbout Dlg( this, AppName, IMAGE_VER, LLoadString(IDS_ABOUT), "icon128.png", "http://www.memecode.com/image.php", "fret@memecode.com"); break; } default: { if (Cmd >= IDM_LANG_BASE && Cmd < IDM_LANG_BASE + 50) { int i = Cmd - IDM_LANG_BASE; LResources *r = LgiGetResObj(); if (r) { LArray *l = r->GetLanguages(); if (l) { LVariant v; GetOptions()->SetValue(OPT_Lang, v = (*l)[i]); LgiMsg(this, LLoadString(IDS_RESTART), AppName); } } } else if (Cmd >= IDM_SCALE_BASE && Cmd < IDM_SCALE_BASE + CountOf(ScalingFactors)) { double Scale = ScalingFactors[Cmd-IDM_SCALE_BASE]; if (Scale > 0.001) { LSurface *pDC = GetDC(); if (pDC) { DetachTool(); LAutoPtr pNew(new ImgMemDC); if (pDC && pNew && pNew->Create((int) ((double)pDC->X() * Scale), (int) ((double)pDC->Y() * Scale), pDC->GetColourSpace()) && ResampleDC(pNew, pDC, 0, Doc.GetProgress())) { SetDC(pNew); } Update(); AttachTool(); Doc.GetProgress()->Value(0); } } } else if (Cmd >= IDM_SCRIPTS_BASE && Cmd < IDM_SCRIPTS_BASE + (int)d->ToolScripts.Length()) { char *Script = d->ToolScripts[Cmd - IDM_SCRIPTS_BASE]; if (Script) CreateScriptWindow(this, Script); } break; } } return LDocApp::OnCommand(Cmd, Event, Handle); } int ImgWnd::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case TOOL_PALETTE: { LSurface *pDC = GetDC(); if (Palette && pDC) { LPalette *Dest = pDC->Palette(); LPalette *Src = Palette->GetPalette(); if (Dest && Src) { // Save snapshot of image BeforeAction(); // Set document to new palette Dest->Set(Src); Update(); // Create undo information AfterAction(); } } break; } case TOOL_BRUSH: { int Id = Ctrl->GetId(); if (Tools[Id]) Tools[Id]->OnProperties(n.Type); if (n.IsMouseEvent() && n.GetMouseEvent().Right()) - OperatorDlg Dlg(this); + { + auto Dlg = new OperatorDlg(this); + Dlg->DoModal(NULL); + } break; } case TOOL_TEXT: case TOOL_SELECT_BRUSH: case TOOL_EYE_DROPPER: case TOOL_ZOOM: // case TOOL_AIRBRUSH: case TOOL_FLOOD: case TOOL_FREEHAND: case TOOL_LINE: case TOOL_ELLIPSE: case TOOL_RECTANGLE: case TOOL_POLYGON: case TOOL_ERASE: { int Id = Ctrl->GetId(); if (Tools[Id]) Tools[Id]->OnProperties(n.Type); break; } case TOOL_GRID: { if (n.IsMouseEvent()) { auto m = n.GetMouseEvent(); if (m.Left()) { DisplayGrid(OptShowGrid.Value()); } else if (m.Right()) { - GridDlg Dlg(this); + auto Dlg = new GridDlg(this); + Dlg->DoModal(NULL); } } break; } case TOOL_GRIDINC: { bool Right = false; if (n.IsMouseEvent()) { auto m = n.GetMouseEvent(); Right = m.Right(); if (!m.Down()) break; } int Inc = Right ? -1 : 1; auto Cur = Doc.GetZoom(); auto New = Cur + Inc; Doc.SetZoom(New); GridSize(New); break; } case IDC_TOOLS: { if (n.Type == LNotifyValueChanged) { } break; } } return 0; } void ImgWnd::OnSettingChange() { Doc.Update(); OptShowGrid.Value(DisplayGrid() != 0); } ImgMemDC *ImgWnd::GetDC() { return Doc.GetDC(); } LAutoPtr ImgWnd::ReleaseDC() { DetachTool(); LAutoPtr dc(Doc.ReleaseDC()); return dc; } bool ImgWnd::SetDC(LAutoPtr pDC) { DetachTool(); if (Palette) { LPalette *Pal = Palette->GetPalette(); if (Pal) { // we might lose our palette here... go true colour ImgCol c; Palette->GetFore(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; Palette->SetFore(c); } Palette->GetBack(&c); if (c.Bits <= 8) { c.c = CBit(24, c.c, c.Bits, Pal); c.Bits = 24; Palette->SetBack(c); } } } if (Doc.GetDC() != pDC) { if (!SetDirty(pDC != NULL)) return false; Doc.SetDC(pDC); } Update(0, true); OnDcChange(); AttachTool(); return true; } void ImgWnd::EmptyUndoQueue() { for (auto e: UndoQueue) { UndoQueue.Delete(e); DeleteObj(e); } OnUndoQueueChange(); } void ImgWnd::PushUndoEvent(ImgEvent *u) { if (u) { // delete any undo objects ahead of us for (int i=UndoPos + 1; i < UndoQueue.Length(); i++) { ImgEvent *e = UndoQueue.ItemAt(i); if (e) { UndoQueue.Delete(e); DeleteObj(e); } } // Insert us on the end UndoQueue.Insert(u); UndoPos = UndoQueue.IndexOf(u); OnUndoQueueChange(); } } void ImgWnd::OnUndoQueueChange() { int Items = UndoQueue.Length(); Undo.Enabled(Items > 0 && UndoPos >= 0); Redo.Enabled(Items > 0 && UndoPos < Items - 1); uint Size = 0; for (auto e: UndoQueue) { Size += e->Sizeof(); } char Str[256]; int ch = sprintf_s(Str, sizeof(Str), "%s", LLoadString(IDS_UNDOQUEUE)); LFormatSize(Str+ch, sizeof(Str)-ch, Size); SetStatusText(Str); } void ImgWnd::OnDcChange() { LString Str; if (Doc.GetDC()) Str.Printf(LLoadString(IDS_IMAGE_FORMAT), Doc.GetDC()->X(), Doc.GetDC()->Y(), Doc.GetDC()->GetBits()); if (Palette) Palette->SetPalette((Doc.GetDC())?Doc.GetDC()->Palette():0); SetStatusText(Str, STATUS_DOCINFO); } void ImgWnd::OnReceiveFiles(LArray &Files) { bool Exit = false; auto Name = Files.Length() ? Files[0] : NULL; const char *Ext; if (Files.Length() == 2) { ImageCompare(this, Files[0], Files[1]); return; } else if (!Name) { return; } else if ((Ext = LGetExtension(Name)) && !_stricmp(Ext, "script")) { // Image script being launched with the command line... CreateScriptWindow(this, Name); } else { // Normal file drop LDocApp::OnReceiveFiles(Files); if (Doc.GetDC() && !d->CmdLineDone) { d->CmdLineDone = true; char s[512]; if (LAppInst->GetOption("resize", s)) { int x = 0; int y = 0; bool Resize = true; LToken t(s, ","); for (unsigned i=0; iX() * atoi(Val) / 100; } else { x = atoi(Val); } } } else if (_stricmp(Var, "y") == 0) { if (Val) { if (strchr(Val, '%')) { y = Doc.GetDC()->Y() * atoi(Val) / 100; } else { y = atoi(Val); } } } else if (_stricmp(Var, "crop") == 0) { Resize = false; } else if (_stricmp(Var, "resize") == 0) { Resize = true; } } if (!x) { if (y) { double Ratio = (double) y / Doc.GetDC()->Y(); x = (int) (Ratio * Doc.GetDC()->X()); } } else if (!y) { if (x) { double Ratio = (double) x / Doc.GetDC()->X(); y = (int) (Ratio * Doc.GetDC()->Y()); } } if (x && y) { LAutoPtr pNew (new ImgMemDC); if (pNew && pNew->Create(x, y, Doc.GetDC()->GetColourSpace()) && ResampleDC(pNew, Doc.GetDC(), 0)) { SetDC(pNew); } else { LgiTrace("%s:%i - 'resize' cmd line failed.\n", __FILE__, __LINE__); } } else { LgiTrace("%s:%i - You must specify at least one size to use the 'resize' cmd line.\n", __FILE__, __LINE__); } } if (LAppInst->GetOption("reduce", s)) { // LReduceOptions } if (LAppInst->GetOption("write", s)) { // Write the output file SaveFile(s); } if (LAppInst->GetOption("exit")) { Exit = true; } } } for (unsigned i=1; i 0); // endop called before startop OpLevel--; if (OpLevel && QuitWhenDone) { LCloseApp(); } } LSurface *ImgWnd::GetDoc() { return Doc.GetDC(); } void ImgWnd::SetDoc(LSurface *s) { LAutoPtr p(new ImgMemDC(s)); SetDC(p); } LSurface *ImgWnd::Brush() { return BrushMap; } void ImgWnd::Brush(LAutoPtr b) { BrushMap = b; } void ImgWnd::SetStatusText(const char *Text, int Pane) { if (Pane >= 0 && Pane < STATUS_MAX && StatusInfo[Pane]) { StatusInfo[Pane]->Name(Text); } } COLOUR ImgWnd::GetColour32(bool Fore) { ImgCol c; if (GetColour(c, Fore)) { COLOUR c24 = CBit(24, c.c, c.Bits, Palette->GetPalette()); return Rgba32(R24(c24), G24(c24), B24(c24), c.a); } return 0; } bool ImgWnd::GetColour(ImgCol &c, bool Fore) { if (!Palette) return false; if (Fore) Palette->GetFore(&c); else Palette->GetBack(&c); c.a = Op() == GDC_ALPHA ? Alpha() : 255; return true; } ImgCol ImgWnd::Fore() { ImgCol c; if (Palette) { Palette->GetFore(&c); } return c; } void ImgWnd::Fore(ImgCol c) { if (Palette) { Palette->SetFore(c); } } ImgCol ImgWnd::Back() { ImgCol c; // int DcBits = (Doc.GetDC()) ? Doc.GetDC()->GetBits() : 24; // return CBit(DcBits, c.c, c.Bits, Palette->GetPalette()); if (Palette) { Palette->GetBack(&c); } return c; } void ImgWnd::Back(ImgCol c) { if (Palette) { Palette->SetBack(c); } } bool ImgWnd::TransparentPaste() { return OptTransparentPaste.Value(); } void ImgWnd::TransparentPaste(bool b) { OptTransparentPaste.Value(b); } bool ImgWnd::UseForeColour() { return UseFore; } void ImgWnd::UseForeColour(bool b) { UseFore = b; } int ImgWnd::Alpha() { return AlphaLevel; } void ImgWnd::Alpha(int i) { AlphaLevel = i; } bool ImgWnd::FillPrimitives() { return FillPrim = OptFillPrimitives.Value(); } void ImgWnd::FillPrimitives(bool b) { FillPrim = b; OptFillPrimitives.Value(b); } bool ImgWnd::CrossHairs() { return OptGuideLines.Value(); } void ImgWnd::CrossHairs(bool b) { OptGuideLines.Value(b); } void ImgWnd::BeforeAction() { if (CaptureUndoLevel != 0) return; // save document state to create undo information with if (!LastFrame) { LastFrame = new ImgMemDC; } if (LastFrame && Doc.GetDC()) { // create the frame buffer if not the right size if (LastFrame->X() != Doc.GetDC()->X() || LastFrame->Y() != Doc.GetDC()->Y() || LastFrame->GetBits() != Doc.GetDC()->GetBits()) { LastFrame->Create(Doc.GetDC()->X(), Doc.GetDC()->Y(), Doc.GetDC()->GetColourSpace()); LastFrame->HasAlpha(Doc.GetDC()->AlphaDC() != 0); } // save the current info // LSurface *In = Doc.GetDC(); LPalette *p = Doc.GetDC()->Palette(); if (p) { LastFrame->Palette(new LPalette(p)); } LastUpdate.ZOff(-1, -1); } } void ImgWnd::BeforeUpdate(LRect *a, bool PaletteChange) { auto pDC = Doc.GetDC(); if (!pDC) return; // save undo information if (LastFrame) { if (a) { if (LastUpdate.Valid()) { // adding to the updated region... LRect u(LastUpdate); u.Union(a); LRegion c(u); c.Subtract(&LastUpdate); for (LRect *r=c.First(); r; r=c.Next()) { LastFrame->Blt(r->x1, r->y1, pDC, r); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(r->x1, r->y1, pDC->AlphaDC(), r); } } LastUpdate = u; } else { // setting the update region... LastUpdate = *a; LastFrame->Blt(a->x1, a->y1, pDC, a); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(a->x1, a->y1, pDC->AlphaDC(), a); } } } else { // don't know where to update so grab everything... LastUpdate.ZOff(pDC->X()-1, pDC->Y()-1); LastFrame->Blt(0, 0, pDC); if (LastFrame->AlphaDC() && pDC->AlphaDC()) { LastFrame->AlphaDC()->Blt(0, 0, pDC->AlphaDC()); } } } // setup for drawing... ImgCol Cur = (UseFore) ? Fore() : Back(); pDC->Colour(CBit(pDC->GetBits(), Cur.c, Cur.Bits, Palette->GetPalette())); pDC->Op(Operator); if (pDC->AlphaDC()) { pDC->AlphaDC()->Colour(Cur.a); } LApplicator *pApp = pDC->Applicator(); if (pApp) { if (Operator == GDC_ALPHA) { pApp->SetVar(GAPP_ALPHA_A, AlphaLevel); pApp->SetVar(GAPP_ALPHA_PAL, (NativeInt)pDC->Palette()); } LVariant Angle; if (GetOptions()->GetValue(OPT_LinearAngle, Angle)) { pApp->SetVar(GAPP_ANGLE, Angle.CastInt32()); } pApp->SetVar(GAPP_BOUNDS, (NativeInt)a); pApp->SetVar(GAPP_BACKGROUND, (UseFore) ? Back().c : Fore().c); } } void ImgWnd::AfterAction() { if (Doc.GetDC()) { // save change history into undo queue if (UndoEnabled && CaptureUndoLevel == 0 && LastFrame) { PushUndoEvent(new ImgEvent(LastFrame, Doc.GetDC(), LastUpdate)); } // set changed flag SetDirty(true); } } void ImgWnd::Update(LRect *Up, bool Now) { if (Zoom) { Zoom->Update(Up); #ifdef WIN32 if (Now) UpdateWindow(Zoom->Handle()); #endif } if (Normal) { Normal->Update(Up); #ifdef WIN32 if (Now) UpdateWindow(Normal->Handle()); #endif } } void ImgView::OnMouseMove(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseMove(im); } void ImgView::OnMouseClick(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; Capture(m.Down()); App->UserMouseClick(im); } void ImgView::OnMouseEnter(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseEnter(im); if (IsCapturing()) SetPulse(); } void ImgView::OnMouseExit(LMouse &m) { ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; App->UserMouseExit(im); if (IsCapturing()) SetPulse(150); } bool ImgView::OnKey(LKey &k) { ImgKey ik(k, this); App->UserKey(ik); return false; } void ImgView::OnPulse() { LMouse m; if (!GetMouse(m)) return; ImgMouse im(m, this); Convert(im.pt, m.x, m.y); im.x = (int)im.pt.x; im.y = (int)im.pt.y; ScrollToPoint(LPoint(im.x, im.y)); App->UserMouseMove(im); } void ImgWnd::UserKey(ImgKey &k) { if (Current) Current->Key(k); } void ImgWnd::UserMouseClick(ImgMouse &m) { char Str[64]; ClickedView = m.view; if (m.Down()) { d->LastMousePos = m; MouseStart = m; sprintf_s(Str, sizeof(Str), LLoadString(IDS_MOUSEMOVE), m.x, m.y, abs(m.x-MouseStart.x)+1, abs(m.y-MouseStart.y)+1); } else { sprintf_s(Str, sizeof(Str), LLoadString(IDS_MOUSEPOS), m.x, m.y); SetPulse(); } SetStatusText(Str, STATUS_POSINFO); if (Current) Current->MouseClick(m); } void ImgWnd::UserMouseEnter(ImgMouse &m) { if (Current) Current->MouseEnter(m); } void ImgWnd::UserMouseExit(ImgMouse &m) { if (Current) Current->MouseExit(m); } void ImgWnd::UserMouseMove(ImgMouse &m) { if (d->LastMousePos == m) return; char Str[256] = ""; d->LastMousePos = m; if (Current) Current->MouseMove(m); bool Tile = DisplayTile() != false; int Tx = TileX(); int Ty = TileY(); int TileMx = m.x / Tx; int TileMy = m.y / Ty; int TileWidth = GetDoc() ? (GetDoc()->X() + (Tx - 1)) / Tx : 1; int TilePos = (TileWidth * TileMy) + TileMx; if (m.Down()) { const char *Fmt = LLoadString(Tile ? IDS_MOUSEMOVE_GUIDE : IDS_MOUSEMOVE); if (Fmt) sprintf_s(Str, sizeof(Str), Fmt, m.x, m.y, abs(m.x-MouseStart.x)+1, abs(m.y-MouseStart.y)+1, TileMx, TileMy, TilePos); } else { const char *Fmt = LLoadString(Tile ? IDS_MOUSEPOS_GUIDE : IDS_MOUSEPOS); if (Fmt) sprintf_s(Str, sizeof(Str), Fmt, m.x, m.y, TileMx, TileMy, TilePos); } SetStatusText(Str, STATUS_POSINFO); auto pDC = Doc.GetDC(); if (!pDC) return; auto c = pDC->Get(m.x, m.y); switch (pDC->GetBits()) { case 8: { auto Pal = pDC->Palette(); auto p = Pal ? (*Pal)[c] : NULL; if (p) { sprintf_s(Str, sizeof(Str), LLoadString(IDS_I8_FORMAT), c, p->r, p->g, p->b, p->r, p->g, p->b); } else { sprintf_s(Str, sizeof(Str), "%i", c); } break; } default: { #define toDbl(i) ((double)(i)/255.0) LColour col(c, pDC->GetBits()); sprintf_s(Str, sizeof(Str), "int:%i,%i,%i,%i hex:%2.2x,%2.2x,%2.2x,%2.2x dbl:%.3f,%.3f,%.3f,%.3f", col.r(), col.g(), col.b(), col.a(), col.r(), col.g(), col.b(), col.a(), toDbl(col.r()), toDbl(col.g()), toDbl(col.b()), toDbl(col.a())); break; } } SetStatusText(Str, STATUS_COLOURINFO); } void ImgWnd::ZoomCenter(int x, int y) { if (!Zoom) return; LRect Pos = Zoom->GetPos(); x = MAX(x - (Pos.X() / GridSize() / 2), 0); y = MAX(y - (Pos.Y() / GridSize() / 2), 0); Zoom->SetScrollPos(x, y); Zoom->Update(); #ifdef WIN32 UpdateWindow(Zoom->Handle()); #endif } void ImgWnd::ZoomTo(LRect r) { LSurface *pDC = Doc.GetDC(); if (!Normal || !pDC) return; LZoomView::ViewportInfo vpi; double Sx = (double) r.X() / pDC->X(); double Sy = (double) r.Y() / pDC->Y(); double S = MAX(Sx, Sy); if (S < 1.0) { double F = 1.0 / S; F = ceil(F); vpi.Zoom = (int)F - 1; vpi.Sx = r.x1; vpi.Sy = r.y1; Normal->SetViewport(vpi); } } bool ImgWnd::Empty() { if (GetDC()) { LAutoPtr Ptr; return SetDC(Ptr); } else { LCloseApp(); } return true; } void ImgWnd::GetFileTypes(LFileSelect *Dlg, bool Write) { EnumAvailableFormats(Dlg, Write ? FILTER_CAP_WRITE : FILTER_CAP_READ); } bool ImgWnd::OpenFile(const char *FileName, bool Ro) { bool Status = false; if (FileName) { DetachTool(); StartOp(); if (!Doc.Load(this, FileName)) { char *ErrorMsg = Doc.Props.GetAttr(LGI_FILTER_ERROR); LgiMsg( this, LLoadString(IDS_ERROR_LOADING_IMAGE), AppName, MB_OK, FileName, ErrorMsg ? ErrorMsg : (char*)""); } else { Status = true; EmptyUndoQueue(); char *Info = Doc.Props.GetAttr(LGI_FILTER_INFO); if (Info && StatusInfo[0]) { StatusInfo[0]->Name(Info); } } EndOp(); bool Undo = UndoEnabled; UndoEnabled = false; OnDcChange(); Update(); AttachTool(); UndoEnabled = Undo; } return Status; } LDom *ImgWnd::GetQuiet() { return d->Quiet; } void ImgWnd::SetQuiet(LDom *b) { d->Quiet = b; } bool ImgWnd::SaveFile(const char *FileName) { bool Status = false; if (FileName) { DetachTool(); StartOp(); if (!Doc.Save(this, FileName, 0, d->Quiet)) { int Cancel = Doc.Props.GetAsInt(OPT_Cancel); if (Cancel < 1) { char *ErrorMsg = Doc.Props.GetAttr(OPT_ErrorMsg); LgiMsg( this, LLoadString(IDS_ERROR_SAVING_IMAGE), AppName, MB_OK, ErrorMsg?ErrorMsg:(char*)""); } } else { Status = true; } EndOp(); OnDcChange(); Update(); AttachTool(); } return Status; } bool ImgWnd::SerializeFile(LAutoPtr &pDC, const char *FileName, bool Write) { bool Status = false; auto f = LFilterFactory::New(FileName, Write ? FILTER_CAP_WRITE : FILTER_CAP_READ, 0); if (f) { if (!Write) { pDC.Reset(new ImgMemDC); } if (pDC) { LHashDom Hd; LVariant v; f->Props = &Hd; f->SetProgress(Meter); f->Props->SetValue("Parent", v = (void*)this); f->Props->SetValue("Fore", v = (int)Fore().c); f->Props->SetValue("Back", v = (int)Back().c); LFile File; if (File.Open(FileName, (Write) ? O_WRITE : O_READ)) { if ((Write) ? f->WriteImage(&File, pDC) : f->ReadImage(pDC, &File)) { Status = true; } else { pDC.Reset(); } } } } return Status; } void ImgWnd::OffsetImage(int x, int y) { LSurface *pDC = GetDC(); if (!pDC) return; DetachTool(); LMemDC *pTemp = new ImgMemDC; if (pTemp && pTemp->Create(pDC->X(), pDC->Y(), pDC->GetColourSpace())) { int Sx = ((x > 0) ? -1 : 1) * pDC->X(); int Sy = ((y > 0) ? -1 : 1) * pDC->Y(); pTemp->Blt(0, 0, pDC); pDC->Blt(x, y, pTemp); pDC->Blt(x+Sx, y, pTemp); pDC->Blt(x, y+Sy, pTemp); pDC->Blt(x+Sx, y+Sy, pTemp); DeleteObj(pTemp); LastOffset.x = x; LastOffset.y = y; Update(); } AttachTool(); } void ImgWnd::DrawBackground(LZoomView *View, LSurface *pDC, LPoint Offset, LRect *Rc) { if (!pDC) return; LRect All = pDC->Bounds(); if (!Rc) Rc = &All; switch (d->Background) { case BgBlack: { pDC->Colour(L_BLACK); pDC->Rectangle(Rc); break; } default: case BgGrey: { LSystemColour c[2] = { L_MED, L_HIGH }; LRect RcOff = *Rc; RcOff.Offset(Offset.x, Offset.y); for (int y = RcOff.y1 >> GREY_SQUARE_SHIFT; y <= RcOff.y2 >> GREY_SQUARE_SHIFT; y++) { for (int x = RcOff.x1 >> GREY_SQUARE_SHIFT; x <= RcOff.x2 >> GREY_SQUARE_SHIFT; x++) { auto col = c[(x&1)^(y&1)]; pDC->Colour(col); int dx = (x << GREY_SQUARE_SHIFT) - Offset.x; int dy = (y << GREY_SQUARE_SHIFT) - Offset.y; LRect r; r.ZOff((1 << GREY_SQUARE_SHIFT) - 1, (1 << GREY_SQUARE_SHIFT) - 1); r.Offset(dx, dy); pDC->Rectangle(&r); } } break; } case BgWhite: { pDC->Colour(L_WHITE); pDC->Rectangle(Rc); break; } } } void ImgWnd::DrawForeground(LZoomView *View, LSurface *Dst, LPoint Offset, LRect *Src) { if (Current) Current->DrawForeground(View, Dst, Offset, Src); } bool ImgWnd::GetVariant(const char *Name, LVariant &Value, const char *Array) { if (!Name) return false; if (_stricmp(Name, "doc") == 0) { Value = Doc.GetDC(); return true; } return false; } bool ImgWnd::SetVariant(const char *Name, LVariant &Value, const char *Array) { return false; } void ImgWnd::RunScript(LViewI *Parent, char *Script, const char *FileName, bool Debug) { if (!Script) return; LAutoPtr Code(new LCompiledCode); LVariant v; Code->Set("App", v = (LDom*)this); Code->Set("Parent", v = Parent->GetGView()); if (Debug) { LCompiler c; if (c.Compile(Code, &d->SysContext, &d->Context, FileName, Script, NULL)) { LVirtualMachine *Vm = new LVirtualMachine(d); if (Vm) { LVmDebugger *Dbg = Vm->OpenDebugger(Code); if (Dbg) { Dbg->OwnCompiledCode(Code); LVmDebuggerWnd *DbgWnd = dynamic_cast(Dbg); Vm->Execute(Dbg->GetCode(), 0, DbgWnd ? DbgWnd->GetLog() : NULL, false, NULL); } } } } else { LStringPipe Console; LScriptEngine Engine(Parent, &d->Context, d); Engine.SetConsole(&Console); Engine.GetConsole()->Print("Compiling script...\n"); if (Engine.Compile(Code, &d->Context, Script, FileName)) { Engine.GetConsole()->Print("Starting script...\n"); Engine.Run(Code); Engine.GetConsole()->Print("Script ended.\n"); } Parent->PostEvent(M_OUTPUT, (LMessage::Param)Console.NewStr()); Update(); } DeleteArray(Script); } ////////////////////////////////////////////////////////////////////////////// ImgMemDC::~ImgMemDC() { } bool ImgMemDC::GetVariant(const char *Name, LVariant &Value, const char *Array) { if (!Name) return false; if (_stricmp(Name, "x") == 0) { Value = X(); return true; } else if (_stricmp(Name, "y") == 0) { Value = Y(); return true; } else if (_stricmp(Name, "bits") == 0) { Value = GetBits(); return true; } return false; } bool ImgMemDC::SetVariant(const char *Name, LVariant &Value, const char *Array) { return false; } ////////////////////////////////////////////////////////////////////////////// ImgDocument::ImgDocument(ImgMemDC *new_dc) { pDC.Reset(new_dc); Dirty = false; Meter = 0; ZoomViewIdx = 0; } ImgDocument::~ImgDocument() { } const char *ImgDocument::Name() { return LBase::Name(); } bool ImgDocument::Name(const char *p) { return LBase::Name(p); } void ImgDocument::VerifyExtension(char *FileName, char *Type) { if (FileName && Type) { char *Dot; char Ext[16]; ZeroObj(Ext); // Extract extension char *s = strchr(Type, '.'); if (s) { s++; char *e = strchr(s, ';'); if (e) strncpy_s(Ext, sizeof(Ext), s, e-s); else strcpy_s(Ext, sizeof(Ext), s); // find last '.' before a DIR_CHAR for (Dot = FileName+strlen(FileName); Dot>FileName && *Dot != '.' && *Dot != DIR_CHAR; Dot--); #ifdef WIN32 _strlwr_s(Ext, sizeof(Ext)); #else strlwr(Ext); #endif if (*Dot == '.') { if (strlen(Dot+1) > 0) { // has an extension // leave it alone } else { strcat(FileName, Ext); } } else { size_t Ch = strlen(FileName); sprintf_s(FileName+Ch, 256-Ch, ".%s", Ext); } } } } int ImgDocument::GetZoom() { return Views.IdxCheck(ZoomViewIdx) ? Views[ZoomViewIdx]->GetZoom() : 0; } void ImgDocument::SetZoom(int z) { if (Views.IdxCheck(ZoomViewIdx)) Views[ZoomViewIdx]->SetZoom(z); } void ImgDocument::Update(LRect *Where) { for (int i=0; iUpdate(Where); } } void ImgDocument::AddView(ImgView *v) { if (!Views.HasItem(v)) { v->SetSurface(pDC, false); Views.Add(v); } } void ImgDocument::RemoveView(ImgView *v) { if (Views.HasItem(v)) { v->SetSurface(NULL, false); Views.Delete(v); } } void ImgDocument::SetDC(LAutoPtr pdc) { if (pDC) { for (int i=0; iSetSurface(NULL, false); } pDC = pdc; if (pDC) { for (int i=0; iSetSurface(pDC, false); } } bool ImgDocument::Save(LView *Parent, const char *FileName, char *Type, LDom *Quiet) { LFilter::IoStatus Status = LFilter::IoError; if (FileName && pDC) { char File[256]; strcpy_s(File, sizeof(File), FileName); auto F = LFilterFactory::New(Type?Type:File, FILTER_CAP_WRITE, 0); VerifyExtension(File, Type); if (F) { LHashDom Hd; LVariant v; F->Props = Quiet ? Quiet : &Hd; F->Props->SetValue(LGI_FILTER_PARENT_WND, v = Parent); if (F->GetFormat() == LFilter::FmtJpeg) { if (!Quiet || !Quiet->GetValue(LGI_FILTER_QUALITY, v)) { GdcJpeg *j = dynamic_cast(F.Get()); if (j) { - LJpegOptions Dlg(j, pDC); - if (Dlg.DoModal() == IDOK) + auto Dlg = new LJpegOptions(j, pDC); + Dlg->DoModal([this, Dlg, F](auto dlg, auto code) { - F->Props->SetValue(LGI_FILTER_QUALITY, v = Dlg.Quality); - F->Props->SetValue(LGI_FILTER_SUBSAMPLE, v = Dlg.SubSample); - F->Props->SetValue(LGI_FILTER_DPI_X, v = Dlg.Dpi.x); - F->Props->SetValue(LGI_FILTER_DPI_Y, v = Dlg.Dpi.y); - } + if (code == IDOK) + { + LVariant v; + F->Props->SetValue(LGI_FILTER_QUALITY, v = Dlg->Quality); + F->Props->SetValue(LGI_FILTER_SUBSAMPLE, v = Dlg->SubSample); + F->Props->SetValue(LGI_FILTER_DPI_X, v = Dlg->Dpi.x); + F->Props->SetValue(LGI_FILTER_DPI_Y, v = Dlg->Dpi.y); + } + delete dlg; + }); } else LAssert(0); } } LFile Out; if (Out.Open(File, O_WRITE)) { F->SetProgress(Meter); Out.SetSize(0); LVariant v; if (!F->Props->GetValue(LGI_FILTER_FOREGROUND, v)) F->Props->SetValue(LGI_FILTER_FOREGROUND, v = (int)MainWnd->Fore().c); if (!F->Props->GetValue(LGI_FILTER_BACKGROUND, v)) F->Props->SetValue(LGI_FILTER_BACKGROUND, v = (int)MainWnd->Back().c); char *Ext = LGetExtension(FileName); if (Ext && !_stricmp(Ext, "gif")) { // Prep background gif channel LSurface *pA = pDC->AlphaDC(); if (pA) { ImgCol b = MainWnd->Back(); // Find a suitable background index int BackIdx = b.c; if (b.Bits != 8 || b.a != 0xff) { bool Used[256]; ZeroObj(Used); for (int y=0; yY(); y++) { uchar *p = (*pDC)[y]; uchar *pe = p + pDC->X(); uchar *a = (*pA)[y]; while (p < pe) { if (*a) Used[*p] = true; p++; a++; } } for (int i=0; i<256; i++) { if (!Used[i]) { BackIdx = i; break; } } } // Pass the background index to the filter F->Props->SetValue(LGI_FILTER_BACKGROUND, v = BackIdx); // Set all the transparent pixels to the background index pDC->Colour(BackIdx); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { if (!pA->Get(x, y)) pDC->Set(x, y); } } } } LSurface *ColourConverted = 0; if (ColourProfile) { LStringPipe p; if (ColourProfile->Save(&p)) { int Len = (int)p.GetSize(); char *Data = new char[Len]; if (Data) { p.Read(Data, Len); v.SetBinary(Len, Data); F->Props->SetValue(LGI_FILTER_COLOUR_PROF, v); DeleteArray(Data); // Create a colour converted copy of the image for saving // The in memory representation is always in sRGB because // thats what we can display. ColourConverted = new ImgMemDC(pDC); if (ColourConverted) { LIccProfile sRGB; if (sRGB.CreateNamed("sRGB")) { ColourProfile->Convert(ColourConverted, pDC, &sRGB); } } } } } Status = F->WriteImage(&Out, ColourConverted ? ColourConverted : pDC); DeleteObj(ColourConverted); if (Status == LFilter::IoSuccess) { Dirty = false; } else { LVariant v; F->Props->GetValue(OPT_Cancel, v); if (v.CastInt32()) { Props.SetAttr(OPT_Cancel, 1); } else { if (F->Props->GetValue(OPT_ErrorMsg, v)) { Props.SetAttr(OPT_ErrorMsg, v.Str()); } } } if (Meter) Meter->Value(0); Name(File); } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_OPEN_WRITING)); } } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_NOFILTER)); } } return Status == LFilter::IoSuccess; } bool ImgDocument::Load(LView *Parent, const char *FileName, char *Type) { LFilter::IoStatus Status = LFilter::IoUnsupportedFormat; auto F = LFilterFactory::New(FileName, FILTER_CAP_READ, 0); if (F) { ColourProfile.Reset(); LFile In; if (In.Open(FileName, O_READ)) { LVariant v; LHashDom Hd; F->Props = &Hd; F->SetProgress(Meter); F->Props->SetValue(LGI_FILTER_PARENT_WND, v = static_cast(MainWnd)); if (!F->Props->GetValue(LGI_FILTER_FOREGROUND, v)) F->Props->SetValue(LGI_FILTER_FOREGROUND, v = (int)MainWnd->Fore().c); if (!F->Props->GetValue(LGI_FILTER_BACKGROUND, v)) F->Props->SetValue(LGI_FILTER_BACKGROUND, v = (int)MainWnd->Back().c); LAutoPtr MemDC(new ImgMemDC); if (MemDC) { Status = F->ReadImage(MemDC, &In); if (Status == LFilter::IoSuccess) { Dirty = false; // Set the palette if (MemDC->GetBits() <= 8 && !MemDC->Palette()) { LPalette *Pal = new LPalette; if (Pal) { int Colours = 1 << MemDC->GetBits(); Pal->SetSize(Colours); for (int i=0; ir = Rgb->g = Rgb->b = i * 255 / (Colours - 1); } } MemDC->Palette(Pal, true); } } // Setup the colour profile. LVariant v; if (F->Props->GetValue(LGI_FILTER_COLOUR_PROF, v)) { ColourProfile.Reset(new LIccProfile); if (ColourProfile) { LMemStream p(v.Value.Binary.Data, v.Value.Binary.Length); if (ColourProfile->Open(&p)) { // Convert into sRGB for editing / display LIccProfile sRGB; if (sRGB.CreateNamed("sRGB")) { sRGB.Convert(MemDC, MemDC, ColourProfile); } } } } if (F->Props->GetValue(LGI_FILTER_INFO, v)) { Props.SetAttr(LGI_FILTER_INFO, v.Str()); } SetDC(MemDC); } else { LVariant v; if (F->Props->GetValue(LGI_FILTER_ERROR, v)) { Props.SetAttr(LGI_FILTER_ERROR, v.Str()); } LAutoPtr Empty; SetDC(Empty); } } if (Meter) Meter->Value(0); Name(FileName); } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_OPEN_READING)); } } else { Props.SetAttr(OPT_ErrorMsg, LLoadString(IDS_ERROR_NOFILTER)); } return Status == LFilter::IoSuccess; } //////////////////////////////////////////////////////////////////////////////// typedef int EventInt; ImgEvent::ImgEvent(LSurface *From, LSurface *To, LRect &Bounds) { Length = 0; Palette = 0; if (From && To && To->GetBits() == From->GetBits()) { // Check for differences in the palette LPalette *FromPal = From->Palette(); LPalette *ToPal = To->Palette(); if (FromPal && ToPal) { bool Different = false; for (int i=0; iGetSize(); i++) { GdcRGB *f = (*FromPal)[i]; GdcRGB *t = (*ToPal)[i]; if (f && t) { if (f->r != t->r || f->g != t->g || f->b != t->b) { Different = true; break; } } } if (FromPal->GetSize() != ToPal->GetSize() || Different) { // Save the old palette Palette = new LPalette(FromPal); } } // Check for differences in the bits LMemQueue Pipe; Bounds.x2 = MIN(Bounds.x2, From->X()-1); Bounds.x2 = MIN(Bounds.x2, To->X()-1); Bounds.y2 = MIN(Bounds.y2, From->Y()-1); Bounds.y2 = MIN(Bounds.y2, To->Y()-1); int Bpp = From->GetBits() >> 3; EventInt n; // step through image(s) for (int y=0; yY(); y++) { if (y>=Bounds.y1 && y<=Bounds.y2) { for (int x=Bounds.x1; x<=Bounds.x2;) { // search for start of dissimilar pixels for (; From->Get(x, y)==To->Get(x, y) && x<=Bounds.x2; x++) ; if (x<=Bounds.x2) { int Start = MAX(x, 0); for (; From->Get(x, y)!=To->Get(x, y) && x<=Bounds.x2; x++); int Length = (x - Start) * Bpp; // push block onto pipe if (Length > 0) { // Position/Length n = Start; Pipe.Write((uchar*)&n, sizeof(n)); n = Length; Pipe.Write((uchar*)&n, sizeof(n)); // Pixels uchar *Pixels = (*From)[y]; if (Pixels) { Pipe.Write(Pixels + (Start * Bpp), Length); } } } } } n = -1; // EOL Pipe.Write((uchar*)&n, sizeof(n)); } n = -2; // EOF Pipe.Write((uchar*)&n, sizeof(n)); // save out result Length = (int)Pipe.GetSize(); if (Data.Reset(new uchar[Length])) { Pipe.Read(Data, Length); } } } ImgEvent::~ImgEvent() { DeleteObj(Palette); } bool ImgEvent::Apply(LSurface *pDC) { if (pDC) { // Palette LPalette *Pal = pDC->Palette(); if (Palette && Pal) { LPalette *Buffer = new LPalette(Pal); if (Buffer) { Pal->Set(Palette); Palette->Set(Buffer); DeleteObj(Buffer); } } // Image diff if (Data) { int y = 0; int Bpp = pDC->GetBits() >> 3; EventInt *d = (EventInt*) Data.Get(); LAutoPtr Buffer(new uchar[pDC->X() * Bpp]); if (Buffer) { while (y < pDC->Y()) { if (*d >= 0) { EventInt x = *d++; EventInt Length = *d++; uchar *Pixels = (*pDC)[y]; if (Pixels) { Pixels += (x * Bpp); memcpy(Buffer, d, Length); memcpy(d, Pixels, Length); memcpy(Pixels, Buffer, Length); } d = (EventInt*) (((char*)d) + Length); } else if (*d == -1) { // EOL y++; d++; } else { // EOF break; } } } } } return false; } uint ImgEvent::Sizeof() { uint Size = (Data) ? Length : 0; if (Palette) { Size += sizeof(*Palette) + (sizeof(GdcRGB) * Palette->GetSize()); } return Size; } ////////////////////////////////////////////////////////////////////// ImgTools::ImgTools(ImgWnd *app, int x, int y) : LPanel("Tools", x, false) { App = app; Alignment(GV_EDGE_LEFT); SetClosedSize(y); } void ImgTools::OnCreate() { LPanel::OnCreate(); } void ImgTools::OnPaint(LSurface *pDC) { LPanel::OnPaint(pDC); } ///////////////////////////////////////////////////////////////////////// int LgiMain(OsAppArguments &AppArgs) { #ifdef _DEBUG const char *Build = "Debug"; #else const char *Build = "Release"; #endif printf("i.Mage v%s %s\n", IMAGE_VER, Build); LApp a(AppArgs, "i.Mage"); if (a.IsOk()) { printf("Screen: %ix%i@%i\n", GdcD->X(), GdcD->Y(), GdcD->GetBits()); if ((a.AppWnd = new ImgWnd)) a.Run(); } return 0; } diff --git a/src/ImageProperties.cpp b/src/ImageProperties.cpp --- a/src/ImageProperties.cpp +++ b/src/ImageProperties.cpp @@ -1,258 +1,258 @@ #include "lgi/common/Lgi.h" #include "Image.h" #include "resdefs.h" #include "ImageProperties.h" #include "lgi/common/Palette.h" int AlignTypes[] = { IDC_LEFT_TOP, IDC_CENTER_TOP, IDC_RIGHT_TOP, IDC_LEFT_CENTER, IDC_CENTERED, IDC_RIGHT_CENTER, IDC_LEFT_BOTTOM, IDC_CENTER_BOTTOM, IDC_RIGHT_BOTTOM, 0 }; -ImageProperties::ImageProperties(ImgWnd *wnd) +ImagePropertiesDlg::ImagePropertiesDlg(ImgWnd *wnd) { Wnd = wnd; SetParent(Wnd); Colour = 0; AlignType = 0; Palette = 0; x = y = Bits = 0; ScaleOption = 0; MaintainAspectRatio = false; KeepData = false; NotifyOthers = true; RemapPalette = false; pDC = 0; if (LoadFromResource(IDD_PROPERTIES)) { int Bits = 24; MoveToCenter(); pDC = Wnd->GetDC(); if (pDC) { // Show current properties char Str[256]; sprintf_s(Str, sizeof(Str), "%i x %i, %i bits", pDC->X(), pDC->Y(), pDC->GetBits()); SetCtrlName(IDC_OLD, Str); OldX = pDC->X(); OldY = pDC->Y(); SetCtrlValue(IDC_BITS, IndexFromBits(Bits = pDC->GetBits())); } else { // Some sensible defaults SetCtrlName(IDC_OLD, "No document"); OldX = 640; OldY = 480; SetCtrlValue(IDC_BITS, IndexFromBits(Bits = GdcD->GetBits())); } SetCtrlValue(IDC_KEEP_DATA, pDC != 0); SetCtrlValue(IDC_X, OldX); SetCtrlValue(IDC_Y, OldY); SetCtrlValue(IDC_SCALE_X, 100); SetCtrlValue(IDC_SCALE_Y, 100); SetCtrlValue(IDC_ASPECT_RATIO, true); SetCtrlValue(IDC_RESAMPLE, 1); SetCtrlValue(IDC_LEFT_TOP, 1); EnableCtrls(GetCtrlValue(IDC_KEEP_DATA) != 0); LColourCtrl *Colour; if (GetViewById(IDC_BACKGROUND, Colour)) { Colour->SetBorder(true); if (Bits <= 8 && pDC) { Colour->Palette(pDC->Palette()); } else if (Bits == 32) { Colour->Colour(LColour()); } } } } -ImageProperties::~ImageProperties() +ImagePropertiesDlg::~ImagePropertiesDlg() { DeleteObj(Palette); } -void ImageProperties::EnableCtrls(bool i) +void ImagePropertiesDlg::EnableCtrls(bool i) { SetCtrlEnabled(IDC_RESAMPLE, i); SetCtrlEnabled(IDC_CROP, i); SetCtrlEnabled(IDC_RESMPL, i); } -int ImageProperties::BitsFromIndex(int i) +int ImagePropertiesDlg::BitsFromIndex(int i) { int n[] = {8, 16, 24, 32}; return (i>=0 && i<4) ? n[i]:24; } -int ImageProperties::IndexFromBits(int i) +int ImagePropertiesDlg::IndexFromBits(int i) { switch (i) { case 8: return 0; case 16: return 1; case 24: return 2; case 32: return 3; } return 2; } -int ImageProperties::OnNotify(LViewI *Ctrl, LNotification n) +int ImagePropertiesDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_KEEP_DATA: { EnableCtrls(GetCtrlValue(IDC_KEEP_DATA) != 0); break; } case IDC_SCALE_X: { if (NotifyOthers) { NotifyOthers = false; int64 s = GetCtrlValue(IDC_SCALE_X); int x = (int) (((double)s * OldX) / 100); SetCtrlValue(IDC_X, MAX(x, 1)); if (GetCtrlValue(IDC_ASPECT_RATIO)) { double Ratio = (double)OldY/OldX; int64 y = GetCtrlValue(IDC_Y); y = (int64) ((double)x * Ratio); SetCtrlValue(IDC_Y, y); SetCtrlValue(IDC_SCALE_Y, s); } NotifyOthers = true; } break; } case IDC_SCALE_Y: { if (NotifyOthers) { NotifyOthers = false; int64 s = GetCtrlValue(IDC_SCALE_Y); int y = (int) (((double)s * OldY) / 100); SetCtrlValue(IDC_Y, MAX(y, 1)); if (GetCtrlValue(IDC_ASPECT_RATIO)) { double Ratio = (double)OldY/OldX; int64 x = GetCtrlValue(IDC_X); x = (int) ((double)y / Ratio); SetCtrlValue(IDC_X, x); SetCtrlValue(IDC_SCALE_X, s); } NotifyOthers = true; } break; } case IDC_X: { if (NotifyOthers) { NotifyOthers = false; int64 x = GetCtrlValue(IDC_X); int s = (int) ((x * 100) / OldX); SetCtrlValue(IDC_SCALE_X, s); if (GetCtrlValue(IDC_ASPECT_RATIO) && OldX > 0) { double Ratio = (double)OldY/OldX; int64 y = GetCtrlValue(IDC_Y); y = (int) ((double)x * Ratio); SetCtrlValue(IDC_Y, y); SetCtrlValue(IDC_SCALE_Y, s); } NotifyOthers = true; } break; } case IDC_Y: { if (NotifyOthers) { NotifyOthers = false; int64 y = GetCtrlValue(IDC_Y); int s = (int) ((y * 100) / OldY); SetCtrlValue(IDC_SCALE_Y, s); if (GetCtrlValue(IDC_ASPECT_RATIO) && OldX > 0) { double Ratio = (double)OldY/OldX; int64 x = GetCtrlValue(IDC_X); x = (int64) ((double)y / Ratio); SetCtrlValue(IDC_X, x); SetCtrlValue(IDC_SCALE_X, s); } NotifyOthers = true; } break; } case IDOK: { x = (int) GetCtrlValue(IDC_X); y = (int) GetCtrlValue(IDC_Y); Bits = BitsFromIndex((int) GetCtrlValue(IDC_BITS)); ScaleOption = (int) GetCtrlValue(IDC_RESAMPLE); MaintainAspectRatio = GetCtrlValue(IDC_ASPECT_RATIO) != 0; KeepData = GetCtrlValue(IDC_KEEP_DATA) != 0; if (Colour) { Background = Colour->Colour(); RemapPalette = Colour->RemapPalette(); Palette = new LPalette(Colour->Palette()); } for (int *A = AlignTypes; *A; A++) { LViewI *v = FindControl(*A); if (v && v->Value()) { AlignType = *A; break; } } // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId() == IDOK); break; } } return 0; } diff --git a/src/ImageProperties.h b/src/ImageProperties.h --- a/src/ImageProperties.h +++ b/src/ImageProperties.h @@ -1,39 +1,39 @@ #ifndef __IMAGE_PROPERTIES_H #define __IMAGE_PROPERTIES_H #define IMGPROP_SCALE_CROP 0 #define IMGPROP_SCALE_RESAMPLE 1 extern int AlignTypes[]; -class ImageProperties : public LDialog +class ImagePropertiesDlg : public LDialog { ImgWnd *Wnd; int OldX, OldY; LColourCtrl *Colour; bool NotifyOthers; LSurface *pDC; int BitsFromIndex(int i); int IndexFromBits(int i); void EnableCtrls(bool i); public: int x, y, Bits; int ScaleOption; // IMGPROP_SCALE_??? bool MaintainAspectRatio; bool KeepData; bool RemapPalette; LColour Background; LPalette *Palette; int AlignType; - ImageProperties(ImgWnd *wnd); - ~ImageProperties(); + ImagePropertiesDlg(ImgWnd *wnd); + ~ImagePropertiesDlg(); int OnNotify(LViewI *Ctrl, LNotification n); }; #endif diff --git a/src/ImageRegTypes.cpp b/src/ImageRegTypes.cpp --- a/src/ImageRegTypes.cpp +++ b/src/ImageRegTypes.cpp @@ -1,347 +1,346 @@ /* ** FILE: ImageRegTypes.cpp ** AUTHOR: Matthew Allen ** DATE: 6/5/1999 ** DESCRIPTION: Image file types registering util ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/List.h" // #include "image.h" #include "resdefs.h" const char *ImageFile = "i.Mage File"; class ImageImgFileType { public: const char *Name; const char *Ext; } Types[] = { {"Bitmap", "bmp"}, {"Jpeg", "jpg"}, {"Jpeg", "jpeg"}, {"Gif", "gif"}, {"Portable Network Graphic", "png"}, {"Tiff", "tif"}, {"Tiff", "tiff"}, {"Pcx", "pcx"}, {"Targa", "tga"}, {0, 0} }; ////////////////////////////////////////////////////////////////////////////// #define A_NONE 1 #define A_REGISTED 2 #define A_REGISTER 3 #define A_UNREGISTER 4 const char *Actions[] = {"", "", "Registered", "Register", "Unregister"}; class ImgFileType : public LListItem { ImageImgFileType *Type; int Action; public: ImgFileType(ImageImgFileType *t = 0); // Data ImageImgFileType *GetType(); void SetType(ImageImgFileType *i); int GetAction(); void SetAction(int i); const char *Extension(); // Item const char *GetText(int i); int GetImage(int Flags = 0); }; class ImageTypeRegister : public LDialog { LList *Lst; public: ImageTypeRegister(LView *p); int OnNotify(LViewI *Ctrl, LNotification n); void SetSelectionAction(int a, int Not, int Must); }; ////////////////////////////////////////////////////////////////////////////// #ifndef WIN32 typedef void *HKEY; #endif bool DeleteRegKey(HKEY hKey, TCHAR *Name) { #ifdef WIN32 HKEY hSub = 0; if (RegOpenKey(hKey, Name, &hSub) == ERROR_SUCCESS) { TCHAR KeyName[256]; int i=0; while (RegEnumKey(hSub, i++, KeyName, sizeof(KeyName)) == ERROR_SUCCESS) { DeleteRegKey(hSub, KeyName); } } return RegDeleteKey(hKey, Name) == ERROR_SUCCESS; #else return false; #endif } ////////////////////////////////////////////////////////////////////////////// ImgFileType::ImgFileType(ImageImgFileType *t) { // Cols = 3; SetAction(A_NONE); SetType(t); } ImageImgFileType *ImgFileType::GetType() { return Type; } void ImgFileType::SetType(ImageImgFileType *i) { Type = i; if (Type && Type->Ext) { #ifdef WIN32 LRegKey k(false, "HKEY_CLASSES_ROOT\\.%s", Type->Ext); if (k.IsOk()) { LAutoString a(NewStr(k.GetStr())); if (a) { if (stricmp(a, ImageFile) == 0) { SetAction(A_REGISTED); Update(); } } } #endif } } int ImgFileType::GetAction() { return Action; } void ImgFileType::SetAction(int i) { Action = i; } const char *ImgFileType::GetText(int i) { switch (i) { case 0: { if (Type) return Type->Name; break; } case 1: { if (Type) return Type->Ext; break; } case 2: { return Actions[Action]; } } return ""; } int ImgFileType::GetImage(int Flags) { return 0; } const char *ImgFileType::Extension() { return (Type) ? Type->Ext : 0; } //////////////////////////////////////////////////////////////////////////////////// ImageTypeRegister::ImageTypeRegister(LView *p) { SetParent(p); if (LoadFromResource(IDD_REGTYPES)) { MoveToCenter(); Lst = dynamic_cast(FindControl(IDC_LIST)); if (Lst) { Lst->AddColumn("Type", 130); Lst->AddColumn("Extension", 70); Lst->AddColumn("Status", 70); for (int i=0; Types[i].Name; i++) { Lst->Insert(new ImgFileType(Types+i)); } // Lst->SetImageList(MainWnd->GetImageList(), FALSE); } - - DoModal(); } } void ImageTypeRegister::SetSelectionAction(int a, int Not, int Must) { if (Lst) { List l; if (Lst->GetSelection(l)) { for (auto Item: l) { ImgFileType *Msg = dynamic_cast(Item); if (Msg) { if (Not < 1 || Msg->GetAction() != Not) { if (Must < 1 || Msg->GetAction() == Must) { Msg->SetAction(a); Item->Update(); } } } } } Lst->LView::Invalidate(); } } int ImageTypeRegister::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: { // first build our type key // "i.Mage File" #ifdef WIN32 LRegKey k(true, "HKEY_CLASSES_ROOT\\%s", ImageFile); if (k.IsOk() || k.Create()) { auto ExeFile = LGetExeFile(); k.SetStr(0, ImageFile); LRegKey DefaultIcon(true, "%s\\DefaultIcon", k.Name()); if (DefaultIcon.IsOk() || DefaultIcon.Create()) { char Icon[256]; sprintf_s(Icon, sizeof(Icon), "%s,1", ExeFile.Get()); DefaultIcon.SetStr(0, Icon); } LRegKey Command(true, "%s\\shell\\open\\command", k.Name()); if (Command.IsOk() || Command.Create()) { char Cmd[256]; sprintf_s(Cmd, sizeof(Cmd), "%s \"%%L\"", ExeFile.Get()); Command.SetStr(0, Cmd); } } #endif if (Lst) { List l; if (Lst->GetAll(l)) { for (auto Item: l) { ImgFileType *Type = dynamic_cast(Item); if (Type) { const char *Ext = Type->Extension(); if (Ext) { char Extension[64]; sprintf(Extension, ".%s", Ext); switch (Type->GetAction()) { case A_REGISTER: { #ifdef WIN32 LRegKey k("HKEY_CLASSES_ROOT\\%s", Extension); if (k.IsOk() || k.Create()) { k.SetStr(0, ImageFile); } #endif break; } case A_UNREGISTER: { #ifdef WIN32 LRegKey k("HKEY_CLASSES_ROOT\\%s", Extension); k.SetStr(0, 0); #endif break; } } } } } } } // fall thru } case IDCANCEL: { EndModal(0); break; } case IDC_REGISTER: { SetSelectionAction(A_REGISTER, A_REGISTED, 0); break; } case IDC_CLEAR: { SetSelectionAction(A_UNREGISTER, 0, A_REGISTED); break; } } return 0; } void RegisterFileTypes(LView *Parent) { - ImageTypeRegister Dlg(Parent); + auto Dlg = new ImageTypeRegister(Parent); + Dlg->DoModal(NULL); } diff --git a/src/OperatorDlg.cpp b/src/OperatorDlg.cpp --- a/src/OperatorDlg.cpp +++ b/src/OperatorDlg.cpp @@ -1,109 +1,107 @@ #include #include "Image.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/Slider.h" #include "lgi/common/Button.h" /////////////////////////////////////////////////////////////////////////////////////////////// #define IDM_MODE 100 #define IDM_PERCENT 101 #define IDM_EDIT_A 102 #define IDM_SCROLL_A 103 OperatorDlg::OperatorDlg(ImgWnd *ws) { SetParent(ws); LRect r(0, 0, 170, 225); SetPos(r); MoveToMouse(); MoveOnScreen(); Name("Paint Mode"); if (Ws()) { Children.Insert(Mode = new LRadioGroup(IDM_MODE, 10, 10, 145, 150, "Mode", Ws()->Op())); if (Mode) { Mode->Append(10, 18, "Set"); Mode->Append(10, 38, "And"); Mode->Append(10, 58, "Or"); Mode->Append(10, 78, "Xor"); Mode->Append(10, 98, "Alpha"); } Children.Insert(AlphaPercent = new LTextLabel(IDM_PERCENT, 16, 134, 27, 16, "0%")); if (AlphaPercent) { char Str[256]; sprintf(Str, "%i%%", Ws()->Alpha() * 100 / 255); AlphaPercent->Name(Str); } Children.Insert(AlphaDecimal = new LEdit(IDM_EDIT_A, 45, 132, 30, -1, "?")); if (AlphaDecimal) { AlphaDecimal->Value(Ws()->Alpha()); } Children.Insert(AlphaScroll = new LSlider(IDM_SCROLL_A, 80, 132, 70, 20, "Alpha", false)); if (AlphaScroll) { AlphaScroll->SetLimits(0, 255); AlphaScroll->Value(Ws()->Alpha()); } Children.Insert(Ok = new LButton(IDOK, 10, 175, 60, 20, "Ok")); Children.Insert(Cancel = new LButton(IDCANCEL, 80, 175, 60, 20, "Cancel")); } else { Mode = 0; Ok = 0; Cancel = 0; } - - DoModal(); } int OperatorDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDM_EDIT_A: { int a = limit(AlphaDecimal->Value(), 0, 255); char Str[256]; sprintf(Str, "%i%%", a * 100 / 255); AlphaPercent->Name(Str); AlphaScroll->Value(a); break; } case IDM_SCROLL_A: { int a = limit(AlphaScroll->Value(), 0, 255); char Str[256]; sprintf(Str, "%i%%", a * 100 / 255); AlphaPercent->Name(Str); AlphaDecimal->Value(a); break; } case IDOK: { if (Ws()) { Ws()->Alpha(AlphaScroll->Value()); Ws()->Op(Mode->Value()); } // fall thru } case IDCANCEL: { EndModal(0); break; } } return 0; } diff --git a/src/ReduceDlg.cpp b/src/ReduceDlg.cpp --- a/src/ReduceDlg.cpp +++ b/src/ReduceDlg.cpp @@ -1,115 +1,115 @@ #include #include "Image.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/Edit.h" #include "lgi/common/CheckBox.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/Palette.h" ////////////////////////////////////////////////////////////////////// LReduceDlg::LReduceDlg(LView *p) { SetParent(p); Colours = 0; Palette = 0; if (LoadFromResource(IDD_REDUCE)) { MoveToCenter(); _Pal = dynamic_cast(FindControl(IDC_PALETTETYPE)); _Match = dynamic_cast(FindControl(IDC_MATCHTYPE)); _File = dynamic_cast(FindControl(IDC_FILE)); _Browse = dynamic_cast(FindControl(IDC_BROWSE)); _To = dynamic_cast(FindControl(IDC_TO)); _ColourNum = dynamic_cast(FindControl(IDC_COLOURS)); _Colours = dynamic_cast(FindControl(IDC_COLOURS_TEXT)); _HalfTone = dynamic_cast(FindControl(IDC_HALFTONE)); if (_Pal && _Match && _File && _Browse && _To && _ColourNum && _Colours && _HalfTone) { LNotification note; OnNotify(_Pal, note); OnNotify(_Match, note); - - DoModal(); } } } LReduceDlg::~LReduceDlg() { DeleteArray(Palette); } int LReduceDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: { PalType = (LColourReducePalette) _Pal->Value(); MatchType = (LColourReduceMatch) _Match->Value(); auto c = _ColourNum->Name(); Colours = (c) ? atoi(c) : 0; if (Colours == 0) Colours = 256; LFile F; if (F.Open(_File->Name(), O_READ)) { Palette = new LPalette(0, 256); if (Palette) { if (!Palette->Load(F)) { // Not a valid palette file DeleteObj(Palette); } } } // fall thru } case IDCANCEL: { - EndModal(); + EndModal(Ctrl->GetId() == IDOK); break; } case IDC_TO: { _Colours->Enabled(_To->Value()); _ColourNum->Enabled(_To->Value()); break; } case IDC_BROWSE: { - LFileSelect Select; + auto Select = new LFileSelect; - Select.Parent(this); - Select.Type("Palette files", "*.pal"); - if (Select.Open()) + Select->Parent(this); + Select->Type("Palette files", "*.pal"); + Select->Open([this](auto s, auto ok) { - _File->Name(Select.Name()); - } + if (ok) + _File->Name(s->Name()); + delete s; + }); break; } case IDC_PALETTETYPE: { int p = _Pal->Value(); _HalfTone->Enabled(p == 0); if (_Match->Value() == 1) _Match->Value(2); _To->Enabled(p == 1); _Colours->Enabled(p == 1 && _To->Value()); _ColourNum->Enabled(p == 1 && _To->Value()); _File->Enabled(p == 2); _Browse->Enabled(p == 2); break; } } return 0; } diff --git a/src/TextDlg.cpp b/src/TextDlg.cpp --- a/src/TextDlg.cpp +++ b/src/TextDlg.cpp @@ -1,204 +1,202 @@ #include #include "Image.h" #include "lgi/common/Edit.h" #include "lgi/common/Combo.h" #include "lgi/common/CheckBox.h" #include "lgi/common/Button.h" #include "lgi/common/DisplayString.h" /////////////////////////////////////////////////////////////////////////////////////// #define IDM_TEXT 100 #define IDM_FONT 101 #define IDM_POINT 102 #define IDM_ANTI_ALIAS 103 TextDlg::TextDlg(ImgWnd *parent) { SetParent(parent); LRect r(0, 0, 250, 300); SetPos(r); MoveToMouse(); MoveOnScreen(); Children.Insert(Text = new LEdit(IDM_TEXT, 10, 10, 220, 120, "text")); Children.Insert(Font = new LCombo(IDM_FONT, 10, 140, 140, 120, "MS Sans Serif")); if (Font) { Font->Sort(true); #ifdef WIN32 HDC hDC = CreateCompatibleDC(NULL); if (hDC) { EnumFontFamilies( hDC, NULL, (FONTENUMPROC) Enum, (LPARAM) this); DeleteDC(hDC); } #endif } Children.Insert(Point = new LCombo(IDM_POINT, 160, 140, 50, 120, "8")); if (Point) { const char *Points[] = {"8", "9", "10", "11", "12", "14", "16", "20", "24", "28", "32", "48", "64", "100", 0}; for (const char **p = Points; *p; p++) { Point->Insert(*p); } } Children.Insert(AntiAlias = new LCheckBox(IDM_ANTI_ALIAS, 10, 170, -1, -1, "Anti alias", true)); Children.Insert(Ok = new LButton(IDOK, 10, 250, 60, 20, "Ok")); Children.Insert(Cancel = new LButton(IDCANCEL, 80, 250, 60, 20, "Cancel")); if (Font && Text && Point && Ttf.Create(Font->Name(), LCss::Len(LCss::LenPt, Point->Value()))) { Text->SetFont(&Ttf); } - - DoModal(); } #ifdef WIN32 int CALLBACK TextDlg::Enum(ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam) { TextDlg *Wnd = (TextDlg*) lParam; if (Wnd) { if (Wnd->Font) { char *Str = #ifdef _UNICODE WideToUtf8 #else NewStr #endif (lpelf->elfLogFont.lfFaceName); Wnd->Font->Insert(Str); } } return TRUE; } #endif int TextDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDM_FONT: case IDM_POINT: { if (Text && Point && Font) { if (Ttf.Create(Font->Name(), LCss::Len(LCss::LenPt, Point->Value()))) { Text->SetFont(&Ttf); Text->Invalidate(); } } break; } case IDOK: { if (Ws() && Ws()->GetDC() && Text) { LSurface *pDC = Ws()->GetDC(); LSurface *Brush = Ws()->Brush(); if (!Brush) { LAutoPtr Mem(Brush = new LMemDC); Ws()->Brush(Mem); } if (Brush && pDC) { auto Str = Text->Name(); LDisplayString ds(&Ttf, Str); Brush->Create(ds.X(), ds.Y(), pDC->GetColourSpace()); Ttf.Fore(L_WHITE); Ttf.Back(L_BLACK); Ttf.Transparent(true); LSurface *pTemp = new LMemDC; if (pTemp && pTemp->Create(ds.X(), ds.Y(), (AntiAlias->Value()) ? System24BitColourSpace : CsIndex8)) { pTemp->Colour(Rgb24(0, 0, 0)); pTemp->Rectangle(); ds.Draw(pTemp, 0, 0); if (Brush->HasAlpha(true)) { ImgCol c = Ws()->Fore(); Brush->Colour(CBit(Brush->GetBits(), c.c, c.Bits)); Brush->Rectangle(); Brush->DrawOnAlpha(true); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { Brush->Colour(pTemp->Get(x, y)); Brush->Set(x, y); } } Brush->DrawOnAlpha(false); Ws()->Op(GDC_ALPHA); Ws()->Alpha(255); Ws()->AttachTool(TOOL_SELECT_BRUSH); } } /* LSurface *pMask = Brush->GetMask(); if (pColour && pMask) { COLOUR Fore = Ws()->Fore(); pColour->Colour(Rgb24(255, 255, 255)); pColour->Rectangle(); Ttf.Text(pColour, 0, 0, Str); for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { COLOUR c = pColour->Get(x, y); bool Trans = (R24(c) > 128); pMask->Colour((Trans) ? -1 : 0); pMask->Set(x, y); pColour->Colour((Trans) ? 0 : Fore); pColour->Set(x, y); } } Ws()->AttachTool(TOOL_SELECT_PASTE); } */ } } EndModal(0); break; } case IDCANCEL: { EndModal(0); break; } } return 0; } diff --git a/src/UiPaletteCtrl.cpp b/src/UiPaletteCtrl.cpp --- a/src/UiPaletteCtrl.cpp +++ b/src/UiPaletteCtrl.cpp @@ -1,1198 +1,1219 @@ /* ** FILE: UiPaletteView.cpp ** AUTHOR: Matthew Allen ** DATE: 27/4/99 ** DESCRIPTION: Image ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include "Image.h" #include "UiPaletteCtrl.h" #include "resdefs.h" #include "lgi/common/Token.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/Edit.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Palette.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #include "resdefs.h" ///////////////////////////////////////////////////////////////////////////////////////// class PalSizeDlg : public LDialog { LRadioGroup *_Group; LEdit *_Colours; LPalette *Pal; public: int64 NewColours; PalSizeDlg(LView *Parent, LPalette *pal) { _Group = 0; _Colours = 0; Pal = pal; SetParent(Parent); if (LoadFromResource(IDD_PAL_SIZE)) { _Group = dynamic_cast(FindControl(IDC_PAL_SIZE)); _Colours = dynamic_cast(FindControl(IDC_COLOURS)); NewColours = Pal ? Pal->GetSize() : 0; if (_Group) { switch (NewColours) { case 2: { _Group->Value(0); break; } case 16: { _Group->Value(1); break; } case 256: { _Group->Value(2); break; } default: { _Group->Value(3); break; } } } if (_Colours) { _Colours->Value(NewColours); } MoveToCenter(); } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: { if (_Group && _Colours) { switch (_Group->Value()) { case 0: { NewColours = 2; break; } case 1: { NewColours = 16; break; } case 2: { NewColours = 256; break; } default: { NewColours = _Colours->Value(); break; } } } // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId()); break; } } return 0; } }; ///////////////////////////////////////////////////////////////////////////////////////// // Defines enum ImageMessages { IDM_SET = 400, IDM_GRADIENT_RGB, IDM_REVERSE, IDM_GREYSCALE, IDM_RGB_CUBE, IDM_HLS_CUBE, IDM_WHITE, IDM_SWAP, IDM_GRADIENT_HLS, IDM_SET_SIZE, IDM_STD_16, }; ////////////////////////////////////////////////////////////////////////////////// LPalette *NewMappedPalette(int Size) { LPalette *Pal = new LPalette(0, Size); if (Pal) { GdcRGB *p = (*Pal)[0]; if (p) { for (int i=0; iRegister(); } #elif defined BEOS GetBView()->SetFlags(GetBView()->Flags() | B_NAVIGABLE); #endif } PaletteCtrl::~PaletteCtrl() { SetPalette(0, true); DeleteObj(pDC); } void PaletteCtrl::OnPosChange() { DrawPalette(); } void PaletteCtrl::DrawPalette() { DeleteObj(pDC); pDC = new LMemDC; // LRect r = GetClient(); /* r.x1 -= 4; r.y1 -= 4; */ Cx = 16; // max(r.X() / Grid, 1); Cy = (256 + Cx - 1) / Cx; if (pDC && pDC->Create(1 + (Grid * Cx), 1 + (Grid * Cy), GdcD->GetColourSpace())) { pDC->Colour(L_WORKSPACE); pDC->Rectangle(); if (Pal) { bool Indexed = (GdcD->GetBits() <= 8); for (int y=0; y= 256) break; int X = 1 + (Grid * x); int Y = 1 + (Grid * y); if (c < Pal->GetSize()) { GdcRGB *p = (*Pal)[c]; pDC->Colour((Indexed) ? c : GdcD->GetColour(Rgb24(p->r, p->g, p->b))); pDC->Rectangle(X, Y, X+Grid-2, Y+Grid-2); } else { pDC->Colour(L_MED); for (int Iy=0; IySet(X+Ix, Y+Iy); } } } } } } else { LSysFont->Transparent(true); LSysFont->Colour(L_TEXT, L_WORKSPACE); LDisplayString ds(LSysFont, "No palette"); ds.Draw(pDC, 5, 5); } } DrawCursor(Focus()); } int PaletteCtrl::GetIndex() { return Index; } int PaletteCtrl::GetPrevIndex() { return PrevIndex; } int PaletteCtrl::GetIndex(int x, int y) { LRect a = GetPos(); a.Offset(-a.x1, -a.y1); a.Inset(3, 3); LPoint p(x, y); if (Pal && p.Inside(a)) { p.x -= a.x1; p.y -= a.y1; p.x /= Grid; p.y /= Grid; int Index = (p.y * Cx) + p.x; if (Index >= 0 && Index < Pal->GetSize()) { return Index; } } return -1; } void PaletteCtrl::SetIndex(int i, bool Drag, int Flags) { if (Pal && i >= 0) { int NewIndex = limit(i, 0, Pal->GetSize()-1); if (!Drag || NewIndex != Index) { DrawCursor(false); if (!Drag) { PrevIndex = NewIndex; } Index = NewIndex; DrawCursor(true); Invalidate(); if (GetParent()) { LNotification note; GetParent()->OnNotify(this, note); } } } } LPalette *PaletteCtrl::GetPalette() { return Pal; } bool PaletteCtrl::SetPalette(LPalette *pal, bool own) { if (OwnPal) DeleteObj(Pal); Pal = pal; OwnPal = own; Update(); OnPaletteChange(); return true; } void PaletteCtrl::OnPaletteChange() { Update(); if (GetParent()) { LNotification note; GetParent()->OnNotify(this, note); } } void PaletteCtrl::operator =(LPalette *p) { Pal = p; DrawPalette(); if (Visible()) { Invalidate(); } } PaletteCtrl::operator LColour() { if (Pal) return LColour(Index, 8, Pal); return LColour(); } void PaletteCtrl::Update() { DrawPalette(); Invalidate(); } void PaletteCtrl::OnFocus(bool f) { DrawCursor(f); Invalidate(); } void PaletteCtrl::OnPaint(LSurface *pScreen) { LRect a = GetPos(); a.Offset(-a.x1, -a.y1); if (a.X()>3 && a.Y()>3) { if (a.Valid()) { pScreen->Colour(L_WORKSPACE); if (pDC) { LRect Inset(0, 0, a.X()-1, a.Y()-1); LRegion Temp(a); pScreen->Blt(a.x1, a.y1, pDC, &Inset); LRect Actual(0, 0, pDC->X()-1, pDC->Y()-1); Actual.Offset(a.x1, a.y1); Temp.Subtract(&Actual); for (LRect *r = Temp.First(); r; r = Temp.Next()) { pScreen->Rectangle(r); } } else { pScreen->Rectangle(&a); } } } } void PaletteCtrl::DrawCursor(bool On) { if (pDC) { LRect a = GetPos(); a.Offset(-a.x1, -a.y1); uint Style = 0xFFFFFFFF; pDC->Colour((On) ? LColour(128, 128, 128) : LColour(L_WORKSPACE)); if (On) Style = pDC->LineStyle(0xAAAAAAAA); if (Pal) { int Start = MIN(PrevIndex, Index); int End = MAX(PrevIndex, Index); if (PrevIndex >= 0 && PrevIndex < 1000 && Index >= 0 && Index < 1000) { for (int i=Start; i<=End; i++) { int x = i % Cx; int y = i / Cx; a.ZOff(Grid, Grid); a.Offset((x * Grid), (y * Grid)); pDC->Box(&a); } } else { printf("%s:%i - Out of range (%i,%i)\n", __FILE__, __LINE__, PrevIndex, Index); } } else { pDC->Box(); } if (On) pDC->LineStyle(Style); } } LMessage::Result PaletteCtrl::OnEvent(LMessage *Msg) { // This event proc is just for the ColourDlg to call back into switch (Msg->Msg()) { #ifdef WIN32 case COL_CTRL_GET_BITS: { return 24; } case COL_CTRL_GET: { if (Pal) { GdcRGB *p = (*Pal)[Index]; if (p) { return Rgb24(p->r, p->g, p->b); } } return 0; } case COL_CTRL_GET_PAL: { return 0; } case COL_CTRL_SET: { if (Pal && Msg->b == 24) { COLOUR c = (COLOUR) Msg->a; int Start = min(Index, PrevIndex); int End = max(Index, PrevIndex); for (int i=Start; i<=End; i++) { GdcRGB *Cur = (*Pal)[i]; if (Cur) { Cur->r = R24(c); Cur->g = G24(c); Cur->b = B24(c); } } DrawPalette(); Invalidate(); } break; } #endif } return LView::OnEvent(Msg); } void PaletteCtrl::Cut() { } void PaletteCtrl::Copy() { if (Pal) { int Start = CopyIndex = MIN(Index, PrevIndex); int End = MAX(Index, PrevIndex); int Len = End - Start + 1; LStringPipe p; for (int i=0; ir, s->g, s->b); p.Push(Str); } } char *Txt = p.NewStr(); if (Txt) { LClipBoard Clip(this); Clip.Text(Txt); DeleteArray(Txt); } } } void PaletteCtrl::Paste() { char *Txt = 0; { LClipBoard Clip(this); Txt = Clip.Text(); } if (Txt) { LToken Line(Txt, "\r\n"); for (int L=0; L= 6) { char n[3]; n[0] = *v++; n[1] = *v++; n[2] = 0; d->r = htoi(n); n[0] = *v++; n[1] = *v++; n[2] = 0; d->g = htoi(n); n[0] = *v++; n[1] = *v++; n[2] = 0; d->b = htoi(n); } } } } CopyIndex = Index; OnPaletteChange(); } bool PaletteCtrl::OnKey(LKey &k) { if (k.Ctrl()) { switch (k.c16) { case 'c': case 'C': { if (k.Down()) { Copy(); } return true; break; } case 'v': case 'V': { if (k.Down()) { Paste(); } return true; break; } } } return false; } void PaletteCtrl::OpenFile(const char *FileName) { if (FileName) { auto Dot = strrchr(FileName, '.'); if (Dot) { if (stricmp(Dot+1, "pal") == 0) { LFile F; if (F.Open(FileName, O_READ)) { if (!Pal) { Pal = NewMappedPalette(256); } else { Pal->SetSize(256); } if (Pal) { if (!Pal->Load(F)) { LgiMsg(this, LLoadString(IDS_ERROR_LOAD_PALETTE), LLoadString(IDS_PALETTE), MB_OK); } OnPaletteChange(); } } return; } } // Doesn't seem to be a palette, try loading the // image and extracting a palette LSurface *pDC = GdcD->Load(FileName); if (pDC) { LPalette *NewPal = pDC->Palette(); if (NewPal) { if (!Pal) { Pal = new LPalette; } if (Pal) { Pal->Set(Pal); } } DeleteObj(pDC); } } } void PaletteCtrl::SaveFile(const char *FileName) { if (FileName) { char Str[256]; strcpy(Str, FileName); if (!strchr(Str, '.')) { strcat(Str, ".pal"); } LFile F; if (F.Open(Str, O_WRITE)) { if (!Pal) { Pal = NewMappedPalette(256); } if (Pal) { if (!Pal->Save(F, GDCPAL_JASC)) { LgiMsg(this, LLoadString(IDS_ERROR_SAVE_PALETTE), LLoadString(IDS_PALETTE), MB_OK); } } } } } void PaletteCtrl::OnMouseClick(LMouse &m) { - if (m.Down()) Focus(true); + if (m.Down()) + Focus(true); + if (m.Left()) { if (m.Double() && m.Down()) { - LAutoPtr Dlg(new ColourDlg(this, COL_RGB)); - if (Dlg && Dlg->DoModal() == IDOK) + auto Dlg = new ColourDlg(this, COL_RGB); + Dlg->DoModal([this, Dlg](auto dlg, auto code) { - if (!Pal) - { - Pal = new LPalette; - if (Pal) - Pal->SetSize(1); - } - - if (Pal && Index < Pal->GetSize()) + if (code == IDOK) { - GdcRGB *To = (*Pal)[Index]; - if (To) + if (!Pal) { - To->r = Dlg->c.r(); - To->g = Dlg->c.g(); - To->b = Dlg->c.b(); + Pal = new LPalette; + if (Pal) + Pal->SetSize(1); } - } - DrawPalette(); - Invalidate(); + if (Pal && Index < Pal->GetSize()) + { + GdcRGB *To = (*Pal)[Index]; + if (To) + { + To->r = Dlg->c.r(); + To->g = Dlg->c.g(); + To->b = Dlg->c.b(); + } + } - OnPaletteChange(); - } + DrawPalette(); + Invalidate(); + + OnPaletteChange(); + } + delete dlg; + }); + return; } else { if (m.Down()) { SetIndex(GetIndex(m.x, m.y), false, m.Flags); } Selecting = m.Down(); Capture(m.Down()); } } if (m.IsContextMenu()) { LSubMenu RClick; RClick.AppendItem("Set", IDM_SET, true); RClick.AppendSeparator(); RClick.AppendItem("Copy", IDM_COPY, Pal != NULL); RClick.AppendItem("Paste", IDM_PASTE, true); // RClick.AppendItem("Swap", IDM_SWAP, true); RClick.AppendSeparator(); RClick.AppendItem("Load", IDM_OPEN, true); RClick.AppendItem("Save", IDM_SAVE, Pal != NULL); // RClick.AppendItem("Set Size", IDM_SET_SIZE, true); RClick.AppendSeparator(); RClick.AppendItem("Gradient RGB", IDM_GRADIENT_RGB, Pal != NULL); RClick.AppendItem("Gradient HLS", IDM_GRADIENT_HLS, Pal != NULL); RClick.AppendItem("Reverse", IDM_REVERSE, Pal != NULL); RClick.AppendSeparator(); RClick.AppendItem("White", IDM_WHITE, true); RClick.AppendItem("Grey scale", IDM_GREYSCALE, true); RClick.AppendItem("RGB cube", IDM_RGB_CUBE, true); RClick.AppendItem("HLS cube", IDM_HLS_CUBE, true); RClick.AppendItem("Standard 16", IDM_STD_16, true); if (GetMouse(m, true)) { switch (RClick.Float(this, m.x, m.y)) { case IDM_SET: { - if (Pal) + if (!Pal) + break; + + auto Dlg = new ColourDlg(this, COL_RGB); + Dlg->DoModal([this](auto dlg, auto code) { - ColourDlg Dlg(this, COL_RGB); - if (Dlg.DoModal() == IDOK) + if (code == IDOK) + { OnPaletteChange(); - } + } + delete dlg; + }); break; } case IDM_COPY: { Copy(); break; } case IDM_PASTE: { Paste(); break; } case IDM_OPEN: { - LFileSelect Select; + auto Select = new LFileSelect; + + Select->Parent(this); + Select->Type("JASC Palette", "*.pal"); + Select->Type("All files", LGI_ALL_FILES); - Select.Parent(this); - Select.Type("JASC Palette", "*.pal"); - Select.Type("All files", LGI_ALL_FILES); - - if (Select.Open()) + Select->Open([this](auto s, auto ok) { - OpenFile(Select.Name()); - } + if (ok) + OpenFile(s->Name()); + delete s; + }); break; } case IDM_SAVE: { - if (Pal) - { - LFileSelect Select; + if (!Pal) + break; + + auto Select = new LFileSelect; - Select.Parent(this); - Select.Type("JASC Palette", "*.pal"); - Select.Type("All files", LGI_ALL_FILES); + Select->Parent(this); + Select->Type("JASC Palette", "*.pal"); + Select->Type("All files", LGI_ALL_FILES); - if (Select.Save()) - { - SaveFile(Select.Name()); - } - } + Select->Save([this](auto s, auto ok) + { + if (ok) + SaveFile(s->Name()); + delete s; + }); break; } case IDM_SET_SIZE: { - PalSizeDlg Dlg(this, Pal); - if (Dlg.DoModal() == IDOK) + auto Dlg = new PalSizeDlg(this, Pal); + Dlg->DoModal([this, Dlg](auto dlg, auto code) { - Pal->SetSize(Dlg.NewColours); - OnPaletteChange(); - } + if (code == IDOK) + { + Pal->SetSize(Dlg->NewColours); + OnPaletteChange(); + } + delete dlg; + }); break; } case IDM_SWAP: { if (Pal && CopyIndex >= 0) { /* for (int i=0; iGetSize(); i++) { GdcRGB *s = (*Clipboard)[i]; GdcRGB *d = (*Pal)[Index+i]; GdcRGB *Prev = (*Pal)[CopyIndex+i]; if (s && d) { if (Prev) *Prev = *d; *d = *s; } OnPaletteChange(); } */ CopyIndex = Index; } break; } case IDM_GRADIENT_HLS: { bool Undefined = false; if (Pal && PrevIndex != Index) { int Start = MIN(Index, PrevIndex); int End = MAX(Index, PrevIndex); GdcRGB *s = (*Pal)[Start]; GdcRGB *e = (*Pal)[End]; if (s && e) { int Len = End - Start + 1; LColour StartC(s->r, s->g, s->b); LColour EndC(e->r, e->g, e->b); Undefined = !StartC.ToHLS() || !EndC.ToHLS(); if (!Undefined) { int Sh = StartC.GetH(); int Sl = StartC.GetL(); int Ss = StartC.GetS(); int Eh = EndC.GetH(); int El = EndC.GetL(); int Es = EndC.GetS(); if (abs(Eh - Sh) > 180) { if (Eh < Sh) { Eh += 360; } else { Sh += 360; } } int Dh = Eh - Sh; int Dl = El - Sl; int Ds = Es - Ss; for (int i=Start+1; ir = c.r(); Cur->g = c.g(); Cur->b = c.b(); } } } OnPaletteChange(); } } if (!Undefined) { break; } else { // fall through to RGB code } } case IDM_GRADIENT_RGB: { if (Pal && PrevIndex != Index) { int Start = MIN(Index, PrevIndex); int End = MAX(Index, PrevIndex); GdcRGB *s = (*Pal)[Start]; GdcRGB *e = (*Pal)[End]; int Len = End - Start + 1; if (s && e) { for (int i=1; ir = ((n * s->r) + (i * e->r)) / (Len - 1); c->g = ((n * s->g) + (i * e->g)) / (Len - 1); c->b = ((n * s->b) + (i * e->b)) / (Len - 1); } } OnPaletteChange(); } } break; } case IDM_REVERSE: { if (Pal && PrevIndex != Index) { int Start = MIN(Index, PrevIndex); int End = MAX(Index, PrevIndex); GdcRGB *s = (*Pal)[Start]; GdcRGB *e = (*Pal)[End]; if (s && e) { for (; StartSetSize(256); } if (Pal) { GdcRGB *p = (*Pal)[0]; for (int i=0; i<256; i++) { p->r = i; p->g = i; p->b = i; p++; } Index = -1; SetIndex(0); OnPaletteChange(); } break; } case IDM_RGB_CUBE: { if (!Pal) { Pal = new LPalette; } if (Pal) { Pal->CreateCube(); Index = -1; SetIndex(0); OnPaletteChange(); } break; } case IDM_HLS_CUBE: { if (!Pal) { Pal = NewMappedPalette(256); } else { Pal->SetSize(256); } if (Pal) { for (int h=0; h<15; h++) { int Hue = (h * 360) / 15; for (int i=0; i<16; i++) { GdcRGB *p = (*Pal)[((h+1)*16) + (15-i)]; if (p) { LColour c; c.SetHLS(Hue, (16-i) * 15, 255); p->r = c.r(); p->g = c.g(); p->b = c.b(); } } } for (int i=0; i<16; i++) { int k = (i << 4) | i; GdcRGB *rgb = (*Pal)[i]; rgb->r = k; rgb->g = k; rgb->b = k; } Index = -1; SetIndex(0); OnPaletteChange(); } break; } case IDM_STD_16: { if (!Pal) { Pal = NewMappedPalette(16); } else { Pal->SetSize(16); } if (Pal) { Pal->Set(0, 0, 0, 0); Pal->Set(1, 0x80, 0, 0); Pal->Set(2, 0, 0x80, 0); Pal->Set(3, 0x80, 0x80, 0); Pal->Set(4, 0, 0, 0x80); Pal->Set(5, 0x80, 0, 0x80); Pal->Set(6, 0, 0x80, 0x80); Pal->Set(7, 0x80, 0x80, 0x80); Pal->Set(8, 0xc0, 0xc0, 0xc0); Pal->Set(9, 0xff, 0, 0); Pal->Set(10, 0, 0xff, 0); Pal->Set(11, 0xff, 0xff, 0); Pal->Set(12, 0, 0, 0xff); Pal->Set(13, 0xff, 0, 0xff); Pal->Set(14, 0, 0xff, 0xff); Pal->Set(15, 0xff, 0xff, 0xff); Index = -1; SetIndex(0); OnPaletteChange(); } break; } case IDM_WHITE: { if (!Pal) { Pal = NewMappedPalette(256); } else { Pal->SetSize(256); } if (Pal) { GdcRGB *p = (*Pal)[0]; p->r = 0; p->g = 0; p->b = 0; p++; for (int i=1; i<256; i++) { p->r = 255; p->g = 255; p->b = 255; p++; } Index = -1; SetIndex(0); OnPaletteChange(); } break; } } DrawPalette(); Invalidate(); } } } void PaletteCtrl::OnMouseMove(LMouse &m) { if (m.Down() && m.Left() && Selecting) { SetIndex(GetIndex(m.x, m.y), true, m.Flags); } } diff --git a/src/UiPaletteView.cpp b/src/UiPaletteView.cpp --- a/src/UiPaletteView.cpp +++ b/src/UiPaletteView.cpp @@ -1,732 +1,744 @@ /* ** FILE: UiPaletteView.cpp ** AUTHOR: Matthew Allen ** DATE: 27/4/99 ** DESCRIPTION: Image ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include "Image.h" #include "lgi/common/Token.h" #include "lgi/common/DropFiles.h" #include "lgi/common/Edit.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Palette.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/TableLayout.h" #include "lgi/common/ScrollBar.h" #include "UiPaletteView.h" LPaletteUI::LPaletteUI(ImgWnd *app) { OpCtrl = NULL; #ifdef WIN32 WndFlags |= WS_CLIPCHILDREN; #endif App = app; ForeCol.Bits = 24; ForeCol.c = Rgb24(255, 0, 0); ForeCol.a = 0xff; BackCol.Bits = 24; BackCol.c = 0; BackCol.a = 0; Resizing = 0; Loading = false; Children.Insert(PalCtrl = new PaletteCtrl(100, 0, 0, 0, 0)); Sx = 200; Sy = 1000; LFontType Small; if (Small.GetSystemFont("Small")) { Font.Reset(Small.Create()); } else { Font.Reset(new LFont("Arial Narrow", #ifdef MAC "9pt" #else "8pt" #endif )); } SwapDown = false; } LPaletteUI::~LPaletteUI() { Serialize(false); } void LPaletteUI::OnCreate() { SetWindow(this); LoadFromResource(IDD_OPERATOR, this); GetViewById(IDC_OP, OpCtrl); auto s = dynamic_cast(FindControl(IDC_ALPHA_SCROLL)); if (s) { s->SetPage(16); s->SetLimits(0, 255+15); } Serialize(true); AttachChildren(); } bool LPaletteUI::GetFormats(LDragFormats &Formats) { Formats.SupportsFileDrops(); Formats.Supports("Image::Colour"); return Formats[0] != 0; } bool LPaletteUI::GetData(LArray &Data) { for (unsigned i=0; iGetPalette() : 0; } bool LPaletteUI::Serialize(bool Load) { bool Status = true; if (Load) { Loading = true; SetCtrlValue(IDC_OP, App->Op()); SetCtrlValue(IDC_ALPHA_EDIT, App->Alpha()); SetCtrlValue(IDC_ALPHA_SCROLL, App->Alpha()); LVariant a = 0; App->GetOptions()->GetValue(OPT_LinearAngle, a); SetCtrlValue(IDC_ANGLE, a.CastInt32()); Loading = false; } else { App->Op (GetCtrlValue(IDC_OP)); App->Alpha (GetCtrlValue(IDC_ALPHA_EDIT)); LVariant v; App->GetOptions()->SetValue(OPT_LinearAngle, v = GetCtrlValue(IDC_ANGLE)); } return Status; } int LPaletteUI::OnNotify(LViewI *Ctrl, LNotification n) { if (Loading) return 0; switch (Ctrl->GetId()) { case 100: { if (n.Type & INDEX_CHANGE) { if (!(n.Type & LGI_EF_CTRL)) { ForeCol.c = PalCtrl->GetIndex(); ForeCol.a = 255; ForeCol.Bits = 8; Invalidate(&ForePos); Invalidate(&ForeTextPos); } else { BackCol.c = PalCtrl->GetIndex(); BackCol.a = 255; BackCol.Bits = 8; Invalidate(&BackPos); Invalidate(&BackTextPos); } } else if (n.Type & PALETTE_CHANGE) { if (GetParent()) { LNotification note; GetParent()->OnNotify(this, note); Invalidate(&ForePos); Invalidate(&BackPos); } } break; } case IDC_OP: { int o = Ctrl->Value(); App->Op(o); SetCtrlEnabled(IDC_ALPHA_SCROLL, o == GDC_ALPHA); SetCtrlEnabled(IDC_ALPHA_EDIT, o == GDC_ALPHA); break; } case IDC_ANGLE: { LVariant v; App->GetOptions()->SetValue(OPT_LinearAngle, v = Ctrl->Value()); break; } case IDC_ALPHA_SCROLL: { int a = Ctrl->Value(); App->Alpha(a); Loading = true; SetCtrlValue(IDC_ALPHA_EDIT, a); Loading = false; break; } case IDC_ALPHA_EDIT: { int a = Ctrl->Value(); App->Alpha(a); Loading = true; SetCtrlValue(IDC_ALPHA_SCROLL, a); Loading = false; break; } } return 0; } void LPaletteUI::SetPalette(LPalette *Pal, bool Own) { if (PalCtrl) { PalCtrl->SetPalette(Pal, Own); LNotification note; if (GetParent()) GetParent()->OnNotify(this, note); } } void LPaletteUI::OnMouseClick(LMouse &m) { - // LRect r = GetPos(); + auto OnChange = [this]() + { + Invalidate(); + if (GetParent()) + { + LNotification note; + GetParent()->OnNotify(this, note); + } + }; if (m.Down()) { bool Changed = false; if (ForePos.Overlap(m.x, m.y)) { UseFore = true; if (m.Right()) { Drag(this, m.Event, DROPEFFECT_COPY); } else { - ColourDlg Colour(this, COL_RGB|COL_PAL|COL_HLS); - if (Colour.DoModal() == IDOK) + auto Colour = new ColourDlg(this, COL_RGB|COL_PAL|COL_HLS); + Colour->DoModal([this, Colour, OnChange](auto dlg, auto code) { - Changed = true; - Changed = true; - ForeCol.c = Colour.c.c32(); - ForeCol.Bits = 32; - if (Colour.Palette) + if (code == IDOK) { - SetPalette(Colour.Palette, true); - Colour.Palette.Reset(); + ForeCol.c = Colour->c.c32(); + ForeCol.Bits = 32; + if (Colour->Palette) + { + SetPalette(Colour->Palette, true); + Colour->Palette.Reset(); + } + + OnChange(); } - } + delete dlg; + }); + return; } } else if (BackPos.Overlap(m.x, m.y)) { UseFore = false; if (m.Right()) { Drag(this, m.Event, DROPEFFECT_COPY); } else { - ColourDlg Colour(this, COL_RGB|COL_PAL|COL_HLS); - if (Colour.DoModal() == IDOK) + auto Colour = new ColourDlg(this, COL_RGB|COL_PAL|COL_HLS); + Colour->DoModal([this, Colour, OnChange](auto dlg, auto code) { - Changed = true; - BackCol.c = Colour.c.c32(); - BackCol.Bits = 32; - if (Colour.Palette) + if (code == IDOK) { - SetPalette(Colour.Palette, true); - Colour.Palette.Reset(); + BackCol.c = Colour->c.c32(); + BackCol.Bits = 32; + if (Colour->Palette) + { + SetPalette(Colour->Palette, true); + Colour->Palette.Reset(); + } + + OnChange(); } - } + delete dlg; + }); + return; } } else if (SwapPos.Overlap(m.x, m.y)) { ImgCol c = ForeCol; int Bits = ForeCol.Bits; ForeCol = BackCol; ForeCol.Bits = BackCol.Bits; BackCol = c; BackCol.Bits = Bits; LRect r(ForePos.x1, ForePos.y1, X()-1, BackPos.y2); Invalidate(&r); } /* else if (m.x >= r.X()-10 && m.y >= r.Y()-10) { // resize Resizing = true; Capture(true); ResizeDx = r.X()-m.x; ResizeDy = r.Y()-m.y; ResizePos = r; } */ if (Changed) - { - Invalidate(); - if (GetParent()) - { - LNotification note; - GetParent()->OnNotify(this, note); - } - } + OnChange(); } else { if (Resizing) { Capture(false); Resizing = false; } } } void LPaletteUI::OnMouseMove(LMouse &m) { if (Resizing) { ResizePos.x2 = ResizePos.x1 + m.x + ResizeDx; ResizePos.y2 = ResizePos.y1 + m.y + ResizeDy; Sx = ResizePos.X(); Sy = ResizePos.Y(); if (Sx != X() || Sy != Y()) { dynamic_cast(GetParent())->PourAll(); Invalidate((LRect*)0, true); GetParent()->Invalidate((LRect*)0, true); PalCtrl->Invalidate((LRect*)0, true); } } } void LPaletteUI::DescribeColour(LSurface *pDC, ImgCol Col, LRect *Where) { char Str[256] = ""; if (Col.a == 0) { strcpy(Str, "Transparent"); } else { if (Col.Bits <= 8) { LPalette *Pal = PalCtrl->GetPalette(); GdcRGB *RGB = (Pal) ? (*Pal)[Col.c] : 0; sprintf(Str, "%i", Col.c & 0xFF); if (RGB) { sprintf(Str+strlen(Str), " (#%2.2X%2.2X%2.2X)", RGB->r, RGB->g, RGB->b); } } else { COLOUR c24 = CBit(24, Col.c, Col.Bits); sprintf(Str, "%i, %i, %i (#%2.2X%2.2X%2.2X)", R24(c24), G24(c24), B24(c24), R24(c24), G24(c24), B24(c24)); } } if (Font && Where) { Font->Colour(L_TEXT, L_MED); LDisplayString ds(Font, Str); ds.Draw(pDC, Where->x1, Where->y1, Where); } } void LPaletteUI::PaintColour(LSurface *pDC, LRect &r, ImgCol &c) { if (c.a < 0xff) { pDC->Colour(L_WHITE); pDC->Rectangle(&r); pDC->Colour(Rgb24(255, 0, 0), 24); pDC->Line(r.x1, r.y1, r.x2, r.y2); pDC->Line(r.x2, r.y1, r.x1, r.y2); if (c.a == 0) return; COLOUR col = CBit(24, c.c, c.Bits, GetPalette()); pDC->Colour(Rgba32(R24(col), G24(col), B24(col), c.a), 32); } else { pDC->Colour(CBit(pDC->GetBits(), c.c, c.Bits, GetPalette())); } pDC->Op(GDC_ALPHA); pDC->Rectangle(&r); } void LPaletteUI::OnPaint(LSurface *pDC) { LRect r = GetClient(); LRegion Grey(r); // draw background (but not over children areas) pDC->Colour(L_MED); Grey.Subtract(&ForePos); Grey.Subtract(&BackPos); Grey.Subtract(&PalPos); for (LRect *Rgn = Grey.First(); Rgn; Rgn = Grey.Next()) { pDC->Rectangle(Rgn); } // draw foreground & background colour LRect B = BackPos; LRect F = ForePos; LRect Both = B; Both.Union(&F); LMemDC Buf; if (Buf.Create(Both.X(), Both.Y(), System32BitColourSpace)) { Buf.Colour(L_MED); Buf.Rectangle(); B.Offset(-F.x1, -F.y1); F.Offset(-F.x1, -F.y1); LWideBorder(&Buf, B, EdgeWin7Sunken); LWideBorder(&Buf, F, EdgeWin7Sunken); PaintColour(&Buf, B, BackCol); PaintColour(&Buf, F, ForeCol); pDC->Blt(ForePos.x1, ForePos.y1, &Buf); } // Draw swap marks int Length = 2; pDC->Colour(L_TEXT); // bars pDC->Line(SwapPos.x1, SwapPos.y1 + Length, SwapPos.x2 - Length, SwapPos.y1 + Length); pDC->Line(SwapPos.x2 - Length, SwapPos.y1 + Length, SwapPos.x2 - Length, SwapPos.y2); // left-top arrow bits pDC->Line(SwapPos.x1, SwapPos.y1 + Length, SwapPos.x1 + Length, SwapPos.y1 + (Length<<1)); pDC->Line(SwapPos.x1, SwapPos.y1 + Length, SwapPos.x1 + Length, SwapPos.y1); // bottom-right arrow bits pDC->Line(SwapPos.x2 - Length, SwapPos.y2, SwapPos.x2 - (Length<<1), SwapPos.y2 - Length); pDC->Line(SwapPos.x2 - Length, SwapPos.y2, SwapPos.x2, SwapPos.y2 - Length); // draw palette control if (PalCtrl) { r = PalCtrl->GetPos(); pDC->SetOrigin(-r.x1, -r.y1); PalCtrl->OnPaint(pDC); pDC->SetOrigin(0, 0); } DescribeColour(pDC, ForeCol, &ForeTextPos); DescribeColour(pDC, BackCol, &BackTextPos); } LMessage::Result LPaletteUI::OnEvent(LMessage *Msg) { int Status = LLayout::OnEvent(Msg); switch (Msg->Msg()) { case COL_CTRL_SET: { ImgCol c; c.c = Msg->A(); c.Bits = Msg->B() ? Msg->B() : ForeCol.Bits; c.a = 0xff; if (UseFore) SetFore(c); else SetBack(c); break; } case COL_CTRL_GET: { if (UseFore) return ForeCol.c; else return BackCol.c; break; } case COL_CTRL_GET_BITS: { if (UseFore) return ForeCol.Bits; else return BackCol.Bits; break; } case COL_CTRL_SET_PAL: { LPalette *p = NULL; if (Msg->A()) { p = new LPalette((LPalette*) Msg->A()); } SetPalette(0); if (PalCtrl) PalCtrl->SetPalette(p, true); if (GetParent()) { LNotification note; GetParent()->OnNotify(this, note); } break; } case COL_CTRL_GET_PAL: { return (NativeInt) GetPalette(); break; } } return Status; } bool LPaletteUI::Pour(LRegion &r) { LRect *Best = FindLargest(r); if (!Best) return false; LRect Take = *Best; Take.y2 = 1100; int Em = GetFont()->GetHeight(); int ColorPx = (int) (Em * 1.3); ForePos.ZOff(ColorPx-1, ColorPx-1); BackPos.ZOff(ColorPx-1, ColorPx-1); BackPos.Offset(ColorPx/2, ColorPx/2); int GapPx = IsHighDpi() ? 4 : 2; SwapPos.x1 = ForePos.x2 + GapPx; SwapPos.y1 = ForePos.y1; SwapPos.x2 = BackPos.x2; SwapPos.y2 = BackPos.y1 - GapPx; ForeTextPos.ZOff(1000, LSysFont->GetHeight()); ForeTextPos.Offset(BackPos.x2 + 5, 0); BackTextPos.ZOff(1000, LSysFont->GetHeight()); BackTextPos.Offset(BackPos.x2 + 5, ForeTextPos.y2+1); PalPos.ZOff(Take.X()-1, Take.X()-1); PalPos.Offset(0, BackPos.y2 + 8); OpPos.ZOff(Take.X()-1, IsHighDpi() ? 300 : 240); OpPos.Offset(0, PalPos.y2 + LTableLayout::CellSpacing + 1); if (PalCtrl) { PalCtrl->SetPos(PalPos); } if (OpCtrl) { OpCtrl->SetPos(OpPos); LViewI *v; if (GetViewById(IDC_OP_TABLE, v)) { LRect r = OpCtrl->GetClient(); r.Inset(4, 4); r.y1 += LSysFont->GetHeight(); v->SetPos(r); } Take.y2 = Take.y1 + OpPos.y2 + 3; } else LAssert(0); SetPos(Take); return true; } int LPaletteUI::GetIndexFromPoint(int x, int y) { if (PalCtrl) { return PalCtrl->GetIndex(x - PalPos.x1, y - PalPos.y1); } return -1; } int LPaletteUI::WillAccept(LDragFormats &Formats, LPoint Pt, int KeyState) { int Effect = DROPEFFECT_NONE; if (PalCtrl->GetPos().Overlap(Pt.x, Pt.y)) { Formats.SupportsFileDrops(); Formats.Supports("Image::Colour"); if (Formats.GetSupported().Length()) Effect = DROPEFFECT_COPY; } return Effect; } int LPaletteUI::OnDrop(LArray &Data, LPoint Pt, int KeyState) { for (unsigned n=0; nValue.Binary.Data); GdcRGB *p = 0; LPalette *Pal = GetPalette(); if (!Pal) { Pal = new LPalette(0, 1); if (Pal) { PalCtrl->SetPalette(Pal, true); p = (*Pal)[0]; } } else { int i = GetIndexFromPoint(Pt.x, Pt.y); if (i >= 0) { p = (*Pal)[i]; } else { int s = Pal->GetSize(); if (s < 256) s++; Pal->SetSize(s); p = (*Pal)[Pal->GetSize()-1]; } } if (p) { p->r = R24(c); p->g = G24(c); p->b = B24(c); } if (PalCtrl) { PalCtrl->Update(); } return DROPEFFECT_COPY; } else if (dd.IsFileDrop()) { LDropFiles Df(dd); PalCtrl->OpenFile(Df[0]); } } return DROPEFFECT_NONE; } bool LPaletteUI::OnKey(LKey &k) { return false; }