diff --git a/src/ImageScript.cpp b/src/ImageScript.cpp --- a/src/ImageScript.cpp +++ b/src/ImageScript.cpp @@ -1,1235 +1,1235 @@ /// \file /// \author Matthew Allen #include "Image.h" #include "lgi/common/Path.h" #include "lgi/common/TextView3.h" #include "lgi/common/TabView.h" #include "lgi/common/Token.h" #include "lgi/common/Combo.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Menu.h" extern LHostFunc ImageCommands[]; #define IDC_RUN 100 #define IDC_TXT 101 #define IDC_OUT 102 char sApp[] = {'A', 'p', 'p', 0}; char sParent[] = {'P', 'a', 'r', 'e', 'n', 't', 0}; LSurface *CastSurface(LVariant *v) { if (!v || v->Type != GV_LSURFACE) return NULL; return v->Value.Surface.Ptr; } int CompToInt(LVariant *v) { if (v) { char *s = v->Str(); if (s) { if (s[0] == '0' && s[1] == 'x') return htoi(s + 2); else return atoi(s); } else { return v->CastInt32(); } } return -1; } ImageScriptContext::ImageScriptContext(ImgWnd *app, LViewI *parent) { Engine = 0; App = app; Parent = parent; Brush = 0; Fnt = 0; } ImageScriptContext::~ImageScriptContext() { DeleteObj(Brush); DeleteObj(Fnt); // for (LPath *p = Paths.First(); p; p = Paths.Next()) for (auto p : Paths) { DeleteObj(p.value); } // for (LSurface *s = Bitmaps.First(); s; s = Bitmaps.Next()) for (auto s : Bitmaps) { DeleteObj(s.value); } } LString ImageScriptContext::GetIncludeFile(const char *FileName) { return LString(); } LPath *ImageScriptContext::GetPath(char *name) { return (LPath*) Paths.Find(name); } void ImageScriptContext::SetEngine(LScriptEngine *Eng) { if ((Engine = Eng)) { LVariant v; LCompiledCode *code = Eng->GetCurrentCode(); if (code) { v = (LDom*)App; code->Set(sApp, v); if (Parent) { #if LGI_VIEW_HANDLE v = (NativeInt)Parent->Handle(); #else v = Parent; #endif code->Set(sParent, v); } } } } LHostFunc *ImageScriptContext::GetCommands() { return ImageCommands; } bool ImageScriptContext::Create(LScriptArguments &Args) { bool Status = false; if (Args.Length() == 3) { LAutoPtr Mem(new ImgMemDC); if (Mem) { int X = Args[0]->CastInt32(); int Y = Args[1]->CastInt32(); int Bits = Args[2]->CastInt32(); if (Mem->Create(X, Y, LBitsToColourSpace(Bits))) { switch (Bits) { case 32: { Mem->Colour(0, 32); Mem->Rectangle(); break; } default: { Mem->Colour(L_WHITE); Mem->Rectangle(); break; } } App->SetDC(Mem); Status = true; } } } return Status; } bool ImageScriptContext::GetDocument(LScriptArguments &Args) { LVariant *Ret = Args.GetReturn(); Ret->Empty(); if ((Ret->Value.Surface.Ptr = App->GetDoc())) { Ret->Value.Surface.Own = false; Ret->Type = GV_LSURFACE; } return true; } bool ImageScriptContext::SetDocument(LScriptArguments &Args) { if (Args.Length() != 1) return false; if (Args[0]->Type != GV_LSURFACE) return false; LSurface *s = (LSurface*) Args[0]->CastVoidPtr(); if (!s) return false; App->SetDoc(s); // Not sure I really need to do this... // Args[0]->Empty(); return true; } bool ImageScriptContext::Load(LScriptArguments &Args) { if (Args.Length() < 1) return false; char *File = Args[0]->CastString(); Args.GetReturn()->SetSurface(GdcD->Load(File), true); return true; } bool ImageScriptContext::Save(LScriptArguments &Args) { if (Args.Length() < 1) return Args.Throw(_FL, "Not enough arguments."); auto Vm = dynamic_cast(Args.GetVm()); if (!Vm) return false; auto Ctx = Vm->SaveContext(); Dom.Empty(); LString callback = Args.StringAt(0); if (!callback) return Args.Throw(_FL, "No callback specified."); LString filename = Args.StringAt(1); if (!filename) return Args.Throw(_FL, "No filename specified."); LString params = Args.StringAt(2); if (params) { for (auto assign: params.SplitDelimit(";")) { auto parts = assign.SplitDelimit("=", 1); if (parts.Length() == 2) { LVariant v; if (parts[1].IsNumeric()) { if (parts[1].Find(".")) v = parts[1].Float(); else v = parts[1].Int(); } else { v = parts[1]; } Dom.SetValue(parts[0], v); } } } App->SetUserDom(&Dom); App->SaveFile(filename, [this, callback, Ctx](auto fn, auto ok) { App->SetUserDom(NULL); LScriptArguments Args(NULL); Args.Add(new LVariant(ok)); Ctx.Call(callback, Args); Args.DeleteObjects(); }); return true; } int Clamp255(int i) { if (i < 0) return 0; if (i > 255) return 255; return i; } bool ImageScriptContext::Rgb(LScriptArguments &Args) { if (Args.Length() == 3) { int R = Clamp255(CompToInt(Args[0])); int G = Clamp255(CompToInt(Args[1])); int B = Clamp255(CompToInt(Args[2])); DeleteObj(Brush); Brush = new LSolidBrush(Rgb32(R, G, B)); return true; } return false; } bool ImageScriptContext::Rgba(LScriptArguments &Args) { if (Args.Length() == 4) { int R = Clamp255(CompToInt(Args[0])); int G = Clamp255(CompToInt(Args[1])); int B = Clamp255(CompToInt(Args[2])); int A = Clamp255(CompToInt(Args[3])); DeleteObj(Brush); Brush = new LSolidBrush(Rgba32(R, G, B, A)); return true; } return false; } bool ImageScriptContext::LinearGradient(LScriptArguments &Args) { if (Args.Length() == 4) { DeleteObj(Brush); LPointF a(Args[0]->CastDouble(), Args[1]->CastDouble()); LPointF b(Args[2]->CastDouble(), Args[3]->CastDouble()); Brush = new LLinearBlendBrush(a, b); return true; } return false; } bool ImageScriptContext::RadialGradient(LScriptArguments &Args) { if (Args.Length() == 3) { DeleteObj(Brush); LPointF a(Args[0]->CastDouble(), Args[1]->CastDouble()); LPointF b(a.x + Args[2]->CastDouble(), a.y); Brush = new LRadialBlendBrush(a, b); return true; } return false; } bool ImageScriptContext::AddStop(LScriptArguments &Args) { if (Args.Length() == 5 && Brush) { LBlendBrush *Blend = dynamic_cast(Brush); if (Blend) { LBlendStop *Existing = 0; int Stops = Blend->GetStops(&Existing); LArray s; s.Length(Stops + 1); if (Stops) memcpy(&s[0], Existing, sizeof(*Existing) * Stops); s[Stops].Pos = Args[0]->CastDouble(); int R = Clamp255(CompToInt(Args[1])); int G = Clamp255(CompToInt(Args[2])); int B = Clamp255(CompToInt(Args[3])); int A = Clamp255(CompToInt(Args[4])); s[Stops].c32 = Rgba32(R, G, B, A); Blend->SetStops(s.Length(), &s[0]); return true; } } return false; } bool ImageScriptContext::NewPath(LScriptArguments &Args) { if (Args.Length() == 1) { LPath *p; char *PathName = Args[0]->Str(); assert(PathName); if (PathName) { if ((p = (LPath*)Paths.Find(PathName))) { p->Empty(); } else if ((p = new LPath)) { Paths.Add(PathName, p); } return true; } } return false; } bool ImageScriptContext::Rect(LScriptArguments &Args) { if (Args.Length() == 5) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->Rectangle(Args[1]->CastDouble(), Args[2]->CastDouble(), Args[3]->CastDouble(), Args[4]->CastDouble()); return true; } } return false; } bool ImageScriptContext::RoundRect(LScriptArguments &Args) { if (Args.Length() == 6) { LPath *p = GetPath(Args[0]->Str()); if (p) { LRectF r(Args[1]->CastDouble(), Args[2]->CastDouble(), Args[3]->CastDouble(), Args[4]->CastDouble()); p->RoundRect(r, Args[5]->CastDouble()); return true; } } return false; } bool ImageScriptContext::Circle(LScriptArguments &Args) { if (Args.Length() == 4) { char *PathName = Args[0]->Str(); LPath *p = GetPath(PathName); if (p) { p->Circle(Args[1]->CastDouble(), Args[2]->CastDouble(), Args[3]->CastDouble()); return true; } } return false; } bool ImageScriptContext::MoveTo(LScriptArguments &Args) { if (Args.Length() == 3) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->MoveTo(Args[1]->CastDouble(), Args[2]->CastDouble()); return true; } } return false; } bool ImageScriptContext::LineTo(LScriptArguments &Args) { if (Args.Length() == 3) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->LineTo(Args[1]->CastDouble(), Args[2]->CastDouble()); return true; } } return false; } bool ImageScriptContext::QuadTo(LScriptArguments &Args) { if (Args.Length() == 5) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->QuadBezierTo(Args[1]->CastDouble(), Args[2]->CastDouble(), Args[3]->CastDouble(), Args[4]->CastDouble()); return true; } } return false; } bool ImageScriptContext::CubicTo(LScriptArguments &Args) { if (Args.Length() == 7) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->CubicBezierTo(Args[1]->CastDouble(), Args[2]->CastDouble(), Args[3]->CastDouble(), Args[4]->CastDouble(), Args[5]->CastDouble(), Args[6]->CastDouble()); return true; } } return false; } bool ImageScriptContext::FillPath(LScriptArguments &Args) { if (Args.Length() == 1 && Brush) { LPath *p = GetPath(Args[0]->Str()); if (p) { p->Fill(App->GetDoc(), *Brush); return true; } } return false; } bool ImageScriptContext::ErasePath(LScriptArguments &Args) { if (Args.Length() == 1 && Brush) { LPath *p = GetPath(Args[0]->Str()); if (p) { LEraseBrush Erase; p->Fill(App->GetDoc(), Erase); return true; } } return false; } bool ImageScriptContext::PathWidth(LScriptArguments &Args) { if (Args.Length() == 1) { LPath *p = GetPath(Args[0]->Str()); if (p) { LRectF b; p->GetBounds(&b); *Args.GetReturn() = b.X(); return true; } } return false; } bool ImageScriptContext::PathHeight(LScriptArguments &Args) { if (Args.Length() == 1) { LPath *p = GetPath(Args[0]->Str()); if (p) { LRectF b; p->GetBounds(&b); *Args.GetReturn() = b.Y(); return true; } } return false; } bool ImageScriptContext::MsgBox(LScriptArguments &Args) { if (Args.Length() == 1) { LgiMsg(Parent, "%s", AppName, MB_OK, Args[0]->Str()); return true; } return false; } bool ImageScriptContext::Font(LScriptArguments &Args) { bool Status = false; if (Args.Length() == 3) { DeleteObj(Fnt); Fnt = new LFont; if (Fnt) { char *Face = Args[0]->Str(); LCss::Len Size(Args[1]->Str()); char *Style = Args[2]->Str(); if (Face && Size.IsValid()) { if (Style) { LToken t(Style, ";, "); for (int i=0; iBold(true); else if (stricmp(t[i], "italic") == 0) Fnt->Italic(true); else if (stricmp(t[i], "underline") == 0) Fnt->Underline(true); } } if (Fnt->Create(Face, Size)) { Status = true; } } if (!Status) { DeleteObj(Fnt); } } } return Status; } bool ImageScriptContext::Text(LScriptArguments &Args) { bool Status = false; if (Args.Length() == 4 && Fnt) { LPath *p = GetPath(Args[0]->Str()); if (p) { double x = Args[1]->CastDouble(); double y = Args[2]->CastDouble(); Status = p->Text(Fnt, x, y, Args[3]->Str()); } } return Status; } bool ImageScriptContext::FontAscent(LScriptArguments &Args) { bool Status = false; if (Args.Length() == 0 && Fnt) { *Args.GetReturn() = Fnt->Ascent(); Status = true; } return Status; } bool ImageScriptContext::FontHeight(LScriptArguments &Args) { bool Status = false; if (Args.Length() == 0 && Fnt) { *Args.GetReturn() = Fnt->GetHeight(); Status = true; } return Status; } bool ImageScriptContext::Blur(LScriptArguments &Args) { if (Args.Length() != 1) return false; if (App->GetDoc()) { double r = Args[0]->CastDouble(); if (r < 0.001) return false; ImgMemDC *NewDC = new ImgMemDC; if (NewDC) { if (GaussianBlur(NewDC, App->GetDoc(), r, 0)) { int Old = App->Op(); App->Op(GDC_SET); App->GetDoc()->Blt(0, 0, NewDC); App->Op(Old); DeleteObj(NewDC); } else { DeleteObj(NewDC); return false; } } else return false; } return true; } bool ImageScriptContext::SetFillRule(LScriptArguments &Args) { if (Args.Length() == 2) { LPath *p = GetPath(Args[0]->Str()); if (p && Args[1]->Str()) { if (stricmp(Args[1]->Str(), "oddeven") == 0) { p->SetFillRule(FILLRULE_ODDEVEN); } else if (stricmp(Args[1]->Str(), "nonzero") == 0) { p->SetFillRule(FILLRULE_NONZERO); } else return false; return true; } } return false; } bool ImageScriptContext::SetDepth(LScriptArguments &Args) { if (Args.Length() < 1) return false; int Bits = Args[0]->CastInt32(); LSurface *pDC = App->GetDC(); if (pDC) { LReduceOptions Opts; *Args.GetReturn() = LReduceBitDepth(pDC, Bits, 0, &Opts); } else Args.GetReturn()->Empty(); return true; } bool ImageScriptContext::Resample(LScriptArguments &Args) { if (Args.Length() < 2) return false; int x = Args[0]->CastInt32(); int y = Args[1]->CastInt32(); *Args.GetReturn() = false; LSurface *pDC = App->GetDC(); if (pDC) { LAutoPtr pNew(new ImgMemDC); if (pNew && pNew->Create(x, y, pDC->GetColourSpace())) { ResampleDC(pNew, pDC, 0); App->SetDC(pNew); *Args.GetReturn() = true; } } return true; } bool ImageScriptContext::Crop(LScriptArguments &Args) { if (Args.Length() < 4) return false; LRect r; r.x1 = Args[0]->CastInt32(); r.y1 = Args[1]->CastInt32(); r.x2 = Args[2]->CastInt32(); r.y2 = Args[3]->CastInt32(); *Args.GetReturn() = false; LSurface *pDC = App->GetDC(); if (pDC) { r.Normal(); LAutoPtr pNew(new ImgMemDC); if (pNew && pNew->Create(r.X(), r.Y(), pDC->GetColourSpace())) { pNew->Blt(0, 0, pDC, &r); App->SetDC(pNew); *Args.GetReturn() = true; } } return true; } bool ImageScriptContext::Difference(LScriptArguments &Args) { if (Args.Length() != 2) return false; if (Args[0]->Type != GV_LSURFACE && Args[1]->Type != GV_LSURFACE) return false; LSurface *a = (LSurface*) Args[0]->CastVoidPtr(); LSurface *b = (LSurface*) Args[1]->CastVoidPtr(); if (!a || !b || a->GetBits() != b->GetBits()) { *Args.GetReturn() = false; return true; } int cx = MIN(a->X(), b->X()); int cy = MIN(a->X(), b->Y()); LSurface *c = new ImgMemDC(cx, cy, a->GetColourSpace()); if (!c) return false; for (int y=0; yGetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *ap = (System24BitPixel*) (*a)[y]; System24BitPixel *bp = (System24BitPixel*) (*b)[y]; System24BitPixel *cp = (System24BitPixel*) (*c)[y]; for (int x=0; xr = abs(ap->r - bp->r); cp->g = abs(ap->g - bp->g); cp->b = abs(ap->b - bp->b); ap++; bp++; cp++; } break; } case System32BitColourSpace: { System32BitPixel *ap = (System32BitPixel*) (*a)[y]; System32BitPixel *bp = (System32BitPixel*) (*b)[y]; System32BitPixel *cp = (System32BitPixel*) (*c)[y]; System32BitPixel *e = ap + cx; while (ap < e) { cp->r = abs(ap->r - bp->r); cp->g = abs(ap->g - bp->g); cp->b = abs(ap->b - bp->b); cp->a = 255; ap++; bp++; cp++; } break; } } } Args.GetReturn()->SetSurface(c, true); return true; } bool ImageScriptContext::Normalize(LScriptArguments &Args) { if (Args.Length() != 1) return false; if (Args[0]->Type != GV_LSURFACE) return false; LSurface *a = (LSurface*) Args[0]->CastVoidPtr(); if (!a) { *Args.GetReturn() = false; return true; } LSurface *c = new ImgMemDC(a->X(), a->Y(), a->GetColourSpace()); if (!c) return false; int Mr = 0, Mg = 0, Mb = 0; for (int y=0; yY(); y++) { switch (a->GetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *ap = (System24BitPixel*) (*a)[y]; for (int x=0; xX(); x++) { Mr = MAX(Mr, ap->r); Mg = MAX(Mg, ap->g); Mb = MAX(Mb, ap->b); ap++; } break; } case System32BitColourSpace: { System32BitPixel *ap = (System32BitPixel*) (*a)[y]; System32BitPixel *e = ap + a->X(); while (ap < e) { Mr = MAX(Mr, ap->r); Mg = MAX(Mg, ap->g); Mb = MAX(Mb, ap->b); ap++; } break; } } } for (int y=0; yY(); y++) { switch (a->GetColourSpace()) { default: LAssert(0); break; case System24BitColourSpace: { System24BitPixel *ap = (System24BitPixel*) (*a)[y]; System24BitPixel *cp = (System24BitPixel*) (*c)[y]; for (int x=0; xX(); x++) { cp->r = ap->r * 255 / Mr; cp->g = ap->g * 255 / Mg; cp->b = ap->b * 255 / Mb; ap++; cp++; } break; } case System32BitColourSpace: { System32BitPixel *ap = (System32BitPixel*) (*a)[y]; System32BitPixel *cp = (System32BitPixel*) (*c)[y]; System32BitPixel *e = ap + a->X(); while (ap < e) { cp->r = ap->r * 255 / Mr; cp->g = ap->g * 255 / Mg; cp->b = ap->b * 255 / Mb; cp->a = 255; ap++; cp++; } break; } } } Args.GetReturn()->SetSurface(c, true); return true; } bool ImageScriptContext::Blt(LScriptArguments &Args) { if (Args.Length() != 4) return false; LSurface *dest = CastSurface(Args[0]); int dx = Args[1]->CastInt32(); int dy = Args[2]->CastInt32(); LSurface *src = CastSurface(Args[3]); if (dest == 0 || src == NULL) return false; int Op = dest->Op(GDC_ALPHA); dest->Blt(dx, dy, src); dest->Op(Op); return true; } #define DefFn(Name) \ LHostFunc(#Name, 0, (ScriptCmd)&ImageScriptContext::Name) LHostFunc ImageCommands[] = { // Utils DefFn(MsgBox), DefFn(Difference), DefFn(Normalize), // Document DefFn(Create), DefFn(Load), DefFn(Save), DefFn(GetDocument), DefFn(SetDocument), // Drawing style DefFn(Rgb), DefFn(Rgba), DefFn(LinearGradient), DefFn(RadialGradient), DefFn(AddStop), DefFn(SetFillRule), // Text DefFn(Font), DefFn(FontHeight), DefFn(FontAscent), // Paths DefFn(NewPath), DefFn(FillPath), DefFn(ErasePath), DefFn(PathWidth), DefFn(PathHeight), // Drawing primitives DefFn(Rect), DefFn(RoundRect), DefFn(Circle), DefFn(MoveTo), DefFn(LineTo), DefFn(QuadTo), DefFn(CubicTo), DefFn(Text), DefFn(Blur), // Image operations DefFn(SetDepth), DefFn(Resample), DefFn(Crop), DefFn(Blt), // AddPrimitive: Add your primitive here to the list, e.g.: // {"myprim", " ", (ScriptCmd) &ImageScriptContext::MyPrim), // End of list marker, do not remove. LHostFunc(0, 0, 0) }; class ScriptWindow : public LWindow { ImgWnd *App; LTextView3 *Script, *Output; LTabView *Tab; char *ScratchFile; public: ScriptWindow(ImgWnd *app, const char *ScriptFile) { ScratchFile = 0; if (ScriptFile) ScratchFile = NewStr(ScriptFile); else { char p[MAX_PATH_LEN]; LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p)); LMakePath(p, sizeof(p), p, "Scratch.script"); ScratchFile = NewStr(p); } Tab = 0; Script = Output = 0; App = app; Name("i.Mage Scripting"); LRect r(0, 0, 1024, 768); SetPos(r); MoveToCenter(); if (Attach(0)) { Menu = new LMenu; if (Menu) { Menu->Attach(this); Menu->Load(this, "IDM_SCRIPTING"); } Tab = new LTabView(10, 0, 0, 100, 100, 0); if (Tab) { auto p = Tab->Append("Input"); if (p) { p->Append(Script = new LTextView3(IDC_TXT, 0, 25, 100, 100, 0)); if (Script) { - Script->SetWrapType(TEXTED_WRAP_NONE); + Script->SetWrapType(L_WRAP_NONE); Script->Sunken(true); Script->Focus(true); char *s = LReadTextFile(ScratchFile); if (s) { Script->Name(s); DeleteArray(s); } } } p = Tab->Append("Output"); if (p) { p->Append(Output = new LTextView3(IDC_OUT, 0, 25, 100, 100, 0)); if (Output) { Output->Sunken(true); Output->Focus(true); } } Tab->Attach(this); } PourAll(); Visible(true); } } ~ScriptWindow() { if (Script && ScratchFile) { auto Txt = Script->Name(); if (Txt) { LFile f; if (f.Open(ScratchFile, O_WRITE)) { f.SetSize(0); f.Write(Txt, strlen(Txt)); } } } DeleteArray(ScratchFile); } void OnPosChange() { LWindow::OnPosChange(); if (Tab) { LRect r = Tab->GetTabClient(); r.Offset(-r.x1, -r.y1); r.Inset(2, 2); if (Script) { Script->SetPos(r); } if (Output) { Output->SetPos(r); } } } LMessage::Result OnEvent(LMessage *m) { switch (m->Msg()) { case M_OUTPUT: { if (Tab) { Tab->Value(1); if (Output) { char *s = (char*)m->A(); Output->Name(s); DeleteArray(s); } } break; } } return LWindow::OnEvent(m); } int OnCommand(int Cmd, int Event, OsView Wnd) { switch (Cmd) { case IDM_SCRIPT_HELP: { App->OpenHelp("scripting.html"); break; } case IDM_CLOSE: { PostEvent(M_CLOSE); break; } case IDM_RUN: case IDM_DEBUG: { if (Script && ScratchFile) { auto Txt = Script->Name(); if (Txt) { LFile f; if (f.Open(ScratchFile, O_WRITE)) { f.SetSize(0); f.Write(Txt, strlen(Txt)); } } } if (Script) { App->RunScript(this, NewStr(Script->Name()), ScratchFile, Cmd==IDM_DEBUG); } break; } } return 0; } }; void CreateScriptWindow(class ImgWnd *App, const char *ScriptFile) { new ScriptWindow(App, ScriptFile); }