From 5b23418c4100b03ad3f90b51a3f4b916ff197815 Mon Sep 17 00:00:00 2001 From: Olly Date: Thu, 25 Jan 2024 15:29:23 +0000 Subject: [PATCH] Image alpha channel usage - remove "transparent color" --- Examples/form.simba | 12 + Examples/image_drawtext.simba | 23 + Source/Simba.lpi | 104 +- Source/Simba.res | Bin 337942 -> 338946 bytes Source/array/simba.array_point.pas | 6 +- .../codetools/simba.ide_codetools_parser.pas | 1 - Source/colormath/simba.colormath.pas | 4 +- .../colormath/simba.colormath_conversion.pas | 8 +- Source/finders/simba.finder_image.pas | 3 +- Source/forms/simba.openexampleform.pas | 1 + Source/image/boxfilled.inc | 10 - Source/image/clearpixelalpha.inc | 6 - Source/image/ellipseantialias.inc | 156 -- Source/image/lineantialias.inc | 65 - Source/image/setpixelantialias.inc | 26 - Source/image/simba.image.pas | 1830 +++++++++++------ Source/image/simba.image_gaussblur.pas | 39 +- Source/image/simba.image_lazbridge.pas | 243 ++- Source/image/simba.image_stringconv.pas | 156 ++ Source/image/simba.image_textdrawer.pas | 72 +- Source/image/simba.image_utils.pas | 46 +- Source/imagebox/simba.imagebox_image.pas | 54 +- Source/imagebox/simba.imagebox_new.pas | 21 +- Source/matchtemplate/simba.matchtemplate.pas | 31 +- .../imports/simba.import_debugimage.pas | 71 - .../imports/simba.import_externalimage.pas | 388 ++-- Source/script/imports/simba.import_image.pas | 347 ++-- .../imports/simba.import_matchtemplate.pas | 71 +- Source/script/imports/simba.import_target.pas | 2 +- Source/script/simba.script_pluginmethods.pas | 47 +- Source/shapebuilders/shapebuilder_boxedge.inc | 23 + .../shapebuilders/shapebuilder_boxfilled.inc | 12 + .../shapebuilder_circle.inc} | 6 +- .../shapebuilder_circlefilled.inc} | 6 +- Source/shapebuilders/shapebuilder_line.inc | 38 + Source/shapebuilders/shapebuilder_linegap.inc | 47 + .../shapebuilder_polygonfilled.inc} | 14 +- Source/simba.base.pas | 9 +- Source/simba.externalimage.pas | 726 ++++--- Source/simba.finder.pas | 2 +- Source/simba.fonthelpers.pas | 12 + Source/simba.geometry.pas | 49 +- Source/simba.ide_showdeclaration.pas | 21 +- Source/simba.scriptcommunication.pas | 59 +- Source/simba.target.pas | 3 +- Source/targets/simba.target_plugin.pas | 9 +- Tests/finder_bitmap.simba | 7 +- Tests/finder_color.simba | 2 +- Tests/finder_dtm.simba | 3 +- Tests/finder_dtmrotated.simba | 2 +- Tests/img_pixeldifference.simba | 26 + Tests/matchtemplatemask.simba | 5 +- Tests/matchtemplatemask2.simba | 2 +- Tests/tpa_minareacircle.simba | 2 +- Third-Party/fpqoi_simba.pas | 606 ------ Third-Party/syncompletion_simba.pas | 7 +- 56 files changed, 2679 insertions(+), 2862 deletions(-) create mode 100644 Examples/image_drawtext.simba delete mode 100644 Source/image/boxfilled.inc delete mode 100644 Source/image/clearpixelalpha.inc delete mode 100644 Source/image/ellipseantialias.inc delete mode 100644 Source/image/lineantialias.inc delete mode 100644 Source/image/setpixelantialias.inc create mode 100644 Source/image/simba.image_stringconv.pas create mode 100644 Source/shapebuilders/shapebuilder_boxedge.inc create mode 100644 Source/shapebuilders/shapebuilder_boxfilled.inc rename Source/{image/circle.inc => shapebuilders/shapebuilder_circle.inc} (75%) rename Source/{image/circlefilled.inc => shapebuilders/shapebuilder_circlefilled.inc} (55%) create mode 100644 Source/shapebuilders/shapebuilder_line.inc create mode 100644 Source/shapebuilders/shapebuilder_linegap.inc rename Source/{image/polygonfilled.inc => shapebuilders/shapebuilder_polygonfilled.inc} (91%) create mode 100644 Tests/img_pixeldifference.simba delete mode 100644 Third-Party/fpqoi_simba.pas diff --git a/Examples/form.simba b/Examples/form.simba index 9a2700623..d67f75b7e 100644 --- a/Examples/form.simba +++ b/Examples/form.simba @@ -38,6 +38,8 @@ var List: TLazListBox; Lbl: TLazLabel; Panel: TLazPanel; + Img: TLazImage; + SimbaImg: TImage; begin Form := TLazForm.Create(); Form.SetCaption('Example form'); @@ -49,6 +51,16 @@ begin Form.SetColor(Colors.DARK_GREY); Form.SetBorderStyle(bsSingle); // Do not allow resizing + SimbaImg := TImage.CreateFromTarget([0,0,400,400]); + + Img := TLazImage.Create(Form); + Img.SetParent(Form); + Img.SetBounds(400,200,200,200); + Img.SetStretch(True); + Img.GetPicture().SetBitmap(SimbaImg.ToLazBitmap()); + + SimbaImg.Free(); + Button := TLazButton.Create(Form); Button.SetParent(Form); Button.SetAutoSize(True); diff --git a/Examples/image_drawtext.simba b/Examples/image_drawtext.simba new file mode 100644 index 000000000..4b3c1e6a9 --- /dev/null +++ b/Examples/image_drawtext.simba @@ -0,0 +1,23 @@ +var + myImage: TImage; + myBox: TBox; +begin + myImage := TImage.Create(500,1000); + myImage.Fill(Colors.DARK_GRAY); + + myImage.SetFontAntialiasing(True); + myImage.SetFontSize(20); + + myImage.DrawTextLines(TImage.FontNames, [10,10], Colors.BLACK); + + myBox := [225,25,450,240]; + myImage.DrawBox(myBox, Colors.RED); + myImage.DrawText(FormatDateTime('c', Now()), myBox, [ETextDrawAlignment.CENTER, ETextDrawAlignment.VERTICAL_CENTER], Colors.BLACK); + + myImage.SetFontBold(True); + myImage.SetFontSize(50); + myImage.DrawText(IntToStr(Random(10000)), [225, 300], Colors.NAVY); + + myImage.Show(); + myImage.Free(); +end. diff --git a/Source/Simba.lpi b/Source/Simba.lpi index fbfaaa298..2212305e9 100644 --- a/Source/Simba.lpi +++ b/Source/Simba.lpi @@ -17,7 +17,7 @@ - + @@ -29,6 +29,7 @@ + @@ -47,7 +48,7 @@ - + @@ -97,7 +98,7 @@ - + @@ -147,7 +148,7 @@ - + @@ -195,7 +196,7 @@ - + @@ -242,7 +243,7 @@ - + @@ -295,7 +296,7 @@ - + @@ -377,7 +378,7 @@ - + @@ -817,130 +818,138 @@ - - - - + + + + - + - + - + - + - + - - - - + + + + - + + + + - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + @@ -949,7 +958,7 @@ - + @@ -969,11 +978,6 @@ - - - - - diff --git a/Source/Simba.res b/Source/Simba.res index cdc30e75f29fdb5f68cc923253d4de3418286cf7..38407db4b4e46ba7525b7d46e768c0f09d73cbdb 100644 GIT binary patch delta 987 zcmZ`%(TdYR6isXQrBFm!T_`@xi^~*a$kx`%%A!e{EM-fTG^|P~OLjYS1D(v0PT6f= z3a;n}1pE%65B`DF7tuFAzz@(b&^u|;bge=s(7ET{Ip^H-{_E!J@0%ay+u6H?@Jqfp z`?38gS45~m`@6_#x|0TV`{Z#Uk5JpA$8PxK*4Aux&&W)pzW_tKd&r4UiV+Z(6DwfU zaf3eLxTDIdTv4ObEx{BqC+SIs$8FeR0Tq}Cp|lBq?gqr?>-Rbv`Mv27i^1jeT;xl{7WQGNcfv;j!6os6?s4j#;Y5n!Fd!9QmJ+_psud9Ei&cb=lsB`<{kIz# zY4*GPIk$0{m87TPxBS+Px@1e5^g?nZQ+f$sDg2ecGmR_)Pg6tbSZ5b-178?=O!}TL z)YEYUS@RWFLFbGxBQ#aBAx0uh{ z-h|+CGK3Vx!f4ZOZjAux(zwj9%z z5&B2%(6pU~p*4>dp#M@|1R5eD)aq=|TS*cfFP141v^1~!*IJf*8$Nd!#1Y%B-(wVu z4XULQP#3nK2kMG{E$wi9b*Jn+O5HLUigi6nBJq1lAunCdYwzZ@44f>SYai#eT 1) then begin for I := 0 to High(Self) - 1 do + begin Buffer.Add(TPointArray.CreateFromLine(Self[I], Self[I+1])); + Buffer.Pop(); // dont duplicate self[I+1] + end; Buffer.Add(TPointArray.CreateFromLine(Self[High(Self)], Self[0])); + Buffer.Pop(); // dont duplicate self[0] end; Result := Buffer.ToArray(False); @@ -869,7 +873,7 @@ function TPointArrayHelper.ShapeFill: TPointArray; for Row in Rows() do for I := 1 to High(Row) do if TSimbaGeometry.PointInPolygon((Row[I-1] + Row[I]) div 2, Self) then - HorzLine(Row[0].Y, Row[I-1].X, Row[I].X); + HorzLine(Row[0].Y, Row[I-1].X + 1, Row[I].X - 1); Result := Buffer.ToArray(False); end; diff --git a/Source/codetools/simba.ide_codetools_parser.pas b/Source/codetools/simba.ide_codetools_parser.pas index 2d0252325..84d6a4548 100644 --- a/Source/codetools/simba.ide_codetools_parser.pas +++ b/Source/codetools/simba.ide_codetools_parser.pas @@ -467,7 +467,6 @@ TCodeParser = class(TmwSimplePasPar) constructor Create; override; destructor Destroy; override; end; - TPluginParser = class(TCodeParser); TCodeParserArray = array of TCodeParser; TCodeParserList = specialize TSimbaObjectList; diff --git a/Source/colormath/simba.colormath.pas b/Source/colormath/simba.colormath.pas index 6e39cd5e1..9dd4ec3c3 100644 --- a/Source/colormath/simba.colormath.pas +++ b/Source/colormath/simba.colormath.pas @@ -27,7 +27,7 @@ interface {$POP} PChannelMultipliers = ^TChannelMultipliers; - TChannelMultipliers = array [0..2] of Single; + TChannelMultipliers = array[0..2] of Single; const DefaultMultipliers: TChannelMultipliers = (1, 1, 1); @@ -38,6 +38,8 @@ interface function AsString: String; end; + // note: TColor stuff should not include alpha + TColor = Graphics.TColor; PColor = ^TColor; diff --git a/Source/colormath/simba.colormath_conversion.pas b/Source/colormath/simba.colormath_conversion.pas index dfcb1470b..96b1880e5 100644 --- a/Source/colormath/simba.colormath_conversion.pas +++ b/Source/colormath/simba.colormath_conversion.pas @@ -3,9 +3,9 @@ Project: Simba (https://github.com/MerlijnWajer/Simba) License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) - Color space converting. + Color space converting, includes lots of code from: https://github.com/slackydev/colorlib - Lots of code from: https://github.com/slackydev/colorlib + note: TColor stuff should not include alpha } unit simba.colormath_conversion; @@ -63,12 +63,12 @@ class function TSimbaColorConversion.ColorToBGRA(const Color: TColor): TColorBGR Result.B := Color shr B_BIT and $FF; Result.G := Color shr G_BIT and $FF; Result.R := Color shr R_BIT and $FF; - Result.A := Color shr A_BIT and $FF; + Result.A := 0; end; class function TSimbaColorConversion.BGRAToColor(const BGRA: TColorBGRA): TColor; begin - Result := TColor(BGRA.R or BGRA.G shl G_BIT or BGRA.B shl B_BIT or BGRA.A shl A_BIT); + Result := TColor(BGRA.R or BGRA.G shl G_BIT or BGRA.B shl B_BIT); end; class function TSimbaColorConversion.ColorToRGB(const Color: TColor): TColorRGB; diff --git a/Source/finders/simba.finder_image.pas b/Source/finders/simba.finder_image.pas index a619e5e2d..752da6b7b 100644 --- a/Source/finders/simba.finder_image.pas +++ b/Source/finders/simba.finder_image.pas @@ -75,7 +75,8 @@ function ConvertBitmapColors(Image: TSimbaImage; ColorSpace: EColorSpace): PByte for I := 0 to (Image.Width * Image.Height) - 1 do begin - PBoolean(Dest)^ := (Image.TransparentColorActive and Source^.EqualsIgnoreAlpha(Image.TransparentRGB)); + PBoolean(Dest)^ := Source^.A = 0; + //PBoolean(Dest)^ := (Image.TransparentColorActive and Source^.EqualsIgnoreAlpha(Image.TransparentRGB)); if not PBoolean(Dest)^ then begin DestFix := Dest + 1; // temp fix, https://gitlab.com/freepascal.org/fpc/source/-/commit/851af5033fb80d4e19c4a7b5c44d50a36f456374 diff --git a/Source/forms/simba.openexampleform.pas b/Source/forms/simba.openexampleform.pas index b6e3b3aa5..b0517b216 100644 --- a/Source/forms/simba.openexampleform.pas +++ b/Source/forms/simba.openexampleform.pas @@ -207,6 +207,7 @@ procedure TSimbaOpenExampleForm.FormCreate(Sender: TObject); AddNode('JSON', 'EXAMPLE_JSON' ); AddNode('Form', 'EXAMPLE_FORM' ); AddNode('IRC', 'EXAMPLE_IRC' ); + AddNode('Draw Text', 'EXAMPLE_DRAWTEXT' ); end; procedure TSimbaOpenExampleForm.DoTreeViewSelectionChanged(Sender: TObject); diff --git a/Source/image/boxfilled.inc b/Source/image/boxfilled.inc deleted file mode 100644 index d534c2164..000000000 --- a/Source/image/boxfilled.inc +++ /dev/null @@ -1,10 +0,0 @@ -// Requires method `procedure _Row(const Y: Integer; X1, X2: Integer);` - -procedure _BoxFilled(Box: TBox); -var - Y: Integer; -begin - for Y := Box.Y1 to Box.Y2 do - _Row(Y, Box.X1, Box.X2); -end; - diff --git a/Source/image/clearpixelalpha.inc b/Source/image/clearpixelalpha.inc deleted file mode 100644 index 915e40171..000000000 --- a/Source/image/clearpixelalpha.inc +++ /dev/null @@ -1,6 +0,0 @@ -procedure _SetPixelAntialias(const X, Y: Integer; const Alpha: Byte); inline; -begin - if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then - FData[Y*FWidth+X].A := 0; -end; - diff --git a/Source/image/ellipseantialias.inc b/Source/image/ellipseantialias.inc deleted file mode 100644 index a113a93ba..000000000 --- a/Source/image/ellipseantialias.inc +++ /dev/null @@ -1,156 +0,0 @@ -// https://zingl.github.io/bresenham.js -// Requires _SetPixelAntialias(const X, Y: Integer; const Alpha: Byte); - -procedure _EllipseAntialias(x0, y0, x1, y1: Integer; Thickness: Single); -var - a,b,b1: Integer; - a2,b2: Single; - dx,dy: Single; - err: Single; - dx2,dy2,e2,ed: Single; - i: Single; - Alpha: Byte; -begin - a := Abs(x1 - x0); - b := Abs(y1 - y0); - b1 := b and 1; - a2 := a-2*Thickness; - b2 := b-2*Thickness; - dx := 4*(a-1)*b*b; - dy := 4*(b1-1)*a*a; - - i := a+b2; - err := b1*a*a; - - if ((Thickness-1) * (2*b-Thickness) > a*a) then - b2 := Sqrt(a*(b-a)*i*a2) / (a-Thickness); - - if ((Thickness-1) * (2*a-Thickness) > b*b) then - begin - a2 := Sqrt(b*(a-b)*i*b2) / (b-Thickness); - Thickness := (a-a2) / 2; - end; - - if (a = 0) or (b = 0) then - Exit; - - if (x0 > x1) then - begin - x0 := x1; - x1 += a; - end; - - if (y0 > y1) then - y0 := y1; - - if (b2 <= 0) then - Thickness := a; - - e2 := Thickness - Floor(Thickness); - Thickness := x0+Thickness-e2; - dx2 := 4*(a2+2*e2-1)*b2*b2; - dy2 := 4*(b1-1)*a2*a2; - e2 := dx2*e2; - y0 += (b+1) shr 1; - y1 := y0-b1; - a := 8*a*a; - b1 := 8*b*b; - a2 := 8*a2*a2; - b2 := 8*b2*b2; - - repeat - while True do - begin - if (err < 0) or (x0 > x1) then - begin - i := x0; - Break; - end; - - i := Min(dx,dy); - ed := Max(dx,dy); - - if ((y0 = y1+1) and (2*err > dx) and (a > b1)) then - ed := a/4 - else - ed += 2*ed*i*i/(4*ed*ed+i*i+1)+1; - i := 255*err/ed; - - Alpha := Byte(Round(i)); - _SetPixelAntialias(x0,y0, Alpha); - _SetPixelAntialias(x0,y1, Alpha); - _SetPixelAntialias(x1,y0, Alpha); - _SetPixelAntialias(x1,y1, Alpha); - - if (err+dy+a < dx) then - begin - i := x0+1; - Break; - end; - - x0 += 1; - x1 -= 1; - err -= dx; - dx -= b1; - end; - - while (i < Thickness) and (2*i <= x0+x1) do - begin - _SetPixelAntialias(Round(i), y0, 0); - _SetPixelAntialias(Round(x0+x1-i), y0, 0); - _SetPixelAntialias(Round(i), y1, 0); - _SetPixelAntialias(Round(x0+x1-i), y1, 0); - - i += 1.0; - end; - - while ((e2 > 0) and (x0+x1 >= 2*Thickness)) do - begin - i := Min(dx2,dy2); - ed := Max(dx2,dy2); - - if (y0 = y1+1) and (2*e2 > dx2) and (a2 > b2) then - ed := a2/4 - else - ed += 2*ed*i*i/(4*ed*ed+i*i); - - Alpha := Byte(Round(255-255*e2/ed)); - _SetPixelAntialias(Round(Thickness), y0, Alpha); - _SetPixelAntialias(Round(x0+x1-Thickness), y0, Alpha); - _SetPixelAntialias(Round(Thickness), y1, Alpha); - _SetPixelAntialias(Round(x0+x1-Thickness), y1, Alpha); - - if (e2+dy2+a2 < dx2) then - Break; - - Thickness += 1; - e2 -= dx2; - dx2 -= b2; - end; - - dy2 += a2; - e2 += dy2; - y0 += 1; - y1 -= 1; - dy += a; - err += dy; - until (x0 >= x1); - - while (y0-y1 <= b) do - begin - Alpha := Byte(Round(255*4*err/b1)); - - _SetPixelAntialias(x0, y0, Alpha); - _SetPixelAntialias(x1, y0, Alpha); - - y0 += 1; - - _SetPixelAntialias(x0, y1, Alpha); - _SetPixelAntialias(x1, y1, Alpha); - - y1 -= 1; - dy += a; - err += dy; - end; -end; - diff --git a/Source/image/lineantialias.inc b/Source/image/lineantialias.inc deleted file mode 100644 index eeadfb508..000000000 --- a/Source/image/lineantialias.inc +++ /dev/null @@ -1,65 +0,0 @@ -// https://zingl.github.io/bresenham.js -// Requires _SetPixelAntialias(const X, Y: Integer; const Alpha: Byte); - -procedure _LineAntialias(x0, y0, x1, y1: Integer; Thickness: Single); -var - dx, dy, err: Integer; - e2, x2, y2: Integer; - ed: Single; - sx, sy: Integer; -begin - dx := Abs(x1 - x0); - dy := Abs(y1 - y0); - - if (x0 < x1) then sx := 1 else sx := -1; - if (y0 < y1) then sy := 1 else sy := -1; - - err := dx-dy; - if (dx+dy = 0) then - ed := 1 - else - ed := Sqrt(Double(dx*dx) + Double(dy*dy)); - - Thickness := (Thickness + 1) / 2; - while True do - begin - _SetPixelAntialias(x0, y0, Round(Max(0, 255 * (Abs(err-dx+dy)/ed-Thickness+1)))); - - e2 := err; - x2 := x0; - if (2*e2 >= -dx) then - begin - e2 += dy; - y2 := y0; - while (e2 < ed*Thickness) and ((y1 <> y2) or (dx > dy)) do - begin - y2 += sy; - _SetPixelAntialias(x0, y2, Round(Max(0, 255 * (Abs(e2)/ed-Thickness+1)))); - e2 += dx; - end; - if (x0 = x1) then - Break; - - e2 := err; - err -= dy; - x0 += sx; - end; - - if (2*e2 <= dy) then - begin - e2 := dx-e2; - while (e2 < ed*Thickness) and ((x1 <> x2) or (dx < dy)) do - begin - x2 += sx; - _SetPixelAntialias(x2, y0, Round(Max(0, 255 * (Abs(e2)/ed-Thickness+1)))); - e2 += dy; - end; - if (y0 = y1) then - Break; - - err += dx; - y0 += sy; - end; - end; -end; - diff --git a/Source/image/setpixelantialias.inc b/Source/image/setpixelantialias.inc deleted file mode 100644 index e3d147244..000000000 --- a/Source/image/setpixelantialias.inc +++ /dev/null @@ -1,26 +0,0 @@ -procedure _SetPixelAntialias(const X, Y: Integer; const Alpha: Byte); inline; -var - Pixel: PColorBGRA; - APlus1, APlus1Inv: UInt32; -begin - if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then - begin - Pixel := @FData[Y*FWidth+X]; - - if (Alpha > 0) then - begin - if (Pixel^.A > 0) then // dont blend again - Exit; - - APlus1 := Alpha + 1; - APlus1Inv := 255 - Alpha + 1; - - Pixel^.R := (BGRA.R * APlus1Inv) shr 8 + Pixel^.R * APlus1 shr 8; - Pixel^.G := (BGRA.G * APlus1Inv) shr 8 + Pixel^.G * APlus1 shr 8; - Pixel^.B := (BGRA.B * APlus1Inv) shr 8 + Pixel^.B * APlus1 shr 8; - Pixel^.A := Alpha; - end else - Pixel^ := BGRA; - end; -end; - diff --git a/Source/image/simba.image.pas b/Source/image/simba.image.pas index d2e88362b..3eccb11b3 100644 --- a/Source/image/simba.image.pas +++ b/Source/image/simba.image.pas @@ -12,9 +12,8 @@ interface uses Classes, SysUtils, Graphics, - simba.baseclass, simba.base, simba.image_textdrawer, - simba.colormath, simba.colormath_distance, simba.matchtemplate, - simba.circle, simba.quad; + simba.baseclass, simba.base, simba.image_textdrawer, simba.image_utils, + simba.colormath, simba.colormath_distance; type {$PUSH} @@ -23,47 +22,59 @@ interface ESimbaImageThreshMethod = (MEAN, MIN_MAX); {$POP} - TSimbaImageRowPtrs = array of PColorBGRA; + TSimbaImageLineStarts = array of PColorBGRA; PSimbaImage = ^TSimbaImage; TSimbaImage = class(TSimbaBaseClass) protected FWidth: Integer; FHeight: Integer; - - FTransparentColorActive: Boolean; - FTransparentRGB: TColorBGRA; - FTransparentColor: TColor; + FCenter: TPoint; FData: PColorBGRA; FDataOwner: Boolean; + FDataSize: SizeUInt; + + FLineStarts: TSimbaImageLineStarts; FTextDrawer: TSimbaTextDrawer; - procedure _DrawTPA(const TPA: TPointArray; Color: TColor); - procedure _DrawTPAAlpha(const TPA: TPointArray; Color: TColor; Alpha: Byte); + procedure RaiseOutOfImageException(X, Y: Integer); + procedure NotifyUnfreed; override; + + procedure _DrawTPA(TPA: TPointArray; Color: TColor); + procedure _DrawTPAAlpha(TPA: TPointArray; Color: TColor; Alpha: Byte); + + procedure _DrawLine(Start, Stop: TPoint; Color: TColor); + procedure _DrawLineAlpha(Start, Stop: TPoint; Color: TColor; Alpha: Byte); + + procedure _DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor); + procedure _DrawLineGapAlpha(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte); procedure _DrawBoxFilled(Box: TBox; Color: TColor); procedure _DrawBoxFilledAlpha(Box: TBox; Color: TColor; Alpha: Byte); - procedure _DrawCircleFilledAlpha(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); + procedure _DrawBoxEdge(Box: TBox; Color: TColor); + procedure _DrawBoxEdgeAlpha(Box: TBox; Color: TColor; Alpha: Byte); + procedure _DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor); + procedure _DrawCircleFilledAlpha(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); - procedure _DrawPolygonFilledAlpha(Points: TPointArray; Color: TColor; Alpha: Byte); procedure _DrawPolygonFilled(Points: TPointArray; Color: TColor); + procedure _DrawPolygonFilledAlpha(Points: TPointArray; Color: TColor; Alpha: Byte); - procedure NotifyUnfreed; override; + procedure _DrawImage(Image: TSimbaImage; P: TPoint); + procedure _DrawImageAlpha(Image: TSimbaImage; P: TPoint; Alpha: Byte); - function GetPixel(X, Y: Integer): TColor; - function GetCenter: TPoint; + function GetPixel(const X, Y: Integer): TColor; function GetFontAntialiasing: Boolean; function GetFontName: String; function GetFontSize: Single; function GetFontBold: Boolean; function GetFontItalic: Boolean; + function GetLineStart(const Y: Integer): PColorBGRA; procedure SetPixel(X, Y: Integer; Color: TColor); - procedure SetTransparentColor(Value: TColor); procedure SetFontAntialiasing(Value: Boolean); procedure SetFontName(Value: String); procedure SetFontSize(Value: Single); @@ -72,8 +83,10 @@ TSimbaImage = class(TSimbaBaseClass) public class var SaveUnfreedImages: ShortString; class function LoadFonts(Dir: String): Boolean; - class function LoadedFontNames: TStringArray; + class function FontNames: TStringArray; public + DefaultPixel: TColorBGRA; + constructor Create; overload; constructor Create(AWidth, AHeight: Integer); overload; constructor CreateFromFile(FileName: String); @@ -84,26 +97,27 @@ TSimbaImage = class(TSimbaBaseClass) destructor Destroy; override; - property Data: PColorBGRA read FData write FData; + property Data: PColorBGRA read FData; property DataOwner: Boolean read FDataOwner write FDataOwner; + property DataSize: SizeUInt read FDataSize; + + property LineStarts: TSimbaImageLineStarts read FLineStarts; + property LineStart[Line: Integer]: PColorBGRA read GetLineStart; + property Width: Integer read FWidth; property Height: Integer read FHeight; - property Center: TPoint read GetCenter; - - property TransparentColorActive: Boolean read FTransparentColorActive write FTransparentColorActive; - property TransparentColor: TColor read FTransparentColor write SetTransparentColor; - property TransparentRGB: TColorBGRA read FTransparentRGB; + property Center: TPoint read FCenter; property Pixel[X, Y: Integer]: TColor read GetPixel write SetPixel; default; + property TextDrawer: TSimbaTextDrawer read FTextDrawer; property FontName: String read GetFontName write SetFontName; property FontSize: Single read GetFontSize write SetFontSize; property FontAntialiasing: Boolean read GetFontAntialiasing write SetFontAntialiasing; property FontBold: Boolean read GetFontBold write SetFontBold; property FontItalic: Boolean read GetFontItalic write SetFontItalic; - function InImage(const X, Y: Integer): Boolean; overload; - procedure AssertInImage(const Method: String; const X, Y: Integer); + function InImage(const X, Y: Integer): Boolean; function Equals(Other: TObject): Boolean; override; function Equals(Other: TSimbaImage): Boolean; overload; @@ -113,11 +127,15 @@ TSimbaImage = class(TSimbaBaseClass) function TextSize(Text: String): TPoint; procedure DrawText(Text: String; Position: TPoint; Color: TColor); overload; - procedure DrawText(Text: String; Box: TBox; ACenter: Boolean; Color: TColor); overload; + procedure DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); overload; procedure DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor); procedure SetSize(NewWidth, NewHeight: Integer); + procedure SetAlpha(Value: Byte); overload; + procedure SetAlpha(Points: TPointArray; Value: Byte); overload; + procedure SetAlpha(Color: TColor; Value: Byte); overload; + function ResizeNN(NewWidth, NewHeight: Integer): TSimbaImage; function ResizeBilinear(NewWidth, NewHeight: Integer): TSimbaImage; @@ -130,49 +148,49 @@ TSimbaImage = class(TSimbaBaseClass) procedure SetExternalData(AData: PColorBGRA; AWidth, AHeight: Integer); procedure ResetExternalData; - procedure DrawATPA(const ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0); - procedure DrawTPA(const TPA: TPointArray; Color: TColor; Alpha: Byte = 0); + // Image + procedure DrawImage(Image: TSimbaImage; Location: TPoint; Alpha: Byte = 0); + + // Point + procedure DrawATPA(ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0); + procedure DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte = 0); // Line - procedure DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); - procedure DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); - procedure DrawLine(Start, Stop: TPoint; Color: TColor); + procedure DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte = 0); + procedure DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte = 0); // Box - procedure DrawBox(B: TBox; Color: TColor); + procedure DrawBox(Box: TBox; Color: TColor; Alpha: Byte = 0); procedure DrawBoxFilled(Box: TBox; Color: TColor; Alpha: Byte = 0); - procedure DrawBoxInverted(B: TBox; Color: TColor); + procedure DrawBoxInverted(B: TBox; Color: TColor; Alpha: Byte = 0); // Poly - procedure DrawPolygon(Points: TPointArray; Color: TColor); + procedure DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte = 0); procedure DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte = 0); - procedure DrawPolygonInverted(Points: TPointArray; Color: TColor); + procedure DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte = 0); // Quad - procedure DrawQuad(Quad: TQuad; Color: TColor); + procedure DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte = 0); procedure DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte = 0); - procedure DrawQuadInverted(Quad: TQuad; Color: TColor); + procedure DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte = 0); // Circle - procedure DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor); - procedure DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor); + procedure DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); procedure DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); - // TCircle overloads - procedure DrawCircle(Circle: TCircle; Color: TColor); overload; - procedure DrawCircleFilled(Circle: TCircle; Color: TColor; Alpha: Byte = 0); overload; - procedure DrawCircleInverted(Circle: TCircle; Color: TColor); overload; - // Antialiased procedure DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single = 1.5); procedure DrawEllipseAA(ACenter: TPoint; XRadius, YRadius: Integer; Color: TColor; Thickness: Single = 1.5); procedure DrawCircleAA(ACenter: TPoint; Radius: Integer; Color: TColor; Thickness: Single = 1.5); + // Arrays procedure DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor = -1); procedure DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor = -1); procedure DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor = -1); - procedure DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1); overload; - procedure DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor = -1); overload; + procedure DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1); procedure DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor = -1); procedure DrawHSLCircle(ACenter: TPoint; Radius: Integer); @@ -180,15 +198,13 @@ TSimbaImage = class(TSimbaBaseClass) procedure DrawMatrix(Matrix: TIntegerMatrix); overload; procedure DrawMatrix(Matrix: TSingleMatrix; ColorMapID: Integer = 0); overload; - procedure Fill(Color: TColor); - - procedure Clear; - procedure Clear(Area: TBox); - procedure ClearInverted(Area: TBox); + procedure Fill(Color: TColor; Alpha: Byte = 0); - procedure Draw(Image: TSimbaImage; X, Y: Integer); overload; - procedure Draw(Image: TSimbaImage; Position: TPoint); overload; + procedure Clear; overload; + procedure Clear(Box: TBox); overload; + procedure ClearInverted(Box: TBox); + procedure SplitChannels(out B,G,R,A: TByteArray); function GetColors: TColorArray; procedure ReplaceColor(OldColor, NewColor: TColor); procedure ReplaceColors(OldColors, NewColors: TColorArray); @@ -211,27 +227,23 @@ TSimbaImage = class(TSimbaBaseClass) procedure Pad(Amount: Integer); function ToLazBitmap: TBitmap; - procedure LoadFromLazBitmap(LazBitmap: TBitmap); + procedure FromLazBitmap(LazBitmap: TBitmap); function ToGreyMatrix: TByteMatrix; - function ToMatrixBGR: TIntegerMatrix; function ToMatrix: TIntegerMatrix; overload; function ToMatrix(X1, Y1, X2, Y2: Integer): TIntegerMatrix; overload; function ThresholdAdaptive(Alpha, Beta: Byte; AInvert: Boolean; Method: ESimbaImageThreshMethod; K: Integer): TSimbaImage; function ThresholdSauvola(Radius: Integer; AInvert: Boolean = False; R: Single = 128; K: Single = 0.5): TSimbaImage; - function RowPtrs: TSimbaImageRowPtrs; - - function SaveToFile(FileName: String; OverwriteIfExists: Boolean = False): Boolean; + procedure Load(FileName: String); overload; + procedure Load(FileName: String; Area: TBox); overload; + function Save(FileName: String; OverwriteIfExists: Boolean = False): Boolean; function SaveToString: String; - procedure LoadFromStream(Stream: TStream; FileName: String); - procedure LoadFromFile(FileName: String); overload; - procedure LoadFromFile(FileName: String; Area: TBox); overload; - procedure LoadFromString(Str: String); - procedure LoadFromData(AWidth, AHeight: Integer; AData: PColorBGRA; ADataWidth: Integer); - procedure LoadFromImage(Image: TSimbaImage); + procedure FromStream(Stream: TStream; FileName: String); + procedure FromString(Str: String); + procedure FromData(AWidth, AHeight: Integer; AData: PColorBGRA; ADataWidth: Integer); function Compare(Other: TSimbaImage): Single; @@ -239,9 +251,6 @@ TSimbaImage = class(TSimbaBaseClass) function PixelDifference(Other: TSimbaImage; Tolerance: Single): Integer; overload; function PixelDifferenceTPA(Other: TSimbaImage): TPointArray; overload; function PixelDifferenceTPA(Other: TSimbaImage; Tolerance: Single): TPointArray; overload; - - function MatchTemplate(Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; - function MatchTemplateMask(Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; end; TSimbaImageArray = array of TSimbaImage; @@ -249,15 +258,13 @@ TSimbaImage = class(TSimbaBaseClass) implementation uses - Math, FPImage, fpqoi_simba, - simba.matrix_float, simba.matrix_int, simba.array_point, - simba.arraybuffer, simba.geometry, simba.algo_sort, - simba.encoding, simba.compress, - simba.nativeinterface, + Math, FPImage, + simba.files, simba.box, simba.quad, simba.geometry, simba.nativeinterface, + simba.matrix_float, simba.matrix_int, simba.array_point, simba.arraybuffer, simba.algo_sort, simba.image_lazbridge, simba.image_integral, simba.image_gaussblur, - simba.image_bitmaparealoader, simba.image_utils, simba.files, simba.box; + simba.image_bitmaparealoader, simba.image_stringconv; -function TSimbaImage.SaveToFile(FileName: String; OverwriteIfExists: Boolean): Boolean; +function TSimbaImage.Save(FileName: String; OverwriteIfExists: Boolean): Boolean; var Stream: TFileStream; WriterClass: TFPCustomImageWriterClass; @@ -265,11 +272,11 @@ function TSimbaImage.SaveToFile(FileName: String; OverwriteIfExists: Boolean): B Result := False; if FileExists(FileName) and (not OverwriteIfExists) then - SimbaException('TSimbaImage.SaveToFile: File already exists "%s"', [FileName]); + SimbaException('TSimbaImage.Save: File already exists "%s"', [FileName]); WriterClass := TFPCustomImage.FindWriterFromFileName(FileName); if (WriterClass = nil) then - SimbaException('TSimbaImage.SaveToFile: Unknown image format "%s"', [FileName]); + SimbaException('TSimbaImage.Save: Unknown image format "%s"', [FileName]); Stream := nil; try @@ -282,12 +289,11 @@ function TSimbaImage.SaveToFile(FileName: String; OverwriteIfExists: Boolean): B Result := True; finally - if (Stream <> nil) then - Stream.Free(); + Stream.Free(); end; end; -procedure TSimbaImage.LoadFromFile(FileName: String); +procedure TSimbaImage.Load(FileName: String); var ReaderClass: TFPCustomImageReaderClass; Stream: TFileStream; @@ -307,7 +313,7 @@ procedure TSimbaImage.LoadFromFile(FileName: String); end; end; -procedure TSimbaImage.LoadFromFile(FileName: String; Area: TBox); +procedure TSimbaImage.Load(FileName: String; Area: TBox); begin if (not FileExists(FileName)) then SimbaException('TSimbaImage.LoadFromFile: File not found "%s"', [FileName]); @@ -316,7 +322,7 @@ procedure TSimbaImage.LoadFromFile(FileName: String; Area: TBox); SimbaImage_LoadBitmapArea(Self, FileName, Area) else begin - LoadFromFile(FileName); + Load(FileName); Area := Area.Clip(TBox.Create(0, 0, FWidth - 1, FHeight - 1)); if (Area.Width > 1) and (Area.Height > 1) then @@ -336,8 +342,8 @@ function TSimbaImage.Copy(X1, Y1, X2, Y2: Integer): TSimbaImage; var Y: Integer; begin - AssertInImage('Copy', X1, Y1); - AssertInImage('Copy', X2, Y2); + if (not InImage(X1, Y1)) then RaiseOutOfImageException(X1, Y1); + if (not InImage(X2, Y2)) then RaiseOutOfImageException(X2, Y2); Result := TSimbaImage.Create(); Result.SetSize(X2-X1+1, Y2-Y1+1); @@ -349,8 +355,8 @@ procedure TSimbaImage.Crop(X1, Y1, X2, Y2: Integer); var Y: Integer; begin - AssertInImage('Crop', X1, Y1); - AssertInImage('Crop', X2, Y2); + if (not InImage(X1, Y1)) then RaiseOutOfImageException(X1, Y1); + if (not InImage(X2, Y2)) then RaiseOutOfImageException(X2, Y2); for Y := Y1 to Y2 do Move(FData[Y * FWidth + X1], FData[(Y-Y1) * FWidth], FWidth * SizeOf(TColorBGRA)); @@ -375,15 +381,6 @@ function TSimbaImage.ToGreyMatrix: TByteMatrix; Result[Y, X] := Round(R * 0.3 + G * 0.59 + B * 0.11); end; -function TSimbaImage.ToMatrixBGR: TIntegerMatrix; -var - Y: Integer; -begin - Result.SetSize(FWidth, FHeight); - for Y := 0 to FHeight - 1 do - Move(FData[Y * FWidth], Result[Y, 0], FWidth * SizeOf(Integer)); -end; - function TSimbaImage.ToMatrix: TIntegerMatrix; var X, Y, W, H: Integer; @@ -395,21 +392,23 @@ function TSimbaImage.ToMatrix: TIntegerMatrix; for Y := 0 to H do for X := 0 to W do - Result[Y][X] := FData[Y * FWidth + X].ToColor(); + with FData[Y * FWidth + X] do + Result[Y, X] := TColor(R or G shl G_BIT or B shl B_BIT); end; function TSimbaImage.ToMatrix(X1, Y1, X2, Y2: Integer): TIntegerMatrix; var X, Y: Integer; begin - AssertInImage('ToMatrix', X1, Y1); - AssertInImage('ToMatrix', X2, Y2); + if (not InImage(X1, Y1)) then RaiseOutOfImageException(X1, Y1); + if (not InImage(X2, Y2)) then RaiseOutOfImageException(X2, Y2); Result.SetSize(X2-X1+1, Y2-Y1+1); for Y := Y1 to Y2 do for X := X1 to X2 do - Result[Y-Y1, X-X1] := FData[Y * FWidth + X].ToColor(); + with FData[Y * FWidth + X] do + Result[Y-Y1, X-X1] := TColor(R or G shl G_BIT or B shl B_BIT); end; procedure TSimbaImage.DrawMatrix(Matrix: TIntegerMatrix); @@ -420,13 +419,16 @@ procedure TSimbaImage.DrawMatrix(Matrix: TIntegerMatrix); W := FWidth - 1; H := FHeight - 1; - for Y := 0 to H do for X := 0 to W do - FData[Y * FWidth + X] := ColorToBGRA(Matrix[Y, X]); + FData[Y * FWidth + X] := TColorBGRA( + (Matrix[Y, X] shr B_BIT and $FF) or + (Matrix[Y, X] shr G_BIT and $FF) shl G_BIT or + (Matrix[Y, X] shr R_BIT and $FF) shl B_BIT + ); end; -procedure TSimbaImage.DrawMatrix(Matrix: TSingleMatrix; ColorMapID: Integer = 0); overload; +procedure TSimbaImage.DrawMatrix(Matrix: TSingleMatrix; ColorMapID: Integer = 0); var X,Y, W,H: TColor; Normed: TSingleMatrix; @@ -474,36 +476,21 @@ procedure TSimbaImage.DrawMatrix(Matrix: TSingleMatrix; ColorMapID: Integer = 0) end; end; - FData[Y*FWidth+X] := TColorBGRA(HSL.ToColor.ToBGRA()); + FData[Y*FWidth+X] := HSL.ToColor.ToBGRA(); end; end; -function TSimbaImage.RowPtrs: TSimbaImageRowPtrs; -var - I: Integer; +function TSimbaImage.SaveToString: String; begin - SetLength(Result, FHeight); - for I := 0 to High(Result) do - Result[I] := @FData[FWidth * I]; + Result := SimbaImage_ToString(Self); end; -function TSimbaImage.SaveToString: String; -const - Header = 'IMG:'; -var - Stream: TStringStream; +procedure TSimbaImage.FromString(Str: String); begin - Stream := TStringStream.Create(); - try - SimbaImage_ToFPImageWriter(Self, TFPWriterQoi, Stream); - - Result := Header + Base64Encode(ZCompressString(Stream.DataString)); - finally - Stream.Free(); - end; + SimbaImage_FromString(Self, Str); end; -procedure TSimbaImage.LoadFromStream(Stream: TStream; FileName: String); +procedure TSimbaImage.FromStream(Stream: TStream; FileName: String); var ReaderClass: TFPCustomImageReaderClass; begin @@ -514,25 +501,7 @@ procedure TSimbaImage.LoadFromStream(Stream: TStream; FileName: String); SimbaImage_FromFPImageReader(Self, ReaderClass, Stream); end; -procedure TSimbaImage.LoadFromString(Str: String); -const - HEADER = 'IMG:'; -var - Stream: TStringStream; -begin - if not Str.StartsWith(HEADER, True) then - SimbaException('TImage.LoadFromString: Invalid string "' + Str + '"'); - Str := Str.After(HEADER); - - Stream := TStringStream.Create(ZDecompressString(Base64Decode(Str))); - try - SimbaImage_FromFPImageReader(Self, TFPReaderQoi, Stream); - finally - Stream.Free(); - end; -end; - -procedure TSimbaImage.LoadFromData(AWidth, AHeight: Integer; AData: PColorBGRA; ADataWidth: Integer); +procedure TSimbaImage.FromData(AWidth, AHeight: Integer; AData: PColorBGRA; ADataWidth: Integer); var Y: Integer; begin @@ -541,10 +510,9 @@ procedure TSimbaImage.LoadFromData(AWidth, AHeight: Integer; AData: PColorBGRA; Exit; if (ADataWidth <> AWidth) then - begin for Y := 0 to AHeight - 1 do Move(AData[Y * ADataWidth], FData[Y * FWidth], FWidth * SizeOf(TColorBGRA)) - end else + else Move(AData^, FData^, FWidth * FHeight * SizeOf(TColorBGRA)); end; @@ -637,29 +605,28 @@ function TSimbaImage.Compare(Other: TSimbaImage): Single; function TSimbaImage.PixelDifference(Other: TSimbaImage): Integer; var I: Integer; - Ptr, OtherPtr: PColorBGRA; + P1, P2: PColorBGRA; begin Result := 0; if (FWidth <> Other.Width) or (FHeight <> Other.Height) then SimbaException('TSimbaImage.PixelDifference: Both images must be equal dimensions'); - Ptr := Data; - OtherPtr := Other.Data; - + P1 := Data; + P2 := Other.Data; for I := 0 to FWidth * FHeight - 1 do begin - if not Ptr^.EqualsIgnoreAlpha(OtherPtr^) then + if not P1^.EqualsIgnoreAlpha(P2^) then Inc(Result); - Inc(Ptr); - Inc(OtherPtr); + Inc(P1); + Inc(P2); end; end; function TSimbaImage.PixelDifference(Other: TSimbaImage; Tolerance: Single): Integer; var I: Integer; - Ptr, OtherPtr: PColorBGRA; + P1, P2: PColorBGRA; begin Result := 0; if (FWidth <> Other.Width) or (FHeight <> Other.Height) then @@ -669,38 +636,40 @@ function TSimbaImage.PixelDifference(Other: TSimbaImage; Tolerance: Single): Int Tolerance := Sqr(Tolerance); - Ptr := Data; - OtherPtr := Other.Data; - + P1 := Data; + P2 := Other.Data; for I := 0 to FWidth * FHeight - 1 do begin - if (not SimilarColors(Ptr^.ToColor(), OtherPtr^.ToColor(), Tolerance)) then + if (not SimilarColors(TColor(P1^.R or P1^.G shl G_BIT or P1^.B shl B_BIT), TColor(P2^.R or P2^.G shl G_BIT or P2^.B shl B_BIT), Tolerance)) then Inc(Result); - Inc(Ptr); - Inc(OtherPtr); + Inc(P1); + Inc(P2); end; end; function TSimbaImage.PixelDifferenceTPA(Other: TSimbaImage): TPointArray; var X, Y, W, H: Integer; - Index: Integer; + P1, P2: PColorBGRA; Buffer: TSimbaPointBuffer; begin if (FWidth <> Other.Width) or (FHeight <> Other.Height) then - SimbaException('TSimbaImage.PixelDifference: Both images must be equal dimensions'); - Buffer.Init(); + SimbaException('TSimbaImage.PixelDifferenceTPA: Both images must be equal dimensions'); W := FWidth - 1; H := FHeight - 1; + P1 := Data; + P2 := Other.Data; for Y := 0 to H do for X := 0 to W do begin - Index := Y * FWidth + X; - if not FData[Index].EqualsIgnoreAlpha(Other.FData[Index]) then - Buffer.Add(TPoint.Create(X, Y)); + if (not P1^.EqualsIgnoreAlpha(P2^)) then + {%H-}Buffer.Add(X, Y); + + Inc(P1); + Inc(P2); end; Result := Buffer.ToArray(False); @@ -709,40 +678,41 @@ function TSimbaImage.PixelDifferenceTPA(Other: TSimbaImage): TPointArray; function TSimbaImage.PixelDifferenceTPA(Other: TSimbaImage; Tolerance: Single): TPointArray; var X, Y, W, H: Integer; - Index: Integer; + P1, P2: PColorBGRA; Buffer: TSimbaPointBuffer; begin if (FWidth <> Other.Width) or (FHeight <> Other.Height) then - SimbaException('TSimbaImage.PixelDifference: Both images must be equal dimensions'); + SimbaException('TSimbaImage.PixelDifferenceTPA: Both images must be equal dimensions'); if (Tolerance = 0) then Exit(PixelDifferenceTPA(Other)); - - Buffer.Init(); - Tolerance := Sqr(Tolerance); W := FWidth - 1; H := FHeight - 1; + P1 := Data; + P2 := Other.Data; for Y := 0 to H do for X := 0 to W do begin - Index := Y * FWidth + X; - if (not (SimilarColors(FData[Index].ToColor(), Other.FData[Index].ToColor(), Tolerance))) then - Buffer.Add(TPoint.Create(X, Y)); + if (not SimilarColors(TColor(P1^.R or P1^.G shl G_BIT or P1^.B shl B_BIT), TColor(P2^.R or P2^.G shl G_BIT or P2^.B shl B_BIT), Tolerance)) then + Buffer.Add(X, Y); + + Inc(P1); + Inc(P2); end; Result := Buffer.ToArray(False); end; -procedure TSimbaImage.LoadFromLazBitmap(LazBitmap: TBitmap); +procedure TSimbaImage.FromLazBitmap(LazBitmap: TBitmap); var TempBitmap: TSimbaImage; begin SetSize(0, 0); TempBitmap := LazImage_ToSimbaImage(LazBitmap); - TempBitmap.DataOwner := False; + TempBitmap.FDataOwner := False; FData := TempBitmap.Data; FWidth := TempBitmap.Width; @@ -751,14 +721,7 @@ procedure TSimbaImage.LoadFromLazBitmap(LazBitmap: TBitmap); TempBitmap.Free(); end; -procedure TSimbaImage.LoadFromImage(Image: TSimbaImage); -begin - SetSize(Image.Width, Image.Height); - - Move(Image.FData^, FData^, FWidth * FHeight * SizeOf(TColorBGRA)); -end; - -procedure TSimbaImage.DrawTPA(const TPA: TPointArray; Color: TColor; Alpha: Byte); +procedure TSimbaImage.DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte); begin if (Alpha = 0) then _DrawTPA(TPA, Color) @@ -766,7 +729,7 @@ procedure TSimbaImage.DrawTPA(const TPA: TPointArray; Color: TColor; Alpha: Byte _DrawTPAAlpha(TPA, Color, Alpha); end; -procedure TSimbaImage.DrawATPA(const ATPA: T2DPointArray; Color: TColor; Alpha: Byte); +procedure TSimbaImage.DrawATPA(ATPA: T2DPointArray; Color: TColor; Alpha: Byte); var I: Integer; begin @@ -774,85 +737,55 @@ procedure TSimbaImage.DrawATPA(const ATPA: T2DPointArray; Color: TColor; Alpha: DrawTPA(ATPA[I], GetDistinctColor(Color, I), Alpha); end; -procedure TSimbaImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); +procedure TSimbaImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte); begin Size := Max(1, Size); with ACenter do begin - Self.DrawLine(Point(X - Size, Y), Point(X + Size, Y), Color); - Self.DrawLine(Point(X, Y - Size), Point(X, Y + Size), Color); + Self.DrawLine(Point(X - Size, Y), Point(X + Size, Y), Color, Alpha); + Self.DrawLine(Point(X, Y - Size), Point(X, Y + Size), Color, Alpha); end; end; -procedure TSimbaImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); +procedure TSimbaImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin Radius := Max(1, Round(Radius/2*Sqrt(2))); with ACenter do begin - Self.DrawLine(Point(X - Radius, Y - Radius), Point(X + Radius, Y + Radius), Color); - Self.DrawLine(Point(X + Radius, Y - Radius), Point(X - Radius, Y + Radius), Color); + Self.DrawLine(Point(X - Radius, Y - Radius), Point(X + Radius, Y + Radius), Color, Alpha); + Self.DrawLine(Point(X + Radius, Y - Radius), Point(X - Radius, Y + Radius), Color, Alpha); end; end; -procedure TSimbaImage.DrawLine(Start, Stop: TPoint; Color: TColor); -var - BGR: TColorBGRA; - - procedure PutPixel(const X, Y: Single); inline; - var - XX, YY: Integer; - begin - XX := Round(X); - YY := Round(Y); - if (XX >= 0) and (YY >= 0) and (XX < FWidth) and (YY < FHeight) then - FData[YY * FWidth + XX] := BGR; - end; - -var - DX, DY, Step, I: Integer; - RX, RY, X, Y: Single; +procedure TSimbaImage.DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte); begin - BGR := ColorToBGRA(Color); - - DX := (Stop.X - Start.X); - DY := (Stop.Y - Start.Y); - if (Abs(DX) > Abs(DY)) then - Step := Abs(DX) + if (Alpha > 0) then + _DrawLineAlpha(Start, Stop, Color, Alpha) else - Step := Abs(DY); - - if (Step = 0) then - begin - RX := DX; - RY := DY; - end else - begin - RX := DX / Step; - RY := DY / Step; - end; - X := Start.X; - Y := Start.Y; - - PutPixel(X, Y); - for I := 1 to Step do - begin - X := X + RX; - Y := Y + RY; + _DrawLine(Start, Stop, Color); +end; - PutPixel(X, Y); - end; +procedure TSimbaImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte); +begin + if (Alpha > 0) then + _DrawLineGapAlpha(Start, Stop, GapSize, Color, Alpha) + else + _DrawLineGap(Start, Stop, GapSize, Color); end; -procedure TSimbaImage.DrawPolygon(Points: TPointArray; Color: TColor); +procedure TSimbaImage.DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte); begin - Self.DrawTPA(Points.Connect(), Color); + if (Length(Points) < 3) then + Exit; + + Self.DrawTPA(Points.Connect(), Color, Alpha); end; procedure TSimbaImage.DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte); begin - if (Length(Points) = 0) then + if (Length(Points) < 3) then Exit; if (Alpha > 0) then @@ -861,51 +794,66 @@ procedure TSimbaImage.DrawPolygonFilled(Points: TPointArray; Color: TColor; Alph _DrawPolygonFilled(Points, Color); end; -procedure TSimbaImage.DrawPolygonInverted(Points: TPointArray; Color: TColor); +procedure TSimbaImage.DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte); var - X, Y, Hi: Integer; - Bounds: TBox; - RGB: TColorBGRA; + B: TBox; + BGRA: TColorBGRA; + X, Y: Integer; begin - Hi := High(Points); - if (Hi < 0) then + if (Length(Points) < 3) then Exit; - RGB := ColorToBGRA(Color); - Bounds := Points.Bounds().Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); + B := Points.Bounds().Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); + + Self.DrawBoxInverted(B, Color, Alpha); + + case (Alpha > 0) of + True: + begin + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; + + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInPolygon(X, Y, Points) then + BlendPixel(@FData[Y*FWidth+X], BGRA); + end; - Self.DrawBoxInverted(Bounds, Color); + False: + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; - for X := Bounds.X1 to Bounds.X2 do - for Y := Bounds.Y1 to Bounds.Y2 do - if not TSimbaGeometry.PointInPolygon(Point(X, Y), Points) then - FData[Y*FWidth+X] := RGB; + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInPolygon(X, Y, Points) then + FData[Y*FWidth+X] := BGRA; + end; + end; end; -procedure TSimbaImage.DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor); +procedure TSimbaImage.DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); +var + BGRA: TColorBGRA; procedure _SetPixel(const X, Y: Integer); inline; begin if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then - FData[Y*FWidth+X].AsInteger := Color; + FData[Y*FWidth+X] := BGRA; end; - {$i circle.inc} + {$i shapebuilder_circle.inc} begin if (Radius < 1) then Exit; - Color := Color.ToBGRA().AsInteger; // rgb to bgr - - _Circle(ACenter.X, ACenter.Y, Radius); -end; + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; -procedure TSimbaImage.DrawCircle(Circle: TCircle; Color: TColor); -begin - DrawCircle(Circle.Center, Circle.Radius, Color); + _BuildCircle(ACenter.X, ACenter.Y, Radius); end; -procedure TSimbaImage.DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); +procedure TSimbaImage.DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin if (Radius < 1) then Exit; @@ -916,72 +864,78 @@ procedure TSimbaImage.DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: _DrawCircleFilled(ACenter, Radius, Color); end; -procedure TSimbaImage.DrawCircleFilled(Circle: TCircle; Color: TColor; Alpha: Byte = 0); -begin - DrawCircleFilled(Circle.Center, Circle.Radius, Color, Alpha); -end; - -procedure TSimbaImage.DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor); +procedure TSimbaImage.DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); var X, Y: Integer; B: TBox; - RGB: TColorBGRA; + BGRA: TColorBGRA; begin - RGB := ColorToBGRA(Color); - B.X1 := Max(ACenter.X-Radius, 0); B.Y1 := Max(ACenter.Y-Radius, 0); B.X2 := Min(ACenter.X+Radius, FWidth-1); B.Y2 := Min(ACenter.Y+Radius, FHeight-1); - Self.DrawBoxInverted(B, Color); + Self.DrawBoxInverted(B, Color, Alpha); - for X := B.X1 to B.X2 do - for Y := B.Y1 to B.Y2 do - if not TSimbaGeometry.PointInCircle(TPoint.Create(X,Y), ACenter, Radius) then - FData[Y*FWidth+X] := RGB; -end; + case (Alpha > 0) of + True: + begin + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; -procedure TSimbaImage.DrawCircleInverted(Circle: TCircle; Color: TColor); -begin - DrawCircleInverted(Circle.Center, Circle.Radius, Color); + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInCircle(X, Y, ACenter.X, ACenter.Y, Radius) then + BlendPixel(@FData[Y*FWidth+X], BGRA); + end; + + False: + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInCircle(X, Y, ACenter.X, ACenter.Y, Radius) then + FData[Y*FWidth+X] := BGRA; + end; + end; end; -procedure TSimbaImage.DrawBox(B: TBox; Color: TColor); +procedure TSimbaImage.DrawBox(Box: TBox; Color: TColor; Alpha: Byte = 0); begin - Self.DrawTPA(TPointArray.CreateFromBox(B, False), Color); + if (Alpha > 0) then + _DrawBoxEdgeAlpha(Box, Color, Alpha) + else + _DrawBoxEdge(Box, Color); end; procedure TSimbaImage.DrawBoxFilled(Box: TBox; Color: TColor; Alpha: Byte); begin - Box := Box.Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); - if (Box.X2 - Box.X1 < 1) or (Box.Y2 - Box.Y1 < 1) then - Exit; - if (Alpha > 0) then _DrawBoxFilledAlpha(Box, Color, Alpha) else _DrawBoxFilled(Box, Color); end; -procedure TSimbaImage.DrawBoxInverted(B: TBox; Color: TColor); +procedure TSimbaImage.DrawBoxInverted(B: TBox; Color: TColor; Alpha: Byte); begin - Self.DrawBoxFilled(TBox.Create(0, 0, B.X1, B.Y1), Color); //Top Left - Self.DrawBoxFilled(TBox.Create(0, B.Y1, B.X1, B.Y2), Color); //Mid Left - Self.DrawBoxFilled(TBox.Create(0, B.Y1, B.X1, FHeight-1), Color); //Btm Left - Self.DrawBoxFilled(TBox.Create(B.X1, 0, B.X2, B.Y1), Color); //Top Mid - Self.DrawBoxFilled(TBox.Create(B.X1, B.Y2, B.X2, FHeight-1), Color); //Btm Mid - Self.DrawBoxFilled(TBox.Create(B.X2, 0, FWidth-1, B.Y1), Color); //Top Right - Self.DrawBoxFilled(TBox.Create(B.X2, B.Y1, FWidth-1, B.Y2), Color); //Mid Right - Self.DrawBoxFilled(TBox.Create(B.X2, B.Y1, FWidth-1, FHeight-1), Color); //Btm Right + Self.DrawBoxFilled(TBox.Create(0, 0, B.X1 - 1, B.Y1 - 1 ), Color, Alpha); //Top Left + Self.DrawBoxFilled(TBox.Create(0, B.Y1, B.X1 - 1, B.Y2 ), Color, Alpha); //Mid Left + Self.DrawBoxFilled(TBox.Create(0, B.Y2 + 1, B.X1 - 1, FHeight - 1), Color, Alpha); //Btm Left + Self.DrawBoxFilled(TBox.Create(B.X1, 0, B.X2, B.Y1 - 1 ), Color, Alpha); //Top Mid + Self.DrawBoxFilled(TBox.Create(B.X1, B.Y2 + 1, B.X2, FHeight - 1), Color, Alpha); //Btm Mid + Self.DrawBoxFilled(TBox.Create(B.X2 + 1, 0, FWidth-1, B.Y1 - 1 ), Color, Alpha); //Top Right + Self.DrawBoxFilled(TBox.Create(B.X2 + 1, B.Y1, FWidth-1, B.Y2 ), Color, Alpha); //Mid Right + Self.DrawBoxFilled(TBox.Create(B.X2 + 1, B.Y2 + 1, FWidth-1, FHeight - 1), Color, Alpha); //Btm Right end; -procedure TSimbaImage.DrawQuad(Quad: TQuad; Color: TColor); +procedure TSimbaImage.DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte); begin - DrawLine(Quad.Top, Quad.Right, Color); - DrawLine(Quad.Right, Quad.Bottom, Color); - DrawLine(Quad.Bottom, Quad.Left, Color); - DrawLine(Quad.Left, Quad.Top, Color); + DrawLine(Quad.Top, Quad.Right, Color, Alpha); + DrawLine(Quad.Right, Quad.Bottom, Color, Alpha); + DrawLine(Quad.Bottom, Quad.Left, Color, Alpha); + DrawLine(Quad.Left, Quad.Top, Color, Alpha); end; procedure TSimbaImage.DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte); @@ -989,22 +943,39 @@ procedure TSimbaImage.DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte); DrawPolygonFilled([Quad.Top, Quad.Right, Quad.Bottom, Quad.Left], Color, Alpha); end; -procedure TSimbaImage.DrawQuadInverted(Quad: TQuad; Color: TColor); +procedure TSimbaImage.DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte); var X, Y: Integer; - Bounds: TBox; - RGB: TColorBGRA; + B: TBox; + BGRA: TColorBGRA; begin - RGB := Color.ToBGRA(); + B := Quad.Bounds.Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); + + Self.DrawBoxInverted(B, Color, Alpha); + + case (Alpha > 0) of + True: + begin + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; - Bounds := Quad.Bounds.Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInQuad(X, Y, Quad.Top, Quad.Right, Quad.Bottom, Quad.Left) then + BlendPixel(@FData[Y*FWidth+X], BGRA); + end; - Self.DrawBoxInverted(Bounds, Color); + False: + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; - for X := Bounds.X1 to Bounds.X2 do - for Y := Bounds.Y1 to Bounds.Y2 do - if not TSimbaGeometry.PointInQuad(TPoint.Create(X, Y), Quad.Top, Quad.Right, Quad.Bottom, Quad.Left) then - FData[Y*FWidth+X] := RGB; + for X := B.X1 to B.X2 do + for Y := B.Y1 to B.Y2 do + if not TSimbaGeometry.PointInQuad(X, Y, Quad.Top, Quad.Right, Quad.Bottom, Quad.Left) then + FData[Y*FWidth+X] := BGRA; + end; + end; end; procedure TSimbaImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor); @@ -1051,17 +1022,6 @@ procedure TSimbaImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Fil DrawCircle(Centers[I], Radius, GetDistinctColor(Color, I)); end; -procedure TSimbaImage.DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor); -var - I: Integer; -begin - for I := 0 to High(Circles) do - if Filled then - DrawCircleFilled(Circles[I], GetDistinctColor(Color, I)) - else - DrawCircle(Circles[I], GetDistinctColor(Color, I)); -end; - procedure TSimbaImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor); var I: Integer; @@ -1092,53 +1052,92 @@ procedure TSimbaImage.DrawHSLCircle(ACenter: TPoint; Radius: Integer); end; end; -procedure TSimbaImage.Fill(Color: TColor); +procedure TSimbaImage.Fill(Color: TColor; Alpha: Byte); +var + BGRA: TColorBGRA; + Ptr: PColorBGRA; + Upper: PtrUInt; begin - FillData(@FData[0], FWidth * FHeight, Color.ToBGRA()); + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + if (Alpha > 0) then + begin + BGRA.A := Alpha; + + Upper := PtrUInt(@FData[FWidth * FHeight]); + Ptr := FData; + while (PtrUInt(Ptr) < Upper) do + begin + BlendPixel(Ptr, BGRA); + + Inc(Ptr); + end; + end else + FillData(FData, FWidth * FHeight, BGRA); end; procedure TSimbaImage.Clear; begin - DrawBoxFilled(TBox.Create(0, 0, FWidth-1, FHeight-1), FTransparentColor); + if (FWidth * FHeight > 0) then + FillData(FData, FWidth * FHeight, DefaultPixel); end; -procedure TSimbaImage.Clear(Area: TBox); +procedure TSimbaImage.Clear(Box: TBox); + + procedure _Row(const Y: Integer; const X1, X2: Integer); + begin + FillData(@FData[Y * FWidth + X1], Box.Width, DefaultPixel); + end; + + {$i shapebuilder_boxfilled.inc} + begin - DrawBoxFilled(Area, FTransparentColor); + Box := Box.Clip(TBox.Create(0, 0, FWidth - 1, FHeight - 1)); + if (Box.Width > 1) and (Box.Height > 1) then + _BuildBoxFilled(Box); end; -procedure TSimbaImage.ClearInverted(Area: TBox); +procedure TSimbaImage.ClearInverted(Box: TBox); begin - DrawBoxInverted(Area, FTransparentColor); + Self.Clear(TBox.Create(0, 0, Box.X1 - 1, Box.Y1 - 1 )); //Top Left + Self.Clear(TBox.Create(0, Box.Y1, Box.X1 - 1, Box.Y2 )); //Mid Left + Self.Clear(TBox.Create(0, Box.Y2 + 1, Box.X1 - 1, FHeight - 1)); //Btm Left + Self.Clear(TBox.Create(Box.X1, 0, Box.X2, Box.Y1 - 1 )); //Top Mid + Self.Clear(TBox.Create(Box.X1, Box.Y2 + 1, Box.X2, FHeight - 1)); //Btm Mid + Self.Clear(TBox.Create(Box.X2 + 1, 0, FWidth-1, Box.Y1 - 1 )); //Top Right + Self.Clear(TBox.Create(Box.X2 + 1, Box.Y1, FWidth-1, Box.Y2 )); //Mid Right + Self.Clear(TBox.Create(Box.X2 + 1, Box.Y2 + 1, FWidth-1, FHeight - 1)); //Btm Right end; -procedure TSimbaImage.Draw(Image: TSimbaImage; X, Y: Integer); +procedure TSimbaImage.SplitChannels(out B,G,R,A: TByteArray); var - LoopX, LoopY, W, H: Integer; - DestX, DestY: Integer; - Color: TColorBGRA; + I: Integer; + Ptr: PColorBGRA; begin - W := Image.Width - 1; - H := Image.Height - 1; + SetLength(B, FWidth*FHeight); + SetLength(G, FWidth*FHeight); + SetLength(R, FWidth*FHeight); + SetLength(A, FWidth*FHeight); - for LoopY := 0 to H do - for LoopX := 0 to W do - begin - DestX := LoopX + X; - DestY := LoopY + Y; + Ptr := FData; + for I := 0 to FWidth * FHeight - 1 do + begin + B[I] := Ptr^.B; + G[I] := Ptr^.G; + R[I] := Ptr^.R; + A[I] := Ptr^.A; - if (DestX >= 0) and (DestY >= 0) and (DestX < FWidth) and (DestY < FHeight) then - begin - Color := Image.Data[LoopY * Image.Width + LoopX]; - if (Image.TransparentColorActive) and (not Color.EqualsIgnoreAlpha(Image.TransparentRGB)) then - FData[DestY * FWidth + DestX] := Image.FData[LoopY * Image.Width + LoopX]; - end; - end; + Inc(Ptr); + end; end; -procedure TSimbaImage.Draw(Image: TSimbaImage; Position: TPoint); +procedure TSimbaImage.DrawImage(Image: TSimbaImage; Location: TPoint; Alpha: Byte = 0); begin - Draw(Image, Position.X, Position.Y); + if (Alpha = 0) then + _DrawImage(Image, Location) + else + _DrawImageAlpha(Image, Location, Alpha); end; function TSimbaImage.GetColors: TColorArray; @@ -1147,7 +1146,8 @@ function TSimbaImage.GetColors: TColorArray; begin SetLength(Result, FHeight * FWidth); for I := 0 to High(Result) do - Result[I] := FData[I].ToColor(); + with FData[I] do + Result[I] := TColor(R or G shl G_BIT or B shl B_BIT); end; function TSimbaImage.Equals(Other: TObject): Boolean; @@ -1158,11 +1158,28 @@ function TSimbaImage.Equals(Other: TObject): Boolean; Result := inherited Equals(Other); end; +// Compare without alpha function TSimbaImage.Equals(Other: TSimbaImage): Boolean; +var + I: Integer; + Ptr, OtherPtr: PColorBGRA; begin - Result := (FWidth = Other.FWidth) and - (FHeight = Other.FHeight) and - (CompareMem(FData, Other.FData, FWidth * FHeight * SizeOf(TColorBGRA))); + if (FWidth <> Other.Width) or (FHeight <> Other.Height) then + Exit(False); + + Ptr := Data; + OtherPtr := Other.Data; + + for I := 0 to FWidth * FHeight - 1 do + begin + if not Ptr^.EqualsIgnoreAlpha(OtherPtr^) then + Exit(False); + + Inc(Ptr); + Inc(OtherPtr); + end; + + Result := True; end; class function TSimbaImage.LoadFonts(Dir: String): Boolean; @@ -1293,6 +1310,40 @@ function TSimbaImage.RotateBilinear(Radians: Single; Expand: Boolean): TSimbaIma var CosAngle, SinAngle: Single; + function ShouldRotate(var p0, p1, p2, p3: TColorBGRA): Boolean; inline; + begin + if (p0.A > 0) or (p1.A > 0) or (p2.A > 0) or (p3.A > 0) then + begin + if (p0.A = 0) then + begin + if (p1.A > 0) then p0 := p1 else + if (p2.A > 0) then p0 := p2 else + if (p3.A > 0) then p0 := p3; + end; + if (p1.A = 0) then + begin + if (p0.A > 0) then p1 := p0 else + if (p2.A > 0) then p1 := p2 else + if (p3.A > 0) then p1 := p3; + end; + if (p2.A = 0) then + begin + if (p0.A > 0) then p2 := p0 else + if (p1.A > 0) then p2 := p1 else + if (p3.A > 0) then p2 := p3; + end; + if (p3.A = 0) then + begin + if (p0.A > 0) then p3 := p0 else + if (p1.A > 0) then p3 := p1 else + if (p2.A > 0) then p3 := p2; + end; + + Result := True; + end else + Result := False; + end; + procedure RotateNoExpand; var x, y, w, h: Integer; @@ -1304,6 +1355,7 @@ function TSimbaImage.RotateBilinear(Radians: Single; Expand: Boolean): TSimbaIma MidX, MidY: Single; begin Result.SetSize(FWidth, FHeight); + Result.SetAlpha(0); MidX := (FWidth - 1) / 2; MidY := (FHeight - 1) / 2; @@ -1334,19 +1386,22 @@ function TSimbaImage.RotateBilinear(Radians: Single; Expand: Boolean): TSimbaIma p2 := FData[cY * FWidth + fX]; p3 := FData[cY * FWidth + cX]; - TopR := dxMinus1 * p0.R + dx * p1.R; - TopG := dxMinus1 * p0.G + dx * p1.G; - TopB := dxMinus1 * p0.B + dx * p1.B; - BtmR := dxMinus1 * p2.R + dx * p3.R; - BtmG := dxMinus1 * p2.G + dx * p3.G; - BtmB := dxMinus1 * p2.B + dx * p3.B; - - with Result.Data[Y * Result.Width + X] do + if ShouldRotate(p0, p1, p2, p3) then begin - R := EnsureRange(Round(dyMinus1 * TopR + dy * BtmR), 0, 255); - G := EnsureRange(Round(dyMinus1 * TopG + dy * BtmG), 0, 255); - B := EnsureRange(Round(dyMinus1 * TopB + dy * BtmB), 0, 255); - A := 0; + TopR := dxMinus1 * p0.R + dx * p1.R; + TopG := dxMinus1 * p0.G + dx * p1.G; + TopB := dxMinus1 * p0.B + dx * p1.B; + BtmR := dxMinus1 * p2.R + dx * p3.R; + BtmG := dxMinus1 * p2.G + dx * p3.G; + BtmB := dxMinus1 * p2.B + dx * p3.B; + + with Result.Data[Y * Result.Width + X] do + begin + R := EnsureRange(Round(dyMinus1 * TopR + dy * BtmR), 0, 255); + G := EnsureRange(Round(dyMinus1 * TopG + dy * BtmG), 0, 255); + B := EnsureRange(Round(dyMinus1 * TopB + dy * BtmB), 0, 255); + A := ALPHA_OPAQUE; + end; end; end; end; @@ -1370,6 +1425,7 @@ function TSimbaImage.RotateBilinear(Radians: Single; Expand: Boolean): TSimbaIma MidY := (NewHeight - 1) / 2; Result.SetSize(NewWidth, NewHeight); + Result.SetAlpha(0); Dec(NewWidth); Dec(NewHeight); @@ -1397,19 +1453,22 @@ function TSimbaImage.RotateBilinear(Radians: Single; Expand: Boolean): TSimbaIma p2 := FData[cY * FWidth + fX]; p3 := FData[cY * FWidth + cX]; - TopR := dxMinus1 * p0.R + dx * p1.R; - TopG := dxMinus1 * p0.G + dx * p1.G; - TopB := dxMinus1 * p0.B + dx * p1.B; - BtmR := dxMinus1 * p2.R + dx * p3.R; - BtmG := dxMinus1 * p2.G + dx * p3.G; - BtmB := dxMinus1 * p2.B + dx * p3.B; - - with Result.Data[Y * Result.Width + X] do + if ShouldRotate(p0, p1, p2, p3) then begin - R := EnsureRange(Round(dyMinus1 * TopR + dy * BtmR), 0, 255); - G := EnsureRange(Round(dyMinus1 * TopG + dy * BtmG), 0, 255); - B := EnsureRange(Round(dyMinus1 * TopB + dy * BtmB), 0, 255); - A := 0; + TopR := dxMinus1 * p0.R + dx * p1.R; + TopG := dxMinus1 * p0.G + dx * p1.G; + TopB := dxMinus1 * p0.B + dx * p1.B; + BtmR := dxMinus1 * p2.R + dx * p3.R; + BtmG := dxMinus1 * p2.G + dx * p3.G; + BtmB := dxMinus1 * p2.B + dx * p3.B; + + with Result.Data[Y * Result.Width + X] do + begin + R := EnsureRange(Round(dyMinus1 * TopR + dy * BtmR), 0, 255); + G := EnsureRange(Round(dyMinus1 * TopG + dy * BtmG), 0, 255); + B := EnsureRange(Round(dyMinus1 * TopB + dy * BtmB), 0, 255); + A := ALPHA_OPAQUE; + end; end; end; end; @@ -1439,11 +1498,15 @@ function TSimbaImage.GreyScale: TSimbaImage; for i := (FHeight * FWidth - 1) downto 0 do begin - Lum := Round(Src^.R * 0.3 + Src^.G * 0.59 + Src^.B * 0.11); + Dest^.A := Src^.A; + if (Dest^.A > 0) then + begin + Lum := Round(Src^.R * 0.3 + Src^.G * 0.59 + Src^.B * 0.11); - Dest^.R := Lum; - Dest^.G := Lum; - Dest^.B := Lum; + Dest^.R := Lum; + Dest^.G := Lum; + Dest^.B := Lum; + end; Inc(Src); Inc(Dest); @@ -1462,9 +1525,13 @@ function TSimbaImage.Brightness(Value: Integer): TSimbaImage; for I := (FHeight * FWidth - 1) downto 0 do begin - Dest^.R := EnsureRange(Src^.R + Value, 0, 255); - Dest^.G := EnsureRange(Src^.G + Value, 0, 255); - Dest^.B := EnsureRange(Src^.B + Value, 0, 255); + Dest^.A := Src^.A; + if (Dest^.A > 0) then + begin + Dest^.R := EnsureRange(Src^.R + Value, 0, 255); + Dest^.G := EnsureRange(Src^.G + Value, 0, 255); + Dest^.B := EnsureRange(Src^.B + Value, 0, 255); + end; Inc(Src); Inc(Dest); @@ -1483,9 +1550,13 @@ function TSimbaImage.Invert: TSimbaImage; for I := (FHeight * FWidth - 1) downto 0 do begin - Dest^.R := not Src^.R; - Dest^.G := not Src^.G; - Dest^.B := not Src^.B; + Dest^.A := Src^.A; + if (Dest^.A > 0) then + begin + Dest^.R := not Src^.R; + Dest^.G := not Src^.G; + Dest^.B := not Src^.B; + end; Inc(Src); Inc(Dest); @@ -1507,9 +1578,13 @@ function TSimbaImage.Posterize(Value: Integer): TSimbaImage; for I := (FHeight * FWidth - 1) downto 0 do begin - Dest^.R := Min(Round(Src^.R / Value) * Value, 255); - Dest^.G := Min(Round(Src^.G / Value) * Value, 255); - Dest^.B := Min(Round(Src^.B / Value) * Value, 255); + Dest^.A := Src^.A; + if (Dest^.A > 0) then + begin + Dest^.R := Min(Round(Src^.R / Value) * Value, 255); + Dest^.G := Min(Round(Src^.G / Value) * Value, 255); + Dest^.B := Min(Round(Src^.B / Value) * Value, 255); + end; Inc(Src); Inc(Dest); @@ -1527,7 +1602,7 @@ function TSimbaImage.BoxBlur(Radius: Integer): TSimbaImage; end; Ptr: PColorBGRA; begin - Result := TSimbaImage.Create(FWidth, FHeight); + Result := Copy(); if (Radius <= 1) or (not Odd(Radius)) then SimbaException('TSimbaImage.BlockBlur: Radius(%d) must be odd (1,3,5 etc).', [Radius]); @@ -1539,46 +1614,86 @@ function TSimbaImage.BoxBlur(Radius: Integer): TSimbaImage; for Y := 0 to H do for X := 0 to W do begin - B.X1 := Max(X-Radius, 0); - B.Y1 := Max(Y-Radius, 0); - B.X2 := Min(X+Radius, W); - B.Y2 := Min(Y+Radius, H); - - Size := 0; - Sum.R := 0; Sum.G := 0; Sum.B := 0; + if (FData[Y * FWidth + X].A > ALPHA_TRANSPARENT) then + begin + B.X1 := Max(X-Radius, 0); + B.Y1 := Max(Y-Radius, 0); + B.X2 := Min(X+Radius, W); + B.Y2 := Min(Y+Radius, H); - for YY := B.Y1 to B.Y2 - 1 do - for XX := B.X1 to B.X2 - 1 do - begin - Ptr := @FData[YY * FWidth + XX]; + Size := 0; + Sum.R := 0; Sum.G := 0; Sum.B := 0; - Sum.R += Ptr^.R; - Sum.G += Ptr^.G; - Sum.B += Ptr^.B; - Size += 1; - end; + for YY := B.Y1 to B.Y2 - 1 do + for XX := B.X1 to B.X2 - 1 do + begin + Ptr := @FData[YY * FWidth + XX]; + if (Ptr^.A > 0) then + begin + Sum.R += Ptr^.R; + Sum.G += Ptr^.G; + Sum.B += Ptr^.B; + + Size += 1; + end; + end; - Ptr := @Result.Data[Y * FWidth + X]; - Ptr^.R := Sum.R div Size; - Ptr^.G := Sum.G div Size; - Ptr^.B := Sum.B div Size; + Ptr := @Result.Data[Y * FWidth + X]; + Ptr^.R := Sum.R div Size; + Ptr^.G := Sum.G div Size; + Ptr^.B := Sum.B div Size; + end; end; end; -function TSimbaImage.Convolute(Matrix: TDoubleMatrix): TSimbaImage; +function TSimbaImage.GaussBlur(Radius: Double): TSimbaImage; var - X, Y, YY, XX, CX, CY: Integer; - SrcRows, DestRows: TSimbaImageRowPtrs; - MatWidth, MatHeight, MidX, MidY: Integer; - NewR, NewG, NewB: Double; + B,G,R,A: TByteArray; + Ignore: TBooleanArray; + I: Integer; + Ptr: PColorBGRA; begin Result := TSimbaImage.Create(FWidth, FHeight); + if (FWidth * FHeight = 0) then + Exit; - SrcRows := RowPtrs; - DestRows := Result.RowPtrs; + SplitChannels(B,G,R,A); - if Matrix.GetSize(MatWidth, MatHeight) then - begin + SetLength(Ignore, Length(A)); + for I := 0 to High(Ignore) do + Ignore[I] := (A[I] = ALPHA_TRANSPARENT); + + imgGaussBlur(Radius, R,G,B, Ignore, FWidth, FHeight); + + Ptr := Result.Data; + for i := 0 to (FWidth * FHeight) - 1 do + begin + Ptr^.A := A[I]; + if (Ptr^.A > ALPHA_TRANSPARENT) then + begin + Ptr^.B := B[I]; + Ptr^.G := G[I]; + Ptr^.R := R[I]; + end; + + Inc(Ptr); + end; +end; + +function TSimbaImage.Convolute(Matrix: TDoubleMatrix): TSimbaImage; +var + X, Y, YY, XX, CX, CY: Integer; + SrcRows, DestRows: TSimbaImageLineStarts; + MatWidth, MatHeight, MidX, MidY: Integer; + NewR, NewG, NewB: Double; +begin + Result := TSimbaImage.Create(FWidth, FHeight); + + SrcRows := LineStarts; + DestRows := Result.LineStarts; + + if Matrix.GetSize(MatWidth, MatHeight) then + begin MidX := MatWidth div 2; MidY := MatHeight div 2; @@ -1640,58 +1755,9 @@ function TSimbaImage.Mirror(Style: ESimbaImageMirrorStyle): TSimbaImage; for X := FHeight - 1 downto 0 do Result.FData[X*FHeight+Y] := FData[Y*FWidth+X]; end; - end; -end; - -function TSimbaImage.GaussBlur(Radius: Double): TSimbaImage; -var - r,g,b: TByteArray; - i: Integer; - ptr: PColorBGRA; - ptrR, ptrG, ptrB: PByte; -begin - Result := TSimbaImage.Create(FWidth, FHeight); - if (FWidth = 0) or (FHeight = 0) then - Exit; - - SetLength(r, FWidth*FHeight); - SetLength(g, FWidth*FHeight); - SetLength(b, FWidth*FHeight); - - ptr := FData; - ptrR := @r[0]; - ptrG := @g[0]; - ptrB := @b[0]; - - for i := 0 to (FWidth * FHeight) - 1 do - begin - ptrR^ := ptr^.R; - ptrG^ := ptr^.G; - ptrB^ := ptr^.B; - - Inc(ptr); - Inc(ptrR); - Inc(ptrG); - Inc(ptrB); - end; - - imgGaussBlur(Radius, r,g,b, FWidth, FHeight); - - ptr := Result.FData; - ptrR := @r[0]; - ptrG := @g[0]; - ptrB := @b[0]; - - for i := 0 to (FWidth * FHeight) - 1 do - begin - ptr^.R := ptrR^; - ptr^.G := ptrG^; - ptr^.B := ptrB^; - Inc(ptr); - Inc(ptrR); - Inc(ptrG); - Inc(ptrB); + else + Result := nil; end; end; @@ -1703,14 +1769,16 @@ function TSimbaImage.Blend(Points: TPointArray; Radius: Integer): TSimbaImage; R, G, B: Int64; Color: TColorBGRA; begin - Result := TSimbaImage.Create(Width, Height); + Result := Self.Copy(); + for P in Points do + Result.Data[P.Y * FWidth + P.X].A := 0; for P in Points do begin Area.X1 := Max(P.X - Radius, 0); Area.Y1 := Max(P.Y - Radius, 0); - Area.X2 := Min(P.X + Radius, FWidth-1); - Area.Y2 := Min(P.Y + Radius, FHeight-1); + Area.X2 := Min(P.X + Radius, FWidth - 1); + Area.Y2 := Min(P.Y + Radius, FHeight - 1); Count := 0; @@ -1721,8 +1789,8 @@ function TSimbaImage.Blend(Points: TPointArray; Radius: Integer): TSimbaImage; for X := Area.X1 to Area.X2 do for Y := Area.Y1 to Area.Y2 do begin - Color := FData[Y * FWidth + X]; - if FTransparentColorActive and Color.EqualsIgnoreAlpha(FTransparentRGB) then + Color := Result.Data[Y * FWidth + X]; + if (Color.A = ALPHA_TRANSPARENT) then Continue; Inc(R, Color.R); @@ -1737,8 +1805,9 @@ function TSimbaImage.Blend(Points: TPointArray; Radius: Integer): TSimbaImage; Color.R := R div Count; Color.G := G div Count; Color.B := B div Count; + Color.A := ALPHA_OPAQUE; - Result.FData[P.Y * FWidth + P.X] := Color; + Result.Data[P.Y * FWidth + P.X] := Color; end; end; end; @@ -1747,7 +1816,7 @@ function TSimbaImage.Downsample(Scale: Integer): TSimbaImage; var Area: Double; - function BlendArea(const X1, Y1, X2, Y2: Integer): TColorBGRA; inline; + function BlendArea(const X1, Y1, X2, Y2: Integer): TColorBGRA; var R, G, B: Integer; Hit, Miss: Integer; @@ -1766,7 +1835,7 @@ function TSimbaImage.Downsample(Scale: Integer): TSimbaImage; begin Color := FData[Y * FWidth + X]; - if FTransparentColorActive and Color.EqualsIgnoreAlpha(FTransparentRGB) then + if (Color.A = ALPHA_TRANSPARENT) then Inc(Miss) else begin @@ -1781,7 +1850,7 @@ function TSimbaImage.Downsample(Scale: Integer): TSimbaImage; Result.R := Round((R + (R div Hit) * Miss) * Area); Result.G := Round((G + (G div Hit) * Miss) * Area); Result.B := Round((B + (B div Hit) * Miss) * Area); - Result.A := 0; + Result.A := ALPHA_OPAQUE; end; var @@ -1803,19 +1872,13 @@ function TSimbaImage.Downsample(Scale: Integer): TSimbaImage; OldY := Y * Scale; Color := FData[OldY * FWidth + OldX]; - if FTransparentColorActive and Color.EqualsIgnoreAlpha(FTransparentRGB) then + if (Color.A = ALPHA_TRANSPARENT) then Continue; Result.Data[Y * Result.Width + X] := BlendArea(OldX, OldY, (OldX + Scale) - 1, (OldY + Scale) - 1); end; end; -function TSimbaImage.GetCenter: TPoint; -begin - Result.X := FWidth div 2; - Result.Y := FHeight div 2; -end; - function TSimbaImage.GetFontAntialiasing: Boolean; begin Result := FTextDrawer.Antialiased; @@ -1826,7 +1889,7 @@ function TSimbaImage.GetFontName: String; Result := FTextDrawer.FontName; end; -class function TSimbaImage.LoadedFontNames: TStringArray; +class function TSimbaImage.FontNames: TStringArray; begin Result := SimbaFreeTypeFontLoader.FontNames; end; @@ -1846,6 +1909,11 @@ function TSimbaImage.GetFontItalic: Boolean; Result := FTextDrawer.Italic; end; +function TSimbaImage.GetLineStart(const Y: Integer): PColorBGRA; +begin + Result := FLineStarts[Y]; +end; + procedure TSimbaImage.SetFontAntialiasing(Value: Boolean); begin FTextDrawer.Antialiased := Value; @@ -1891,9 +1959,9 @@ procedure TSimbaImage.DrawText(Text: String; Position: TPoint; Color: TColor); FTextDrawer.DrawText(Text, Position, Color); end; -procedure TSimbaImage.DrawText(Text: String; Box: TBox; ACenter: Boolean; Color: TColor); +procedure TSimbaImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); begin - FTextDrawer.DrawText(Text, Box, ACenter, Color); + FTextDrawer.DrawText(Text, Box, Alignments, Color); end; procedure TSimbaImage.DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor); @@ -1916,7 +1984,6 @@ procedure TSimbaImage.SetSize(NewWidth, NewHeight: Integer); var NewData: PColorBGRA; I, MinW, MinH: Integer; - Size: SizeInt; begin if (not FDataOwner) then SimbaException('TSimbaImage.SetSize: Cannot resize a image with external memory'); @@ -1924,28 +1991,73 @@ procedure TSimbaImage.SetSize(NewWidth, NewHeight: Integer); if (NewWidth <> FWidth) or (NewHeight <> FHeight) then begin if (NewWidth * NewHeight <> 0) then - NewData := AllocMem(NewWidth * NewHeight * SizeOf(TColorBGRA)) - else + begin + NewData := GetMem(NewWidth * NewHeight * SizeOf(TColorBGRA)); + + FillData(NewData, NewWidth * NewHeight, DefaultPixel); + end else NewData := nil; if Assigned(FData) and Assigned(NewData) and (FWidth * FHeight <> 0) then begin MinW := Min(NewWidth, FWidth); MinH := Min(NewHeight, FHeight); - - Size := MinW * SizeOf(TColorBGRA); for I := 0 to MinH - 1 do - Move(FData[I * FWidth], NewData[I * NewWidth], Size); + Move(FData[I * FWidth], NewData[I * NewWidth], MinW * SizeOf(TColorBGRA)); end; if Assigned(FData) then FreeMem(FData); FData := NewData; + FDataSize := (NewWidth * NewHeight) * SizeOf(TColorBGRA); FWidth := NewWidth; FHeight := NewHeight; + FCenter := TPoint.Create(FWidth div 2, FHeight div 2); + + SetLength(FLineStarts, FHeight); + for I := 0 to High(FLineStarts) do + FLineStarts[I] := @FData[FWidth * I]; + end; +end; + +procedure TSimbaImage.SetAlpha(Value: Byte); +var + Upper: PtrUInt; + Ptr: PColorBGRA; +begin + Upper := PtrUInt(@FData[FWidth * FHeight]); + Ptr := FData; + + while (PtrUInt(Ptr) < Upper) do + begin + Ptr^.A := Value; + + Inc(Ptr); end; end; +procedure TSimbaImage.SetAlpha(Points: TPointArray; Value: Byte); +var + P: TPoint; +begin + for P in Points do + if (P.X >= 0) and (P.Y >= 0) and (P.X < FWidth) and (P.Y < FHeight) then + FData[P.Y * FWidth + P.X].A := Value; +end; + +procedure TSimbaImage.SetAlpha(Color: TColor; Value: Byte); +var + BGRA: TColorBGRA; + X, Y: Integer; +begin + BGRA := Color.ToBGRA(); + + for Y := 0 to FHeight - 1 do + for X := 0 to FWidth - 1 do + if FData[Y * FWidth + X].EqualsIgnoreAlpha(BGRA) then + FData[Y * FWidth + X].A := Value; +end; + function TSimbaImage.ResizeNN(NewWidth, NewHeight: Integer): TSimbaImage; var X, Y, W, H: Integer; @@ -1961,17 +2073,51 @@ function TSimbaImage.ResizeNN(NewWidth, NewHeight: Integer): TSimbaImage; end; function TSimbaImage.ResizeBilinear(NewWidth, NewHeight: Integer): TSimbaImage; + + function ShouldResize(var p0, p1, p2, p3: TColorBGRA): Boolean; inline; + begin + if (p0.A > 0) or (p1.A > 0) or (p2.A > 0) or (p3.A > 0) then + begin + if (p0.A = 0) then + begin + if (p1.A > 0) then p0 := p1 else + if (p2.A > 0) then p0 := p2 else + if (p3.A > 0) then p0 := p3; + end; + if (p1.A = 0) then + begin + if (p0.A > 0) then p1 := p0 else + if (p2.A > 0) then p1 := p2 else + if (p3.A > 0) then p1 := p3; + end; + if (p2.A = 0) then + begin + if (p0.A > 0) then p2 := p0 else + if (p1.A > 0) then p2 := p1 else + if (p3.A > 0) then p2 := p3; + end; + if (p3.A = 0) then + begin + if (p0.A > 0) then p3 := p0 else + if (p1.A > 0) then p3 := p1 else + if (p2.A > 0) then p3 := p2; + end; + + Result := True; + end else + Result := False; + end; + var X, Y, OldX, OldY: Integer; p0, p1, p2, p3: TColorBGRA; RatioX, RatioY, dX, dY: Single; - SrcRows: TSimbaImageRowPtrs; Color: TColorBGRA; W,H: Integer; begin Result := TSimbaImage.Create(NewWidth, NewHeight); - SrcRows := RowPtrs; + Color := DefaultPixel; RatioX := (FWidth - 1) / NewWidth; RatioY := (FHeight - 1) / NewHeight; @@ -1984,36 +2130,39 @@ function TSimbaImage.ResizeBilinear(NewWidth, NewHeight: Integer): TSimbaImage; OldX := Trunc(RatioX * X); OldY := Trunc(RatioY * Y); - p0 := SrcRows[OldY, OldX ]; - p1 := SrcRows[OldY, OldX + 1]; - p2 := SrcRows[OldY + 1, OldX ]; - p3 := SrcRows[OldY + 1, OldX + 1]; - - dX := ratioX * X - OldX; - dY := ratioY * Y - OldY; - - Color.R := Trunc( - p0.R * (1-dX) * (1-dY) + - p1.R * (dX * (1-dY)) + - p2.R * (dY * (1-dX)) + - p3.R * (dX * dY) - ); - - Color.G := Trunc( - p0.G * (1-dX) * (1-dY) + - p1.G * (dX * (1-dY)) + - p2.G * (dY * (1-dX)) + - p3.G * (dX * dY) - ); - - Color.B := Trunc( - p0.B * (1-dX) * (1-dY) + - p1.B * (dX * (1-dY)) + - p2.B * (dY * (1-dX)) + - p3.B * (dX * dY) - ); + p0 := FLineStarts[OldY, OldX ]; + p1 := FLineStarts[OldY, OldX + 1]; + p2 := FLineStarts[OldY + 1, OldX ]; + p3 := FLineStarts[OldY + 1, OldX + 1]; - Result.Data[Y * NewWidth + X] := Color; + if ShouldResize(p0, p1, p2, p3) then + begin + dX := ratioX * X - OldX; + dY := ratioY * Y - OldY; + + Color.R := Trunc( + p0.R * (1-dX) * (1-dY) + + p1.R * (dX * (1-dY)) + + p2.R * (dY * (1-dX)) + + p3.R * (dX * dY) + ); + + Color.G := Trunc( + p0.G * (1-dX) * (1-dY) + + p1.G * (dX * (1-dY)) + + p2.G * (dY * (1-dX)) + + p3.G * (dX * dY) + ); + + Color.B := Trunc( + p0.B * (1-dX) * (1-dY) + + p1.B * (dX * (1-dY)) + + p2.B * (dY * (1-dX)) + + p3.B * (dX * dY) + ); + + Result.Data[Y * NewWidth + X] := Color; + end; end; end; @@ -2022,12 +2171,15 @@ function TSimbaImage.GetPixels(Points: TPointArray): TColorArray; I: Integer; begin SetLength(Result, Length(Points)); + for I := 0 to High(Points) do with Points[I] do begin - AssertInImage('GetPixels', X, Y); + if (X < 0) or (Y < 0) or (X >= FWidth) or (Y >= FHeight) then + RaiseOutOfImageException(X, Y); - Result[I] := FData[Y * FWidth + X].ToColor(); + with FData[Y * FWidth + X] do + Result[I] := TColor(R or G shl G_BIT or B shl B_BIT); end; end; @@ -2041,7 +2193,8 @@ procedure TSimbaImage.SetPixels(Points: TPointArray; Colors: TColorArray); for I := 0 to High(Points) do with Points[I] do begin - AssertInImage('SetPixels', X, Y); + if (X < 0) or (Y < 0) or (X >= FWidth) or (Y >= FHeight) then + RaiseOutOfImageException(X, Y); FData[Y * FWidth + X] := Colors[I].ToBGRA(); end; @@ -2227,107 +2380,222 @@ procedure TSimbaImage.Pad(Amount: Integer); // clear old pixels if (Y < Amount) then - FillData(@FData[Y * FWidth], OldWidth * SizeOf(TColorBGRA), TransparentRGB) + FillData(@FData[Y * FWidth], OldWidth * SizeOf(TColorBGRA), DefaultPixel) else - FillData(@FData[Y * FWidth], Amount * SizeOf(TColorBGRA), TransparentRGB); + FillData(@FData[Y * FWidth], Amount * SizeOf(TColorBGRA), DefaultPixel); end; end; -procedure TSimbaImage._DrawTPA(const TPA: TPointArray; Color: TColor); +procedure TSimbaImage._DrawTPA(TPA: TPointArray; Color: TColor); var BGRA: TColorBGRA; Point: TPoint; begin BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; for Point in TPA do if (Point.X >= 0) and (Point.Y >= 0) and (Point.X < FWidth) and (Point.Y < FHeight) then FData[Point.Y * FWidth + Point.X] := BGRA; end; -procedure TSimbaImage._DrawTPAAlpha(const TPA: TPointArray; Color: TColor; Alpha: Byte); +procedure TSimbaImage._DrawTPAAlpha(TPA: TPointArray; Color: TColor; Alpha: Byte); var - R,G,B,A: Byte; + BGRA: TColorBGRA; Point: TPoint; - Ptr: PColorBGRA; begin - R := (Color.R * (255 - Alpha + 1)) shr 8; - G := (Color.G * (255 - Alpha + 1)) shr 8; - B := (Color.B * (255 - Alpha + 1)) shr 8; - A := Alpha + 1; + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; for Point in TPA do if (Point.X >= 0) and (Point.Y >= 0) and (Point.X < FWidth) and (Point.Y < FHeight) then - begin - Ptr := @FData[Point.Y * FWidth + Point.X]; - Ptr^.R := R + Ptr^.R * A shr 8; - Ptr^.G := G + Ptr^.G * A shr 8; - Ptr^.B := B + Ptr^.B * A shr 8; - Ptr^.A := Alpha; - end; + BlendPixel(@FData[Point.Y * FWidth + Point.X], BGRA); end; -procedure TSimbaImage._DrawBoxFilled(Box: TBox; Color: TColor); +procedure TSimbaImage._DrawLine(Start, Stop: TPoint; Color: TColor); var BGRA: TColorBGRA; - Size: Integer; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Pixel(const X, Y: Integer); inline; + begin + if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then + FData[Y * FWidth + X] := BGRA; + end; + + {$i shapebuilder_line.inc} + +begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + _BuildLine(Start, Stop); +end; + +procedure TSimbaImage._DrawLineAlpha(Start, Stop: TPoint; Color: TColor; Alpha: Byte); +var + BGRA: TColorBGRA; + + procedure _Pixel(const X, Y: Integer); + begin + if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then + BlendPixel(@FData[Y * FWidth + X], BGRA); + end; + + {$i shapebuilder_line.inc} + +begin + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; + + _BuildLine(Start, Stop); +end; + +procedure TSimbaImage._DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor); +var + BGRA: TColorBGRA; + + procedure _Pixel(const X, Y: Integer); inline; + begin + if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then + FData[Y * FWidth + X] := BGRA; + end; + + {$i shapebuilder_linegap.inc} + +begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + _BuildLineGap(Start, Stop, GapSize); +end; + +procedure TSimbaImage._DrawLineGapAlpha(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte); +var + BGRA: TColorBGRA; + + procedure _Pixel(const X, Y: Integer); begin - FillData(@FData[Y * FWidth + X1], Size, BGRA); + if (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight) then + BlendPixel(@FData[Y * FWidth + X], BGRA); end; - {$i boxfilled.inc} + {$i shapebuilder_linegap.inc} begin BGRA := Color.ToBGRA(); - Size := (Box.X2 - Box.X1) + 1; + BGRA.A := Alpha; + + _BuildLineGap(Start, Stop, GapSize); +end; + +procedure TSimbaImage._DrawBoxFilled(Box: TBox; Color: TColor); +var + BGRA: TColorBGRA; + + procedure _Row(const Y: Integer; const X1, X2: Integer); + begin + FillData(@FData[Y * FWidth + X1], Box.Width, BGRA); + end; + + {$i shapebuilder_boxfilled.inc} - _BoxFilled(Box); +begin + Box := Box.Clip(TBox.Create(0, 0, FWidth-1, FHeight - 1)); + + if (Box.Width > 1) and (Box.Height > 1) then + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + _BuildBoxFilled(Box); + end; end; procedure TSimbaImage._DrawBoxFilledAlpha(Box: TBox; Color: TColor; Alpha: Byte); var - R,G,B,A: Byte; - Size: Integer; + BGRA: TColorBGRA; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Row(const Y: Integer; const X1, X2: Integer); var Ptr: PColorBGRA; Upper: PtrUInt; begin Ptr := @FData[Y * FWidth + X1]; - Upper := PtrUInt(Ptr) + Size; + Upper := PtrUInt(Ptr) + ((X2 - X1) * SizeOf(TColorBGRA)); while (PtrUInt(Ptr) <= Upper) do begin - Ptr^.R := R + Ptr^.R * A shr 8; - Ptr^.G := G + Ptr^.G * A shr 8; - Ptr^.B := B + Ptr^.B * A shr 8; - Ptr^.A := Alpha; + BlendPixel(Ptr, BGRA); Inc(Ptr); end; end; - {$i boxfilled.inc} + {$i shapebuilder_boxfilled.inc} + +begin + Box := Box.Clip(TBox.Create(0, 0, FWidth - 1, FHeight - 1)); + + if (Box.Width > 1) and (Box.Height > 1) then + begin + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; + + _BuildBoxFilled(Box); + end; +end; + +procedure TSimbaImage._DrawBoxEdge(Box: TBox; Color: TColor); +var + BGRA: TColorBGRA; + + procedure _Row(const Y: Integer; const X1, X2: Integer); + begin + FillData(@FData[Y * FWidth + X1], (X2 - X1) + 1, BGRA); + end; + + {$i shapebuilder_boxedge.inc} begin - R := (Color.R * (255 - Alpha + 1)) shr 8; - G := (Color.G * (255 - Alpha + 1)) shr 8; - B := (Color.B * (255 - Alpha + 1)) shr 8; - A := Alpha + 1; + Box := Box.Clip(TBox.Create(0, 0, FWidth - 1, FHeight - 1)); - Size := (Box.X2 - Box.X1) * SizeOf(TColorBGRA); + if (Box.Width > 1) and (Box.Height > 1) then + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; - _BoxFilled(Box); + _BuildBoxEdge(Box); + end; +end; + +procedure TSimbaImage._DrawBoxEdgeAlpha(Box: TBox; Color: TColor; Alpha: Byte); +var + BGRA: TColorBGRA; + + procedure _Row(const Y: Integer; const X1, X2: Integer); + begin + FillData(@FData[Y * FWidth + X1], (X2 - X1) + 1, BGRA); + end; + + {$i shapebuilder_boxedge.inc} + +begin + Box := Box.Clip(TBox.Create(0, 0, FWidth - 1, FHeight - 1)); + + if (Box.Width > 1) and (Box.Height > 1) then + begin + BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + _BuildBoxEdge(Box); + end; end; procedure TSimbaImage._DrawCircleFilledAlpha(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); var - R,G,B,A: Byte; + BGRA: TColorBGRA; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Row(const Y: Integer; X1, X2: Integer); var Ptr: PColorBGRA; Upper: PtrUInt; @@ -2342,12 +2610,9 @@ procedure TSimbaImage._DrawCircleFilledAlpha(ACenter: TPoint; Radius: Integer; C Ptr := @FData[Y * FWidth + X1]; Upper := PtrUInt(Ptr) + ((X2 - X1) * SizeOf(TColorBGRA)); - while PtrUInt(Ptr) <= Upper do + while (PtrUInt(Ptr) <= Upper) do begin - Ptr^.R := R + Ptr^.R * A shr 8; - Ptr^.G := G + Ptr^.G * A shr 8; - Ptr^.B := B + Ptr^.B * A shr 8; - Ptr^.A := Alpha; + BlendPixel(Ptr, BGRA); Inc(Ptr); end; @@ -2355,22 +2620,20 @@ procedure TSimbaImage._DrawCircleFilledAlpha(ACenter: TPoint; Radius: Integer; C end; end; - {$i circlefilled.inc} + {$i shapebuilder_circlefilled.inc} begin - R := (Color.R * (255 - Alpha + 1)) shr 8; - G := (Color.G * (255 - Alpha + 1)) shr 8; - B := (Color.B * (255 - Alpha + 1)) shr 8; - A := Alpha + 1; + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; - _CircleFilled(ACenter.X, ACenter.Y, Radius); + _BuildCircleFilled(ACenter.X, ACenter.Y, Radius); end; procedure TSimbaImage._DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor); var BGRA: TColorBGRA; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Row(const Y: Integer; X1, X2: Integer); begin if (Y >= 0) and (Y < FHeight) then begin @@ -2381,19 +2644,20 @@ procedure TSimbaImage._DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: end; end; - {$i circlefilled.inc} + {$i shapebuilder_circlefilled.inc} begin BGRA := Color.ToBGRA(); // rgb to bgra + BGRA.A := ALPHA_OPAQUE; - _CircleFilled(ACenter.X, ACenter.Y, Radius); + _BuildCircleFilled(ACenter.X, ACenter.Y, Radius); end; procedure TSimbaImage._DrawPolygonFilledAlpha(Points: TPointArray; Color: TColor; Alpha: Byte); var - R,G,B,A: Byte; + BGRA: TColorBGRA; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Row(const Y: Integer; X1, X2: Integer); var Ptr: PColorBGRA; Upper: PtrUInt; @@ -2407,34 +2671,29 @@ procedure TSimbaImage._DrawPolygonFilledAlpha(Points: TPointArray; Color: TColor Ptr := @FData[Y * FWidth + X1]; Upper := PtrUInt(Ptr) + ((X2 - X1) * SizeOf(TColorBGRA)); - while PtrUInt(Ptr) <= Upper do + while (PtrUInt(Ptr) <= Upper) do begin - Ptr^.R := R + Ptr^.R * A shr 8; - Ptr^.G := G + Ptr^.G * A shr 8; - Ptr^.B := B + Ptr^.B * A shr 8; - Ptr^.A := Alpha; + BlendPixel(Ptr, BGRA); Inc(Ptr); end; end; end; - {$i polygonfilled.inc} + {$i shapebuilder_polygonfilled.inc} begin - R := (Color.R * (255 - Alpha + 1)) shr 8; - G := (Color.G * (255 - Alpha + 1)) shr 8; - B := (Color.B * (255 - Alpha + 1)) shr 8; - A := Alpha + 1; + BGRA := Color.ToBGRA(); + BGRA.A := Alpha; - _PolygonFilled(Points, TRect.Create(0,0,FWidth-1,FHeight-1)); + _BuildPolygonFilled(Points, TRect.Create(0, 0, FWidth-1, FHeight-1)); end; procedure TSimbaImage._DrawPolygonFilled(Points: TPointArray; Color: TColor); var BGRA: TColorBGRA; - procedure _Row(const Y: Integer; X1, X2: Integer); inline; + procedure _Row(const Y: Integer; X1, X2: Integer); begin // Y is already clipped in _PolygonFilled X1 := EnsureRange(X1, 0, FWidth - 1); @@ -2444,12 +2703,63 @@ procedure TSimbaImage._DrawPolygonFilled(Points: TPointArray; Color: TColor); FillData(@FData[Y * FWidth + X1], (X2 - X1) + 1, BGRA); end; - {$i polygonfilled.inc} + {$i shapebuilder_polygonfilled.inc} begin BGRA := Color.ToBGRA(); + BGRA.A := ALPHA_OPAQUE; + + _BuildPolygonFilled(Points, TRect.Create(0, 0, FWidth-1, FHeight-1)); +end; + +procedure TSimbaImage._DrawImage(Image: TSimbaImage; P: TPoint); +var + W, H: Integer; + LoopX, LoopY, DestX, DestY: Integer; + Color: TColorBGRA; +begin + W := Image.Width - 1; + H := Image.Height - 1; + + for LoopY := 0 to H do + for LoopX := 0 to W do + begin + DestX := LoopX + P.X; + DestY := LoopY + P.Y; + if (DestX >= 0) and (DestY >= 0) and (DestX < FWidth) and (DestY < FHeight) then + begin + Color := Image.Data[LoopY * Image.Width + LoopX]; + Color.A := ALPHA_OPAQUE; + + FData[DestY * FWidth + DestX] := Color; + end; + end; +end; + +procedure TSimbaImage._DrawImageAlpha(Image: TSimbaImage; P: TPoint; Alpha: Byte); +var + LoopX, LoopY, DestX, DestY, W, H: Integer; + Color: TColorBGRA; +begin + W := Image.Width - 1; + H := Image.Height - 1; + + for LoopY := 0 to H do + for LoopX := 0 to W do + begin + DestX := LoopX + P.X; + DestY := LoopY + P.Y; - _PolygonFilled(Points, TRect.Create(0,0,FWidth-1,FHeight-1)); + Color := Image.Data[LoopY * Image.Width + LoopX]; + Color.A := Alpha; + + BlendPixel(FData, FWidth, FHeight, DestX, DestY, Color); + end; +end; + +procedure TSimbaImage.RaiseOutOfImageException(X, Y: Integer); +begin + SimbaException('%d,%d is outside the image bounds (0,0,%d,%d)', [X, Y, FWidth-1, FHeight-1]); end; procedure TSimbaImage.NotifyUnfreed; @@ -2458,16 +2768,17 @@ procedure TSimbaImage.NotifyUnfreed; if (SaveUnfreedImages <> '') then try - SaveToFile(IncludeTrailingPathDelimiter(SetDirSeparators(SaveUnfreedImages)) + IntToStr(PtrUInt(Self)) + '.bmp'); + Save(IncludeTrailingPathDelimiter(SetDirSeparators(SaveUnfreedImages)) + IntToStr(PtrUInt(Self)) + '.bmp'); except on E: Exception do DebugLn(E.ToString); end; end; -function TSimbaImage.GetPixel(X, Y: Integer): TColor; +function TSimbaImage.GetPixel(const X, Y: Integer): TColor; begin - AssertInImage('GetPixel', X, Y); + if (X < 0) or (Y < 0) or (X >= FWidth) or (Y >= FHeight) then + RaiseOutOfImageException(X, Y); with FData[Y * FWidth + X] do Result := R or G shl G_BIT or B shl B_BIT; @@ -2475,104 +2786,270 @@ function TSimbaImage.GetPixel(X, Y: Integer): TColor; procedure TSimbaImage.SetPixel(X, Y: Integer; Color: TColor); begin - AssertInImage('SetPixel', X, Y); + if (X < 0) or (Y < 0) or (X >= FWidth) or (Y >= FHeight) then + RaiseOutOfImageException(X, Y); FData[Y * FWidth + X] := Color.ToBGRA(); end; -procedure TSimbaImage.SetTransparentColor(Value: TColor); -begin - FTransparentColor := Value; - FTransparentRGB := Value.ToBGRA(); -end; - function TSimbaImage.InImage(const X, Y: Integer): Boolean; begin Result := (X >= 0) and (Y >= 0) and (X < FWidth) and (Y < FHeight); end; -procedure TSimbaImage.AssertInImage(const Method: String; const X, Y: Integer); -begin - if (X < 0) or (Y < 0) or (X >= FWidth) or (Y >= FHeight) then - SimbaException('TSimbaImage.%s: %d,%d is outside the image bounds: %d,%d,%d,%d', [Method, X, Y, 0,0,FWidth-1,FHeight-1]); -end; +procedure TSimbaImage.DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single); +var + RGB: TColorBGRA; + Alpha: Byte absolute RGB.A; -function TSimbaImage.MatchTemplate(Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; -begin - Result := simba.matchtemplate.MatchTemplate(Self.ToMatrixBGR(), Template.ToMatrixBGR(), Formula); -end; + // https://zingl.github.io/bresenham.js + procedure _LineAntialias(x0, y0, x1, y1: Integer; Thickness: Single); + var + dx, dy, err: Integer; + e2, x2, y2: Integer; + ed: Single; + sx, sy: Integer; + begin + dx := Abs(x1 - x0); + dy := Abs(y1 - y0); -function TSimbaImage.MatchTemplateMask(Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; -begin - Result := simba.matchtemplate.MatchTemplateMask(Self.ToMatrixBGR(), Template.ToMatrixBGR(), Formula); -end; + if (x0 < x1) then sx := 1 else sx := -1; + if (y0 < y1) then sy := 1 else sy := -1; -procedure TSimbaImage.DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single); + err := dx-dy; + if (dx+dy = 0) then + ed := 1 + else + ed := Sqrt(Double(dx*dx) + Double(dy*dy)); - procedure DoClearAlpha; - {$i clearpixelalpha.inc} - {$i lineantialias.inc} - begin - _LineAntialias( - Start.X, Start.Y, - Stop.X, Stop.Y, - Thickness - ); - end; + Thickness := (Thickness + 1) / 2; + while True do + begin + Alpha := Round(255-Max(0, 255 * (Abs(err-dx+dy)/ed-Thickness+1))); + BlendPixel(FData, FWidth, FHeight, x0, y0, RGB); - procedure DoDraw; - var - BGRA: TColorBGRA; + e2 := err; + x2 := x0; + if (2*e2 >= -dx) then + begin + e2 += dy; + y2 := y0; + while (e2 < ed*Thickness) and ((y1 <> y2) or (dx > dy)) do + begin + y2 += sy; - {$i setpixelantialias.inc} - {$i lineantialias.inc} - begin - BGRA := Color.ToBGRA(); + Alpha := Round(255-Max(0, 255 * (Abs(e2)/ed-Thickness+1))); + BlendPixel(FData, FWidth, FHeight, x0, y2, RGB); + + e2 += dx; + end; + if (x0 = x1) then + Break; + + e2 := err; + err -= dy; + x0 += sx; + end; + + if (2*e2 <= dy) then + begin + e2 := dx-e2; + while (e2 < ed*Thickness) and ((x1 <> x2) or (dx < dy)) do + begin + x2 += sx; + + Alpha := Round(255-Max(0, 255 * (Abs(e2)/ed-Thickness+1))); + BlendPixel(FData, FWidth, FHeight, x2, y0, RGB); + + e2 += dy; + end; + if (y0 = y1) then + Break; - _LineAntialias( - Start.X, Start.Y, - Stop.X, Stop.Y, - Thickness - ); + err += dx; + y0 += sy; + end; + end; end; begin - DoClearAlpha(); - DoDraw(); + RGB := Color.ToBGRA(); + + _LineAntialias( + Start.X, Start.Y, + Stop.X, Stop.Y, + Thickness + ); end; procedure TSimbaImage.DrawEllipseAA(ACenter: TPoint; XRadius, YRadius: Integer; Color: TColor; Thickness: Single); +var + RGB: TColorBGRA; + Alpha: Byte absolute RGB.A; - procedure DoClearAlpha; - {$i clearpixelalpha.inc} - {$i ellipseantialias.inc} + // https://zingl.github.io/bresenham.js + procedure _EllipseAntialias(x0, y0, x1, y1: Integer; Thickness: Single); + var + a,b,b1: Integer; + a2,b2: Single; + dx,dy: Single; + err: Single; + dx2,dy2,e2,ed: Single; + i: Single; begin - _EllipseAntialias( - ACenter.X - XRadius, ACenter.Y - YRadius, - ACenter.X + XRadius, ACenter.Y + YRadius, - Thickness - ); - end; + a := Abs(x1 - x0); + b := Abs(y1 - y0); + if (a = 0) or (b = 0) then + Exit; - procedure DoDraw; - var - BGRA: TColorBGRA; + b1 := b and 1; + a2 := a-2*Thickness; + b2 := b-2*Thickness; + dx := 4*(a-1)*b*b; + dy := 4*(b1-1)*a*a; - {$i setpixelantialias.inc} - {$i ellipseantialias.inc} - begin - BGRA := Color.ToBGRA(); + i := a+b2; + err := b1*a*a; + + if ((Thickness-1) * (2*b-Thickness) > a*a) then + b2 := Sqrt(a*(b-a)*i*a2) / (a-Thickness); + + if ((Thickness-1) * (2*a-Thickness) > b*b) then + begin + a2 := Sqrt(b*(a-b)*i*b2) / (b-Thickness); + Thickness := (a-a2) / 2; + end; + + if (x0 > x1) then + begin + x0 := x1; + x1 += a; + end; + + if (y0 > y1) then + y0 := y1; + + if (b2 <= 0) then + Thickness := a; + + e2 := Thickness - Floor(Thickness); + Thickness := x0+Thickness-e2; + dx2 := 4*(a2+2*e2-1)*b2*b2; + dy2 := 4*(b1-1)*a2*a2; + e2 := dx2*e2; + y0 += (b+1) shr 1; + y1 := y0-b1; + a := 8*a*a; + b1 := 8*b*b; + a2 := 8*a2*a2; + b2 := 8*b2*b2; + + repeat + while True do + begin + if (err < 0) or (x0 > x1) then + begin + i := x0; + Break; + end; + + i := Min(dx,dy); + ed := Max(dx,dy); + + if ((y0 = y1+1) and (2*err > dx) and (a > b1)) then + ed := a/4 + else + ed += 2*ed*i*i/(4*ed*ed+i*i+1)+1; + i := 255*err/ed; + + Alpha := 255-Byte(Round(i)); + + BlendPixel(FData, FWidth, FHeight, x0, y0, RGB); + BlendPixel(FData, FWidth, FHeight, x0, y1, RGB); + BlendPixel(FData, FWidth, FHeight, x1, y0, RGB); + BlendPixel(FData, FWidth, FHeight, x1, y1, RGB); + + if (err+dy+a < dx) then + begin + i := x0+1; + Break; + end; + + x0 += 1; + x1 -= 1; + err -= dx; + dx -= b1; + end; + + Alpha := 255; + + while (i < Thickness) and (2*i <= x0+x1) do + begin + BlendPixel(FData, FWidth, FHeight, Round(i), y0, RGB); + BlendPixel(FData, FWidth, FHeight, Round(x0+x1-i), y0, RGB); + BlendPixel(FData, FWidth, FHeight, Round(i), y1, RGB); + BlendPixel(FData, FWidth, FHeight, Round(x0+x1-i), y1, RGB); + + i += 1.0; + end; + + while ((e2 > 0) and (x0+x1 >= 2*Thickness)) do + begin + i := Min(dx2, dy2); + ed := Max(dx2, dy2); + + if (y0 = y1+1) and (2*e2 > dx2) and (a2 > b2) then + ed := a2/4 + else + ed += 2*ed*i*i/(4*ed*ed+i*i); + + Alpha := 255-Byte(Round(255-255*e2/ed)); + + BlendPixel(FData, FWidth, FHeight, Round(Thickness), y0, RGB); + BlendPixel(FData, FWidth, FHeight, Round(x0+x1-Thickness), y0, RGB); + BlendPixel(FData, FWidth, FHeight, Round(Thickness), y1, RGB); + BlendPixel(FData, FWidth, FHeight, Round(x0+x1-Thickness), y1, RGB); - _EllipseAntialias( - ACenter.X - XRadius, ACenter.Y - YRadius, - ACenter.X + XRadius, ACenter.Y + YRadius, - Thickness - ); + if (e2+dy2+a2 < dx2) then + Break; + + Thickness += 1; + e2 -= dx2; + dx2 -= b2; + end; + + dy2 += a2; + e2 += dy2; + y0 += 1; + y1 -= 1; + dy += a; + err += dy; + until (x0 >= x1); + + while (y0-y1 <= b) do + begin + Alpha := 255 - Byte(Round(255*4*err/b1)); + + BlendPixel(FData, FWidth, FHeight, x0, y0, RGB); + BlendPixel(FData, FWidth, FHeight, x1, y0, RGB); + BlendPixel(FData, FWidth, FHeight, x0, y1, RGB); + BlendPixel(FData, FWidth, FHeight, x1, y1, RGB); + + y0 += 1; + y1 -= 1; + dy += a; + err += dy; + end; end; begin - DoClearAlpha(); - DoDraw(); + RGB := Color.ToBGRA(); + + _EllipseAntialias( + ACenter.X - XRadius, ACenter.Y - YRadius, + ACenter.X + XRadius, ACenter.Y + YRadius, + Thickness + ); end; procedure TSimbaImage.DrawCircleAA(ACenter: TPoint; Radius: Integer; Color: TColor; Thickness: Single); @@ -2587,8 +3064,8 @@ constructor TSimbaImage.Create; FDataOwner := True; FTextDrawer := TSimbaTextDrawer.Create(Self); - TransparentColorActive := True; - TransparentColor := 0; + DefaultPixel.AsInteger := 0; + DefaultPixel.A := ALPHA_OPAQUE; end; constructor TSimbaImage.Create(AWidth, AHeight: Integer); @@ -2602,7 +3079,7 @@ constructor TSimbaImage.CreateFromFile(FileName: String); begin Create(); - LoadFromFile(FileName); + Load(FileName); end; constructor TSimbaImage.CreateFromZip(ZipFileName, ZipEntryName: String); @@ -2613,7 +3090,7 @@ constructor TSimbaImage.CreateFromZip(ZipFileName, ZipEntryName: String); if ZipExtractOne(ZipFileName, ZipEntryName, Stream) then try - LoadFromStream(Stream, ZipEntryName); + FromStream(Stream, ZipEntryName); finally Stream.Free(); end; @@ -2623,14 +3100,14 @@ constructor TSimbaImage.CreateFromString(Str: String); begin Create(); - LoadFromString(Str); + FromString(Str); end; constructor TSimbaImage.CreateFromData(AWidth, AHeight: Integer; AData: PColorBGRA; ADataWidth: Integer); begin Create(); - LoadFromData(AWidth, AHeight, AData, ADataWidth); + FromData(AWidth, AHeight, AData, ADataWidth); end; constructor TSimbaImage.CreateFromWindow(Window: TWindowHandle); @@ -2643,7 +3120,7 @@ constructor TSimbaImage.CreateFromWindow(Window: TWindowHandle); if SimbaNativeInterface.GetWindowBounds(Window, B) and SimbaNativeInterface.GetWindowImage(Window, 0, 0, B.Width - 1, B.Height - 1, ImageData) then try - LoadFromData(B.Width - 1, B.Height - 1, ImageData, B.Width - 1); + FromData(B.Width - 1, B.Height - 1, ImageData, B.Width - 1); finally FreeMem(ImageData); end; @@ -2654,8 +3131,7 @@ destructor TSimbaImage.Destroy; if FDataOwner then SetSize(0, 0); - if (FTextDrawer <> nil) then - FreeAndNil(FTextDrawer); + FreeAndNil(FTextDrawer); inherited Destroy(); end; diff --git a/Source/image/simba.image_gaussblur.pas b/Source/image/simba.image_gaussblur.pas index 1c15e6c02..55aa524e8 100644 --- a/Source/image/simba.image_gaussblur.pas +++ b/Source/image/simba.image_gaussblur.pas @@ -1,4 +1,7 @@ // https://blog.ivank.net/fastest-gaussian-blur.html +// Olly: +// Hacked in `ignore` param to ignore a pixel, seems to work at first glance. +// This does mean the edges will be sharp, but might be useful. unit simba.image_gaussblur; {$i simba.inc} @@ -9,7 +12,7 @@ interface Classes, SysUtils, simba.base; -procedure imgGaussBlur(radius: Double; var r,g,b: TByteArray; width, height: Integer); +procedure imgGaussBlur(radius: Double; var r,g,b: TByteArray; ignore: TBooleanArray; width, height: Integer); implementation @@ -42,7 +45,7 @@ function boxesForGauss(sigma: Double; n: Integer): TIntegerArray; end; end; -procedure boxBlurH_4(var scl, tcl: TByteArray; w, h: Integer; r: Integer); +procedure boxBlurH_4(var scl, tcl: TByteArray; ignore: TBooleanArray; w, h: Integer; r: Integer); var iarr: Double; i,j: Integer; @@ -54,6 +57,9 @@ procedure boxBlurH_4(var scl, tcl: TByteArray; w, h: Integer; r: Integer); for i := 0 to h-1 do begin ti := i * w; + if (ignore[ti]) then + Continue; + li := ti; ri := Round(ti + r); fv := scl[ti]; @@ -90,7 +96,7 @@ procedure boxBlurH_4(var scl, tcl: TByteArray; w, h: Integer; r: Integer); end; end; -procedure boxBlurT_4(var scl, tcl: TByteArray; w, h: Integer; r: Integer); +procedure boxBlurT_4(var scl, tcl: TByteArray; ignore: TBooleanArray; w, h: Integer; r: Integer); var iarr: Double; i,j: Integer; @@ -138,25 +144,22 @@ procedure boxBlurT_4(var scl, tcl: TByteArray; w, h: Integer; r: Integer); end; end; -procedure boxBlur_4(var scl, tcl: TByteArray; w,h: Integer; r: Integer); -var - i: Integer; +procedure boxBlur_4(var scl, tcl: TByteArray; skip: TBooleanArray; w,h: Integer; r: Integer); begin - for i:=0 to High(scl) do - tcl[i] := scl[i]; + Move(scl[0], tcl[0], Length(scl) * SizeOf(Byte)); - boxBlurH_4(tcl, scl, w, h, r); - boxBlurT_4(scl, tcl, w, h, r); + boxBlurH_4(tcl, scl, skip, w, h, r); + boxBlurT_4(scl, tcl, skip, w, h, r); end; -procedure gaussBlur_4(var scl, tcl: TByteArray; w,h: Integer; r: Double; bxs: TIntegerArray); +procedure gaussBlur_4(var scl, tcl: TByteArray; skip: TBooleanArray; w,h: Integer; r: Double; bxs: TIntegerArray); begin - boxBlur_4(scl, tcl, w, h, (bxs[0] - 1) div 2); - boxBlur_4(tcl, scl, w, h, (bxs[1] - 1) div 2); - boxBlur_4(scl, tcl, w, h, (bxs[2] - 1) div 2); + boxBlur_4(scl, tcl, skip, w, h, (bxs[0] - 1) div 2); + boxBlur_4(tcl, scl, skip, w, h, (bxs[1] - 1) div 2); + boxBlur_4(scl, tcl, skip, w, h, (bxs[2] - 1) div 2); end; -procedure imgGaussBlur(radius: Double; var r, g, b: TByteArray; width, height: Integer); +procedure imgGaussBlur(radius: Double; var r, g, b: TByteArray; ignore: TBooleanArray; width, height: Integer); var outR, outG, outB: TByteArray; boxes: TIntegerArray; @@ -167,9 +170,9 @@ procedure imgGaussBlur(radius: Double; var r, g, b: TByteArray; width, height: I boxes := boxesForGauss(radius, 3); - gaussBlur_4(r, outR, width, height-1, radius, boxes); - gaussBlur_4(g, outG, width, height, radius, boxes); - gaussBlur_4(b, outB, width, height, radius, boxes); + gaussBlur_4(r, outR, ignore, width, height, radius, boxes); + gaussBlur_4(g, outG, ignore, width, height, radius, boxes); + gaussBlur_4(b, outB, ignore, width, height, radius, boxes); r := outR; g := outG; diff --git a/Source/image/simba.image_lazbridge.pas b/Source/image/simba.image_lazbridge.pas index bd887f072..b4d7f891e 100644 --- a/Source/image/simba.image_lazbridge.pas +++ b/Source/image/simba.image_lazbridge.pas @@ -16,10 +16,19 @@ interface Classes, SysUtils, Graphics, GraphType, IntfGraphics, FPImage, simba.base, simba.image; +{$scopedenums on} +type + ELazPixelFormat = (UNKNOWN, BGR, BGRA, RGB, RGBA, ARGB); +{$scopedenums off} + +procedure LazImage_CopyRow_BGR(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorBGR); inline; +procedure LazImage_CopyRow_ARGB(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorARGB); inline; +procedure LazImage_CopyRow_BGRA(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorBGRA); inline; + procedure LazImage_FromData(LazImage: TBitmap; Data: PColorBGRA; Width, Height: Integer); procedure LazImage_FromSimbaImage(LazImage: TBitmap; SimbaImage: TSimbaImage); function LazImage_ToSimbaImage(LazImage: TBitmap): TSimbaImage; -function LazImage_PixelFormat(LazImage: TBitmap): String; +function LazImage_PixelFormat(LazImage: TBitmap): ELazPixelFormat; procedure SimbaImage_ToFPImageWriter(SimbaImage: TSimbaImage; WriterClass: TFPCustomImageWriterClass; Stream: TStream); procedure SimbaImage_FromFPImageReader(SimbaImage: TSimbaImage; ReaderClass: TFPCustomImageReaderClass; Stream: TStream); @@ -28,49 +37,61 @@ function SimbaImage_ToLazImage(SimbaImage: TSimbaImage): TBitmap; implementation +uses + TypInfo; + var SimbaRawImgDescription: TRawImageDescription; +procedure LazImage_CopyRow_BGR(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorBGR); +begin + while (PtrUInt(Source) < SourceUpper) do + begin + PColorBGR(Dest)^ := PColorBGR(Source)^; // Can just use first three bytes + + Inc(Source); + Inc(Dest); + end; +end; + +procedure LazImage_CopyRow_ARGB(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorARGB); +begin + while (PtrUInt(Source) < SourceUpper) do + begin + PUInt32(Dest)^ := SwapEndian(PUInt32(Source)^); // reverse the bytes + + Inc(Source); + Inc(Dest); + end; +end; + +procedure LazImage_CopyRow_BGRA(Source: PColorBGRA; SourceUpper: PtrUInt; Dest: PColorBGRA); +begin + Move(Source^, Dest^, SourceUpper - PtrUInt(Source)); // same formats, copy whole row +end; + procedure LazImage_FromData(LazImage: TBitmap; Data: PColorBGRA; Width, Height: Integer); var Source, Dest: PByte; SourceBytesPerLine, DestBytesPerLine: Integer; - Upper: PtrUInt; + SourceUpper: PtrUInt; - procedure BGR; - var - RowUpper: PtrUInt; - RowSource, RowDest: PByte; + procedure BGRA; begin - while (PtrUInt(Source) < Upper) do + while (PtrUInt(Source) < SourceUpper) do begin - RowSource := Source; - RowDest := Dest; - RowUpper := PtrUInt(Source + SourceBytesPerLine); - - while (PtrUInt(RowSource) < RowUpper) do - begin - PColorRGB(RowDest)^ := PColorRGB(RowSource)^; // Can just use first three bytes - - Inc(RowSource, SizeOf(TColorBGRA)); - Inc(RowDest, SizeOf(TColorRGB)); - end; + LazImage_CopyRow_BGRA(PColorBGRA(Source), PtrUInt(Source + SourceBytesPerLine), PColorBGRA(Dest)); Inc(Source, SourceBytesPerLine); Inc(Dest, DestBytesPerLine); end; end; - procedure BGRA; - var - RowSource, RowDest: PByte; + procedure BGR; begin - while (PtrUInt(Source) < Upper) do + while (PtrUInt(Source) < SourceUpper) do begin - RowSource := Source; - RowDest := Dest; - - Move(RowSource^, RowDest^, DestBytesPerLine); + LazImage_CopyRow_BGR(PColorBGRA(Source), PtrUInt(Source + SourceBytesPerLine), PColorBGR(Dest)); Inc(Source, SourceBytesPerLine); Inc(Dest, DestBytesPerLine); @@ -78,23 +99,10 @@ procedure LazImage_FromData(LazImage: TBitmap; Data: PColorBGRA; Width, Height: end; procedure ARGB; - var - RowUpper: PtrUInt; - RowSource, RowDest: PByte; begin - while (PtrUInt(Source) < Upper) do + while (PtrUInt(Source) < SourceUpper) do begin - RowSource := Source; - RowDest := Dest; - RowUpper := PtrUInt(Source + SourceBytesPerLine); - - while PtrUInt(RowSource) < RowUpper do - begin - PUInt32(RowDest)^ := SwapEndian(PUInt32(RowSource)^); - - Inc(RowSource, SizeOf(TColorBGRA)); - Inc(RowDest, SizeOf(TColorARGB)); - end; + LazImage_CopyRow_ARGB(PColorBGRA(Source), PtrUInt(Source + SourceBytesPerLine), PColorARGB(Dest)); Inc(Source, SourceBytesPerLine); Inc(Dest, DestBytesPerLine); @@ -110,13 +118,14 @@ procedure LazImage_FromData(LazImage: TBitmap; Data: PColorBGRA; Width, Height: Source := PByte(Data); SourceBytesPerLine := Width * SizeOf(TColorBGRA); - - Upper := PtrUInt(Source + (SourceBytesPerLine * Height)); + SourceUpper := PtrUInt(Source + (SourceBytesPerLine * Height)); case LazImage_PixelFormat(LazImage) of - 'BGR': BGR(); - 'BGRA': BGRA(); - 'ARGB': ARGB(); + ELazPixelFormat.BGR: BGR(); + ELazPixelFormat.BGRA: BGRA(); + ELazPixelFormat.ARGB: ARGB(); + else + SimbaException('not supported'); end; LazImage.EndUpdate(); @@ -210,43 +219,59 @@ function LazImage_ToSimbaImage(LazImage: TBitmap): TSimbaImage; Upper := PtrUInt(Source + (SourceBytesPerLine * LazImage.Height)); case LazImage_PixelFormat(LazImage) of - 'BGR': BGR(); - 'BGRA': BGRA(); - 'ARGB': ARGB(); + ELazPixelFormat.BGR: BGR(); + ELazPixelFormat.BGRA: BGRA(); + ELazPixelFormat.ARGB: ARGB(); + else + SimbaException('Not supported'); end; end; -function LazImage_PixelFormat(LazImage: TBitmap): String; +function LazImage_PixelFormat(LazImage: TBitmap): ELazPixelFormat; - function isARGB: Boolean; + function isRGBA: Boolean; begin - // ARGB or ARGB but alpha is not used with LazImage.RawImage.Description do - Result := ((BitsPerPixel = 32) and (AlphaPrec = 8) and (((AlphaShift = 0) and (RedShift = 8) and (GreenShift = 16) and (BlueShift = 24) and (ByteOrder = riboLSBFirst)) or ((AlphaShift = BitsPerPixel - 8) and (RedShift = BitsPerPixel - 16) and (GreenShift = BitsPerPixel - 24) and (BlueShift = BitsPerPixel - 32) and (ByteOrder = riboMSBFirst)))) or - ((BitsPerPixel = 32) and (AlphaPrec = 0) and (((RedShift = 8) and (GreenShift = 16) and (BlueShift = 24) and (ByteOrder = riboLSBFirst)) or ((RedShift = BitsPerPixel - 16) and (GreenShift = BitsPerPixel - 24) and (BlueShift = BitsPerPixel - 32) and (ByteOrder = riboMSBFirst)))); + Result := (BitsPerPixel = 32) and (Depth = 32) and (RedShift = 0) and (GreenShift = 8) and (BlueShift = 16) and (AlphaShift = 24); end; function isBGRA: Boolean; begin with LazImage.RawImage.Description do - Result := ((BitsPerPixel = 32) and (((BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (ByteOrder = riboLSBFirst)) or ((BlueShift = BitsPerPixel - 8) and (GreenShift = BitsPerPixel - 16) and (RedShift = BitsPerPixel - 24) and (ByteOrder = riboMSBFirst)))); + Result := ((BitsPerPixel = 32) and (Depth = 32) and (ByteOrder = riboLSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (AlphaShift = 24)) or + ((BitsPerPixel = 32) and (Depth = 24) and (ByteOrder = riboLSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (AlphaShift = 0)); // 32bit but alpha not used + end; + + function isARGB: Boolean; + begin + with LazImage.RawImage.Description do + Result := ((BitsPerPixel = 32) and (Depth = 32) and (ByteOrder = riboMSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (AlphaShift = 24)) or + ((BitsPerPixel = 32) and (Depth = 24) and (ByteOrder = riboMSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (AlphaShift = 0)); // 32bit but alpha not used end; function isBGR: Boolean; begin with LazImage.RawImage.Description do - Result := ((BitsPerPixel = 24) and (((BlueShift = 0) and (GreenShift = 8) and (RedShift = 16) and (ByteOrder = riboLSBFirst)) or ((BlueShift = BitsPerPixel - 8) and (GreenShift = BitsPerPixel - 16) and (RedShift = BitsPerPixel - 24) and (ByteOrder = riboMSBFirst)))); + Result := (BitsPerPixel = 24) and (Depth = 24) and (ByteOrder = riboLSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16); + end; + + function isRGB: Boolean; + begin + with LazImage.RawImage.Description do + Result := (BitsPerPixel = 24) and (Depth = 24) and (ByteOrder = riboMSBFirst) and (BlueShift = 0) and (GreenShift = 8) and (RedShift = 16); end; var ChannelCount: Integer; begin + Result := ELazPixelFormat.UNKNOWN; + with LazImage.RawImage.Description do begin if ((BitsPerPixel and 7) <> 0) then - SimbaException('%d bit per pixel found but multiple of 8bit expected', [BitsPerPixel]); - if (BitsPerPixel < 24) then - SimbaException('%d bit per pixel found but at least 24bit expected', [BitsPerPixel]); + SimbaException('LazImage_PixelFormat: %d BitsPerPixel found but expected multiple of 8', [BitsPerPixel]); + if (BitsPerPixel < 24) or (BitsPerPixel > 32) then + SimbaException('LazImage_PixelFormat: %d BitsPerPixel found but expected 24..32', [BitsPerPixel]); ChannelCount := 0; if (RedPrec > 0) then Inc(ChannelCount); @@ -254,23 +279,22 @@ function LazImage_PixelFormat(LazImage: TBitmap): String; if (BluePrec > 0) then Inc(ChannelCount); if (ChannelCount < 3) then - SimbaException('%d color channels found but 3 or 4 expected.', [ChannelCount]); + SimbaException('LazImage_PixelFormat: %d channels found but 3 or 4 expected.', [ChannelCount]); - if isARGB() then - Result := 'ARGB' - else - if isBGRA() then - Result := 'BGRA' - else - if isBGR() then - Result := 'BGR' + if isRGBA() then Result := ELazPixelFormat.RGBA + else if isBGRA() then Result := ELazPixelFormat.BGRA + else if isARGB() then Result := ELazPixelFormat.ARGB + else if isBGR() then Result := ELazPixelFormat.BGR + else if isRGB() then Result := ELazPixelFormat.RGB else SimbaException( - 'Pixel format not supported: ' + - 'BitsPerPixel: ' + IntToStr(BitsPerPixel) + ', ' + - 'RedShit: ' + IntToStr(RedShift) + ', Prec: ' + IntToStr(RedPrec) + ', ' + - 'GreenShit: ' + IntToStr(GreenShift) + ', Prec: ' + IntToStr(GreenPrec) + ', ' + - 'BlueShift: ' + IntToStr(BlueShift) + ', Prec: ' + IntToStr(BluePrec) + ', ' + + 'LazImage_PixelFormat: Pixel format not supported.' + + 'ByteOrder: ' + GetEnumName(TypeInfo(TRawImageByteOrder), Integer(ByteOrder)) + ', ' + + 'Depth: ' + IntToStr(Depth) + ', ' + + 'BitsPerPixel: ' + IntToStr(BitsPerPixel) + ', ' + + 'RedShit: ' + IntToStr(RedShift) + ', Prec: ' + IntToStr(RedPrec) + ', ' + + 'GreenShit: ' + IntToStr(GreenShift) + ', Prec: ' + IntToStr(GreenPrec) + ', ' + + 'BlueShift: ' + IntToStr(BlueShift) + ', Prec: ' + IntToStr(BluePrec) + ', ' + 'AlphaShift: ' + IntToStr(AlphaShift) + ', Prec: ' + IntToStr(AlphaPrec) ); end; @@ -278,76 +302,73 @@ function LazImage_PixelFormat(LazImage: TBitmap): String; procedure SimbaImage_ToFPImageWriter(SimbaImage: TSimbaImage; WriterClass: TFPCustomImageWriterClass; Stream: TStream); var - Writer: TFPCustomImageWriter; Img: TLazIntfImage; + Writer: TFPCustomImageWriter; begin Img := nil; Writer := nil; - try + Writer := WriterClass.Create(); Img := TLazIntfImage.Create(SimbaImage_ToRawImage(SimbaImage), False); - Writer := WriterClass.Create(); Writer.ImageWrite(Stream, Img); finally if Assigned(Img) then Img.Free(); - if Assigned(Writer) then - Writer.Free(); end; end; procedure SimbaImage_FromFPImageReader(SimbaImage: TSimbaImage; ReaderClass: TFPCustomImageReaderClass; Stream: TStream); var - Reader: TFPCustomImageReader; Img: TLazIntfImage; + Reader: TFPCustomImageReader; begin Img := nil; Reader := nil; - try + Reader := ReaderClass.Create(); + Img := TLazIntfImage.Create(0, 0); Img.DataDescription := SimbaRawImgDescription; - Reader := ReaderClass.Create(); Reader.ImageRead(Stream, Img); - SimbaImage.LoadFromData(Img.Width, Img.Height, PColorBGRA(Img.PixelData), Img.Width); + SimbaImage.FromData(Img.Width, Img.Height, PColorBGRA(Img.PixelData), Img.Width); finally - if Assigned(Img) then - Img.Free(); - if Assigned(Reader) then - Reader.Free(); + Img.Free(); + Reader.Free(); end; end; function SimbaImage_ToRawImage(SimbaImage: TSimbaImage): TRawImage; begin Result.Init(); - - Result.Description.PaletteColorCount := 0; - Result.Description.MaskBitsPerPixel := 0; - Result.Description.Width := SimbaImage.Width; - Result.Description.Height := SimbaImage.Height; - - Result.Description.Format := ricfRGBA; - Result.Description.ByteOrder := riboLSBFirst; - Result.Description.BitOrder := riboBitsInOrder; // should be fine - Result.Description.Depth := 24; - Result.Description.BitsPerPixel := 32; - Result.Description.LineOrder := riloTopToBottom; - Result.Description.LineEnd := rileDWordBoundary; - - Result.Description.RedPrec := 8; - Result.Description.GreenPrec := 8; - Result.Description.BluePrec := 8; - Result.Description.AlphaPrec := 0; - - Result.Description.RedShift := 16; - Result.Description.GreenShift := 8; - Result.Description.BlueShift := 0; - - Result.DataSize := Result.Description.Width * Result.Description.Height * (Result.Description.BitsPerPixel shr 3); + Result.Description.Init_BPP32_B8G8R8A8_BIO_TTB(SimbaImage.Width, SimbaImage.Height); + + //Result.Description.PaletteColorCount := 0; + //Result.Description.MaskBitsPerPixel := 0; + //Result.Description.Width := SimbaImage.Width; + //Result.Description.Height := SimbaImage.Height; + // + //Result.Description.Format := ricfRGBA; + //Result.Description.ByteOrder := riboLSBFirst; + //Result.Description.BitOrder := riboBitsInOrder; // should be fine + //Result.Description.Depth := 32; + //Result.Description.BitsPerPixel := 32; + //Result.Description.LineOrder := riloTopToBottom; + //Result.Description.LineEnd := rileDWordBoundary; + // + //Result.Description.RedPrec := 8; + //Result.Description.GreenPrec := 8; + //Result.Description.BluePrec := 8; + //Result.Description.AlphaPrec := 8; + // + //Result.Description.AlphaShift := 24; + //Result.Description.RedShift := 16; + //Result.Description.GreenShift := 8; + //Result.Description.BlueShift := 0; + + Result.DataSize := Result.Description.Width * Result.Description.Height * SizeOf(TColorBGRA); Result.Data := PByte(SimbaImage.Data); end; @@ -359,7 +380,7 @@ function SimbaImage_ToLazImage(SimbaImage: TSimbaImage): TBitmap; end; initialization - SimbaRawImgDescription.Init_BPP32_B8G8R8_BIO_TTB(0, 0); // TColorBGRA format + SimbaRawImgDescription.Init_BPP32_B8G8R8A8_BIO_TTB(0, 0); // TColorBGRA format end. diff --git a/Source/image/simba.image_stringconv.pas b/Source/image/simba.image_stringconv.pas new file mode 100644 index 000000000..0494b287b --- /dev/null +++ b/Source/image/simba.image_stringconv.pas @@ -0,0 +1,156 @@ +{ + Author: Raymond van Venetië and Merlijn Wajer + Project: Simba (https://github.com/MerlijnWajer/Simba) + License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) + -------------------------------------------------------------------------- + + Save/Load bitmaps from/to a base64 string. + + Channels (B,G,R,A) are split and compressed with zlib individually for better compression. +} +unit simba.image_stringconv; + +{$i simba.inc} + +interface + +uses + Classes, SysUtils, + simba.base, simba.image; + +const + HeaderPrefix = 'IMG:'; + +type + TImageStringHeader = packed record + Version: Integer; + Width, Height: Integer; + ChannelSize: record + B,G,R,A: Integer; + end; + Name: String[128]; + end; + +procedure SimbaImage_FromString(Image: TSimbaImage; Str: String); +function SimbaImage_ToString(Image: TSimbaImage): String; + +implementation + +uses + ZStream, + simba.encoding; + +procedure SimbaImage_FromString(Image: TSimbaImage; Str: String); + + procedure DecompressChannel(SourceStream: TStream; SourceSize: Integer; ChannelOffset: Integer); + var + Stream: TMemoryStream; + DecompressStream: Tdecompressionstream; + Buffer: array[0.. 16384-1] of Byte; + I, Count, DataIndex: Integer; + begin + Stream := nil; + DecompressStream := nil; + try + Stream := TMemoryStream.Create(); + Stream.CopyFrom(SourceStream, SourceSize); + Stream.Position := 0; + + DecompressStream := Tdecompressionstream.create(Stream); + + DataIndex := 0; + repeat + Count := DecompressStream.Read(Buffer[0], Length(Buffer)); + for I := 0 to Count - 1 do + PByte(@Image.Data[DataIndex + I].B + ChannelOffset)^ := Buffer[I]; + Inc(DataIndex, Count); + until (Count = 0); + finally + Stream.Free(); + DecompressStream.Free(); + end; + end; + +var + Stream: TStringStream; + Header: TImageStringHeader; +begin + if not Str.StartsWith(HeaderPrefix, True) then + SimbaException('TImage.LoadFromString: Invalid string, it should begin with "IMG:"'); + Str := Str.After(HeaderPrefix); + + Stream := nil; + try + Stream := TStringStream.Create(Base64Decode(Str)); + Stream.Read(Header, SizeOf(TImageStringHeader)); + + Image.SetSize(Header.Width, Header.Height); + Image.Name := Header.Name; + + DecompressChannel(Stream, Header.ChannelSize.B, 0); + DecompressChannel(Stream, Header.ChannelSize.G, 1); + DecompressChannel(Stream, Header.ChannelSize.R, 2); + DecompressChannel(Stream, Header.ChannelSize.A, 3); + finally + if (Stream <> nil) then + FreeAndNil(Stream); + end; +end; + +function SimbaImage_ToString(Image: TSimbaImage): String; + + procedure CompressChannel(ChannelOffset: Byte; DestStream: TStream; out Count: Integer); + var + Channel: TByteArray; + I: Integer; + CompressionStream: Tcompressionstream; + begin + CompressionStream := nil; + + Count := DestStream.Position; + try + CompressionStream := Tcompressionstream.create(clDefault, DestStream); + + SetLength(Channel, Image.Width * Image.Height); + for I := 0 to High(Channel) do + Channel[I] := PByte(@Image.Data[I].B + ChannelOffset)^; + + CompressionStream.Write(Channel[0], Length(Channel)); + CompressionStream.Flush(); + finally + CompressionStream.Free(); + end; + + Count := DestStream.Position - Count; + end; + +var + Stream: TStringStream; + Header: TImageStringHeader; +begin + Stream := nil; + try + Stream := TStringStream.Create(); + Stream.Write(Header{%H-}, SizeOf(TImageStringHeader)); // write header last, but reserve space + + CompressChannel(0, Stream, Header.ChannelSize.B); + CompressChannel(1, Stream, Header.ChannelSize.G); + CompressChannel(2, Stream, Header.ChannelSize.R); + CompressChannel(3, Stream, Header.ChannelSize.A); + + Header.Version := 1; + Header.Width := Image.Width; + Header.Height := Image.Height; + Header.Name := Image.Name; + + Stream.Position := 0; + Stream.Write(Header, SizeOf(TImageStringHeader)); + + Result := HeaderPrefix + Base64Encode(Stream.DataString); + finally + Stream.Free(); + end; +end; + +end. + diff --git a/Source/image/simba.image_textdrawer.pas b/Source/image/simba.image_textdrawer.pas index 5ff559d9a..ffc3f4959 100644 --- a/Source/image/simba.image_textdrawer.pas +++ b/Source/image/simba.image_textdrawer.pas @@ -29,18 +29,26 @@ TFontCacheEntry = record FSystemFontsLoaded: Boolean; procedure LoadSystemFonts; + + function GetFontNames: TStringArray; public function LoadFonts(Dir: String): Boolean; function GetFont(AName: String; ASize: Single; AAntialised, ABold, AItalic: Boolean): TFreeTypeFont; - property FontNames: TStringArray read FFontNames; + property FontNames: TStringArray read GetFontNames; end; + {$scopedenums on} + ETextDrawAlign = (LEFT, CENTER, RIGHT, JUSTIFY, TOP, VERTICAL_CENTER, BASE_LINE, BOTTOM); + ETextDrawAlignSet = set of ETextDrawAlign; + {$scopedenums off} + TSimbaTextDrawer = class(TFPImageFreeTypeDrawer) protected FWidth: Integer; FHeight: Integer; FData: PColorBGRA; + FCurrentX, FCurrentY: Integer; FCurrentColor: PColorBGRA; FSimbaImage: TObject; FFonts: TStringArray; @@ -52,6 +60,8 @@ TSimbaTextDrawer = class(TFPImageFreeTypeDrawer) FBold: Boolean; FItalic: Boolean; FLock: TSimpleEnterableLock; + FDrawn: Boolean; + FDrawnBox: TBox; procedure MoveToPixel(X, Y: Integer); override; function GetCurrentColor: TFPColor; override; @@ -67,8 +77,11 @@ TSimbaTextDrawer = class(TFPImageFreeTypeDrawer) property Bold: Boolean read FBold write FBold; property Italic: Boolean read FItalic write FItalic; + property Drawn: Boolean read FDrawn; + property DrawnBox: TBox read FDrawnBox; + procedure DrawText(Text: String; Position: TPoint; Color: TColor); overload; - procedure DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); overload; + procedure DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); overload; function TextWidth(Text: String): Integer; function TextHeight(Text: String): Integer; @@ -84,7 +97,14 @@ implementation uses Forms, FileUtil, LazFileUtils, LazFreeTypeFontCollection, - simba.image; + simba.image, simba.image_utils, simba.fonthelpers; + +function TSimbaFreeTypeFontLoader.GetFontNames: TStringArray; +begin + LoadSystemFonts(); + + Result := FFontNames; +end; procedure TSimbaFreeTypeFontLoader.LoadSystemFonts; var @@ -259,29 +279,52 @@ function TSimbaFreeTypeFontLoader.GetFont(AName: String; ASize: Single; AAntiali procedure TSimbaTextDrawer.MoveToPixel(X, Y: Integer); begin FCurrentColor := @FData[Y * FWidth + X]; + FCurrentX := X; + FCurrentY := Y; end; function TSimbaTextDrawer.GetCurrentColor: TFPColor; begin with FCurrentColor^ do begin - Result.Red := (R shl 8) or R; // TFPColor fields are 16 bits. So duplicate our 8 bit data - Result.Green := (G shl 8) or G; - Result.Blue := (B shl 8) or B; - Result.Alpha := (255 shl 8) or 255; + Result.Red := R + (R shr 8); // TFPColor fields are 16 bits. So duplicate our 8 bit data + Result.Green := G + (G shr 8); + Result.Blue := B + (B shr 8); + Result.Alpha := A + (A shr 8); end; end; procedure TSimbaTextDrawer.SetCurrentColorAndMoveRight(const AColor: TFPColor); +var + BGRA: TColorBGRA; begin - FCurrentColor^ := TColorBGRA(((AColor.Blue shr 8) and $FF) or (AColor.Green and $FF00) or ((AColor.Red shl 8) and $FF0000) or (AColor.Alpha and $FF000000)); + BGRA.R := AColor.Red div 257; + BGRA.G := AColor.Green div 257; + BGRA.B := AColor.Blue div 257; + BGRA.A := AColor.Alpha shr 8; + + BlendPixel(FCurrentColor, BGRA); + + if FDrawn then + begin + if (FCurrentX > FDrawnBox.X2) then FDrawnBox.X2 := FCurrentX else + if (FCurrentX < FDrawnBox.X1) then FDrawnBox.X1 := FCurrentX; + if (FCurrentY > FDrawnBox.Y2) then FDrawnBox.Y2 := FCurrentY else + if (FCurrentY < FDrawnBox.Y1) then FDrawnBox.Y1 := FCurrentY; + end else + begin + FDrawn := True; + FDrawnBox := TBox.Create(FCurrentX, FCurrentY, FCurrentX, FCurrentY); + end; Inc(FCurrentColor); + Inc(FCurrentX); end; procedure TSimbaTextDrawer.MoveRight; begin Inc(FCurrentColor); + Inc(FCurrentX); end; function TSimbaTextDrawer.GetClipRect: TRect; @@ -303,6 +346,8 @@ procedure TSimbaTextDrawer.BeginDrawing; FClipRect.Right := FWidth; FClipRect.Bottom := FHeight; + FDrawn := False; + FFont := SimbaFreeTypeFontLoader.GetFont(FFontName, FSize, FFontAntialised, FBold, FItalic); if (FFont = nil) then SimbaException('Font "%s" not found', [FFontName]); @@ -318,6 +363,7 @@ constructor TSimbaTextDrawer.Create(SimbaImage: TObject); FSimbaImage := SimbaImage; FSize := 20; FFontAntialised := False; + FFontName := GetDefaultFontName(); end; procedure TSimbaTextDrawer.DrawText(Text: String; Position: TPoint; Color: TColor); @@ -330,15 +376,13 @@ procedure TSimbaTextDrawer.DrawText(Text: String; Position: TPoint; Color: TColo end; end; -procedure TSimbaTextDrawer.DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); +procedure TSimbaTextDrawer.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); +var + FreeTypeAlignments: TFreeTypeAlignments absolute Alignments; begin BeginDrawing(); - try - if Center then - inherited DrawTextRect(Text, FFont, Box.X1, Box.Y1, Box.X2, Box.Y2, TColorToFPColor(Color), [ftaCenter, ftaVerticalCenter]) - else - inherited DrawTextRect(Text, FFont, Box.X1, Box.Y1, Box.X2, Box.Y2, TColorToFPColor(Color), [ftaLeft, ftaTop]); + inherited DrawTextRect(Text, FFont, Box.X1, Box.Y1, Box.X2, Box.Y2, TColorToFPColor(Color), FreeTypeAlignments); finally EndDrawing(); end; diff --git a/Source/image/simba.image_utils.pas b/Source/image/simba.image_utils.pas index c9d4037c4..cdd643036 100644 --- a/Source/image/simba.image_utils.pas +++ b/Source/image/simba.image_utils.pas @@ -10,14 +10,20 @@ interface // https://sashamaps.net/docs/resources/20-colors/ const - DISTINCT_COLORS: TIntegerArray = ( + DISTINCT_COLORS: TColorArray = ( $4B19E6, $4BB43C, $19E1FF, $D86343, $3182F5, $B41E91, $F4D442, $E632F0, $45EFBF, $D4BEFA, $909946, $FFBEDC, $24639A, $C8FAFF, $000080, $C3FFAA, $008080, $B1D8FF, $750000, $A9A9A9 ); -function GetDistinctColor(const Index: Integer): Integer; overload; inline; -function GetDistinctColor(const Color, Index: Integer): Integer; overload; inline; + ALPHA_OPAQUE = Byte(255); + ALPHA_TRANSPARENT = Byte(0); + +procedure BlendPixel(const Data: PColorBGRA; const DataW, DataH: Integer; const X,Y: Integer; const Color: TColorBGRA); overload; inline; +procedure BlendPixel(const Pixel: PColorBGRA; const Color: TColorBGRA); overload; inline; + +function GetDistinctColor(const Index: Integer): Integer; overload; +function GetDistinctColor(const Color, Index: Integer): Integer; overload; function GetRotatedSize(W, H: Integer; Angle: Single): TBox; @@ -28,6 +34,40 @@ implementation uses simba.array_point, simba.geometry; +procedure BlendPixel(const Pixel: PColorBGRA; const Color: TColorBGRA); +begin + if (Pixel^.A > 0) then + with Pixel^ do + begin + A := 255 - ((255 - A) * (255 - Color.A) div 255); + R := (R * Byte(255 - Color.A) + Color.R * Color.A) div 255; + G := (G * Byte(255 - Color.A) + Color.G * Color.A) div 255; + B := (B * Byte(255 - Color.A) + Color.B * Color.A) div 255; + end + else + Pixel^ := Color; +end; + +procedure BlendPixel(const Data: PColorBGRA; const DataW, DataH: Integer; const X, Y: Integer; const Color: TColorBGRA); +var + Pixel: PColorBGRA; +begin + if (X >= 0) and (Y >= 0) and (X < DataW) and (Y < DataH) then + begin + Pixel := @Data[Y * DataW + X]; + if (Pixel^.A > 0) then + with Pixel^ do + begin + A := 255 - ((255 - A) * (255 - Color.A) div 255); + R := (R * Byte(255 - Color.A) + Color.R * Color.A) div 255; + G := (G * Byte(255 - Color.A) + Color.G * Color.A) div 255; + B := (B * Byte(255 - Color.A) + Color.B * Color.A) div 255; + end + else + Pixel^ := Color; + end; +end; + function GetDistinctColor(const Index: Integer): Integer; begin Result := DISTINCT_COLORS[Index mod Length(DISTINCT_COLORS)]; diff --git a/Source/imagebox/simba.imagebox_image.pas b/Source/imagebox/simba.imagebox_image.pas index 9b85a66de..8e13bff1b 100644 --- a/Source/imagebox/simba.imagebox_image.pas +++ b/Source/imagebox/simba.imagebox_image.pas @@ -7,7 +7,7 @@ interface uses Classes, SysUtils, Graphics, LCLType, - simba.base; + simba.base, simba.image_lazbridge; type PSimbaImageBoxBitmap = ^TSimbaImageBoxBitmap; @@ -28,7 +28,7 @@ TSimbaImageBoxBitmap = class(TObject) end; protected FBitmap: TBitmap; - FPixelFormat: String; + FPixelFormat: ELazPixelFormat; FOffset: TPoint; FRect: TRect; @@ -89,7 +89,7 @@ TSimbaImageBoxBitmap = class(TObject) implementation uses - simba.image_lazbridge, simba.matrix_float, simba.box; + simba.box, simba.matrix_float; const HEATMAP_LOOKUP_TABLE: array[0..837] of TColor = ( @@ -300,9 +300,9 @@ procedure TSimbaImageBoxBitmap.DrawLine(Start, Stop: TPoint; Color: TColor); begin case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -361,9 +361,9 @@ procedure TSimbaImageBoxBitmap.DrawLineGap(Start, Stop: TPoint; Gap: Integer; Co begin case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -449,9 +449,9 @@ procedure TSimbaImageBoxBitmap.DrawBoxTransparent(Box: TBox; Color: TColor; Tran Transparency := 1.0 - Transparency; case FPixelFormat of - 'BGR': DrawBGR(); - 'BGRA': DrawBGRA(); - 'ARGB': DrawARGB(); + ELazPixelFormat.BGR: DrawBGR(); + ELazPixelFormat.BGRA: DrawBGRA(); + ELazPixelFormat.ARGB: DrawARGB(); end; end; @@ -478,9 +478,9 @@ procedure TSimbaImageBoxBitmap.DrawBoxFilled(Box: TBox; Color: TColor); Box.Normalize(); case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -533,9 +533,9 @@ procedure TSimbaImageBoxBitmap.DrawPoints(TPA: TPointArray; Color: TColor); Exit; case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -582,9 +582,9 @@ procedure TSimbaImageBoxBitmap.DrawCircle(Center: TPoint; Radius: Integer; Color begin case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -613,9 +613,9 @@ procedure TSimbaImageBoxBitmap.DrawCircleFilled(Center: TPoint; Radius: Integer; begin case FPixelFormat of - 'BGR': DrawBGR(BGR(Color)); - 'BGRA': DrawBGRA(BGRA(Color)); - 'ARGB': DrawARGB(ARGB(Color)); + ELazPixelFormat.BGR: DrawBGR(BGR(Color)); + ELazPixelFormat.BGRA: DrawBGRA(BGRA(Color)); + ELazPixelFormat.ARGB: DrawARGB(ARGB(Color)); end; end; @@ -651,9 +651,9 @@ procedure TSimbaImageBoxBitmap.DrawHeatmap(const Mat: TSingleMatrix); begin case FPixelFormat of - 'BGR': DrawBGR(); - 'BGRA': DrawBGRA(); - 'ARGB': DrawARGB(); + ELazPixelFormat.BGR: DrawBGR(); + ELazPixelFormat.BGRA: DrawBGRA(); + ELazPixelFormat.ARGB: DrawARGB(); end; end; diff --git a/Source/imagebox/simba.imagebox_new.pas b/Source/imagebox/simba.imagebox_new.pas index a15e85ab0..bc2c7bd64 100644 --- a/Source/imagebox/simba.imagebox_new.pas +++ b/Source/imagebox/simba.imagebox_new.pas @@ -14,7 +14,7 @@ interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, ExtCtrls, ATScrollBar, LCLType, LMessages, - simba.base, simba.component_statusbar; + simba.base, simba.component_statusbar, simba.image_lazbridge; const ZOOM_LEVELS: array[1..9] of Integer = ( @@ -29,6 +29,8 @@ TSimbaImageBoxNew = class; TSimbaImageScrollBox = class(TCustomControl) protected + FPixelFormat: ELazPixelFormat; + FImageBox: TSimbaImageBoxNew; FImageWidth: Integer; FImageHeight: Integer; @@ -84,22 +86,25 @@ TSimbaImageScrollBox = class(TCustomControl) procedure MoveTo(ImageXY: TPoint); property Background: TBitmap read FBackground; + property PixelFormat: ELazPixelFormat read FPixelFormat; end; TSimbaImageBoxNew = class(TCustomControl) protected FImageScrollBox: TSimbaImageScrollBox; FStatusBar: TSimbaStatusBar; + FPixelFormat: ELazPixelFormat; + FBackground: TBitmap; - function GetBackground: TBitmap; function GetMousePoint: TPoint; public constructor Create(AOwner: TComponent); override; property StatusBar: TSimbaStatusBar read FStatusBar; - property Background: TBitmap read GetBackground; + property Background: TBitmap read FBackground; property MousePoint: TPoint read GetMousePoint; property OnDblClick; + property PixelFormat: ELazPixelFormat read FPixelFormat; end; implementation @@ -546,6 +551,8 @@ constructor TSimbaImageScrollBox.Create(AOwner: TComponent); FBitmap := TBitmap.Create(); FBackground := TBitmap.Create(); FBackground.OnChange := @DoBackgroundChange; + + FPixelFormat := LazImage_PixelFormat(FBackground); end; destructor TSimbaImageScrollBox.Destroy; @@ -556,11 +563,6 @@ destructor TSimbaImageScrollBox.Destroy; inherited Destroy(); end; -function TSimbaImageBoxNew.GetBackground: TBitmap; -begin - Result := FImageScrollBox.Background; -end; - function TSimbaImageBoxNew.GetMousePoint: TPoint; begin Result := FImageScrollBox.MousePoint; @@ -583,6 +585,9 @@ constructor TSimbaImageBoxNew.Create(AOwner: TComponent); FStatusBar.PanelTextMeasure[1] := '1234 x 1234'; FStatusBar.PanelTextMeasure[2] := '1000%'; FStatusBar.PanelText[2] := '100%'; + + FPixelFormat := FImageScrollBox.PixelFormat; + FBackground := FImageScrollBox.Background; end; end. diff --git a/Source/matchtemplate/simba.matchtemplate.pas b/Source/matchtemplate/simba.matchtemplate.pas index 27f4ad5b7..363d63ff9 100644 --- a/Source/matchtemplate/simba.matchtemplate.pas +++ b/Source/matchtemplate/simba.matchtemplate.pas @@ -23,7 +23,7 @@ interface uses Classes, SysUtils, - simba.base, simba.baseclass; + simba.base, simba.baseclass, simba.image; type PTMFormula = ^ETMFormula; @@ -43,10 +43,17 @@ TMatchTemplateCacheBase = class(TSimbaBaseClass) Height: Integer; end; +// TIntegerMatrix function MatchTemplateCache(Image, Template: TIntegerMatrix; Formula: ETMFormula): TMatchTemplateCacheBase; overload; function MatchTemplateMask(Cache: TMatchTemplateCacheBase; Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload; function MatchTemplateMask(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload; -function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; +function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload; + +// TSimbaImage +function MatchTemplateCache(Image, Template: TSimbaImage; Formula: ETMFormula): TMatchTemplateCacheBase; overload; +function MatchTemplateMask(Cache: TMatchTemplateCacheBase; Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; overload; +function MatchTemplateMask(Image, Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; overload; +function MatchTemplate(Image, Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; overload; function CalculateSlices(SearchWidth, SearchHeight: Integer): Integer; @@ -175,6 +182,26 @@ function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TS end; end; +function MatchTemplateCache(Image, Template: TSimbaImage; Formula: ETMFormula): TMatchTemplateCacheBase; +begin + Result := MatchTemplateCache(Image.ToMatrix(), Template.ToMatrix(), Formula); +end; + +function MatchTemplateMask(Cache: TMatchTemplateCacheBase; Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; +begin + Result := MatchTemplateMask(Cache, Template.ToMatrix(), Formula); +end; + +function MatchTemplateMask(Image, Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; +begin + Result := MatchTemplateMask(Image.ToMatrix(), Template.ToMatrix(), Formula); +end; + +function MatchTemplate(Image, Template: TSimbaImage; Formula: ETMFormula): TSingleMatrix; +begin + Result := MatchTemplate(Image.ToMatrix(), Template.ToMatrix(), Formula); +end; + initialization MatchTemplateMultithreadOpts.Enabled := True; MatchTemplateMultithreadOpts.SliceWidth := 125; diff --git a/Source/script/imports/simba.import_debugimage.pas b/Source/script/imports/simba.import_debugimage.pas index 0c518e9fe..5670fac87 100644 --- a/Source/script/imports/simba.import_debugimage.pas +++ b/Source/script/imports/simba.import_debugimage.pas @@ -58,18 +58,6 @@ implementation > procedure Show(Quad: TQuad; Filled: Boolean = False); *) -(* -Show -~~~~ -> procedure Show(Circles: TCircleArray; Filled: Boolean = False); -*) - -(* -Show -~~~~ -> procedure Show(Circle: TCircle; Filled: Boolean = False); -*) - (* Show ~~~~ @@ -106,18 +94,6 @@ implementation > procedure ShowOnClient(Quad: TQuad; Filled: Boolean = False); *) -(* -ShowOnClient -~~~~~~~~~~~~ -> procedure ShowOnClient(Circles: TCircleArray; Filled: Boolean = False); -*) - -(* -ShowOnClient -~~~~~~~~~~~~ -> procedure ShowOnClient(Circle: TCircle; Filled: Boolean = False); -*) - (* ShowOnClient ~~~~~~~~~~~~ @@ -327,33 +303,6 @@ procedure ImportDebugImage(Compiler: TSimbaScript_Compiler); 'end;' ]); - addGlobalFunc( - 'procedure Show(Circles: TCircleArray; Filled: Boolean = False); overload;', [ - 'var', - ' Boxes: TBoxArray;', - ' Circle: TCircle;', - 'begin', - ' for Circle in Circles do', - ' Boxes += Circle.Bounds();', - '', - ' with Boxes.Merge() do', - ' with TImage.Create(X1+X2+1, Y1+Y2+1) do', - ' try', - ' DrawCircleArray(Circles, Filled);', - ' Show();', - ' finally', - ' Free();', - ' end;', - 'end;' - ]); - - addGlobalFunc( - 'procedure Show(Circle: TCircle; Filled: Boolean = False); overload;', [ - 'begin', - ' Show(TCircleArray([Circle]), Filled);', - 'end;' - ]); - addGlobalFunc( 'procedure ShowOnClient(Quads: TQuadArray; Filled: Boolean = False); overload;', [ 'begin', @@ -394,26 +343,6 @@ procedure ImportDebugImage(Compiler: TSimbaScript_Compiler); 'end;' ]); - addGlobalFunc( - 'procedure ShowOnClient(Circles: TCircleArray; Filled: Boolean = False); overload;', [ - 'begin', - ' with TImage.CreateFromTarget() do', - ' try', - ' DrawCircleArray(Circles, Filled);', - ' Show();', - ' finally', - ' Free();', - ' end;', - 'end;' - ]); - - addGlobalFunc( - 'procedure ShowOnClient(Circle: TCircle; Filled: Boolean = False); overload;', [ - 'begin', - ' ShowOnClient(TCircleArray([Circle]), Filled);', - 'end;' - ]); - addGlobalFunc( 'procedure ShowOnClient(TPA: TPointArray; Color: Integer = $0000FF); overload;', [ 'begin', diff --git a/Source/script/imports/simba.import_externalimage.pas b/Source/script/imports/simba.import_externalimage.pas index 79f60902b..367f40e48 100644 --- a/Source/script/imports/simba.import_externalimage.pas +++ b/Source/script/imports/simba.import_externalimage.pas @@ -14,7 +14,7 @@ implementation uses lptypes, - simba.image, simba.externalimage; + simba.image, simba.image_textdrawer, simba.externalimage; procedure _LapeExternalImage_Create(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin @@ -28,12 +28,12 @@ procedure _LapeExternalImage_FreeOnTerminate(const Params: PParamArray; const Re procedure _LapeExternalImage_Width(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PInteger(Result)^ := PSimbaExternalImage(Params^[0])^.Width(); + PInteger(Result)^ := PSimbaExternalImage(Params^[0])^.Width; end; procedure _LapeExternalImage_Height(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PInteger(Result)^ := PSimbaExternalImage(Params^[0])^.Height(); + PInteger(Result)^ := PSimbaExternalImage(Params^[0])^.Height; end; (* @@ -156,6 +156,21 @@ procedure _LapeExternalImage_TextHeight(const Params: PParamArray; const Result: PInteger(Result)^ := PSimbaExternalImage(Params^[0])^.TextHeight(PString(Params^[1])^); end; +procedure _LapeExternalImage_Clear1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaExternalImage(Params^[0])^.Clear(); +end; + +procedure _LapeExternalImage_Clear2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaExternalImage(Params^[0])^.Clear(PBox(Params^[1])^); +end; + +procedure _LapeExternalImage_ClearInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaExternalImage(Params^[0])^.ClearInverted(PBox(Params^[1])^); +end; + (* TExternalImage.TextSize ~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,24 +191,14 @@ procedure _LapeExternalImage_DrawText(const Params: PParamArray); LAPE_WRAPPER_C PSimbaExternalImage(Params^[0])^.DrawText(PString(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^); end; -(* -TExternalImage.Draw -~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.Draw(Image: TImage; Position: TPoint); -*) -procedure _LapeExternalImage_Draw(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaExternalImage(Params^[0])^.Draw(PSimbaImage(Params^[1])^, PPoint(Params^[2])^); -end; - (* TExternalImage.DrawText ~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); +> procedure TExternalImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignmentSet; Color: TColor); *) procedure _LapeExternalImage_DrawTextEx(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawText(PString(Params^[1])^, PBox(Params^[2])^, PBoolean(Params^[3])^, PColor(Params^[4])^); + PSimbaExternalImage(Params^[0])^.DrawText(PString(Params^[1])^, PBox(Params^[2])^, ETextDrawAlignSet(Params^[3]^), PColor(Params^[4])^); end; (* @@ -206,309 +211,184 @@ procedure _LapeExternalImage_DrawTextLines(const Params: PParamArray); LAPE_WRAP PSimbaExternalImage(Params^[0])^.DrawTextLines(PStringArray(Params^[1])^, PPoint(Params^[2])^, PInteger(Params^[3])^); end; -(* -TExternalImage.DrawATPA -~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawATPA(ATPA: T2DPointArray; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawATPA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_Fill(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawATPA(P2DPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.Fill(PColor(Params^[1])^, PByte(Params^[2])^); end; -(* -TExternalImage.DrawTPA -~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawTPA(TPA: TPointArray; Color: TColor); -*) -procedure _LapeExternalImage_DrawTPA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_SetMemory(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawTPA(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.SetMemory(PPointer(Params^[1])^, PInteger(Params^[2])^, PInteger(Params^[3])^); end; -(* -TExternalImage.DrawCross -~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); -*) -procedure _LapeExternalImage_DrawCross(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_GetUserData(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCross(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); + PPointer(Result)^ := PSimbaExternalImage(Params^[0])^.UserData end; -(* -TExternalImage.DrawCrosshairs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); -*) -procedure _LapeExternalImage_DrawCrosshairs(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_SetUserData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCrosshairs(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.UserData := PPointer(Params^[1])^; end; -(* -TExternalImage.DrawLine -~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor); -*) -procedure _LapeExternalImage_DrawLine(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCircleAA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawLine(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCircleAA(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PSingle(Params^[4])^); end; -(* -TExternalImage.DrawLine -~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor); -*) -procedure _LapeExternalImage_DrawLineEx(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawLineAA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawLine(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawLineAA(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^, PSingle(Params^[4])^); end; -(* -TExternalImage.DrawPolygon -~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawPolygon(Points: TPointArray; Color: TColor); -*) -procedure _LapeExternalImage_DrawPolygon(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawEllipseAA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawPolygon(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawEllipseAA(PPoint(Params^[1])^, PInteger(Params^[2])^, PInteger(Params^[3])^, PColor(Params^[4])^, PSingle(Params^[5])^); end; -(* -TExternalImage.DrawPolygonFilled -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawPolygonFilled(Points: TPointArray; Color: TColor); -*) -procedure _LapeExternalImage_DrawPolygonFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_BeginUpdate(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawPolygonFilled(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.BeginUpdate(); end; -(* -TExternalImage.DrawPolygonInverted -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawPolygonInverted(Points: TPointArray; Color: TColor); -*) -procedure _LapeExternalImage_DrawPolygonInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_EndUpdate(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawPolygonInverted(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.EndUpdate(); end; -(* -TExternalImage.DrawCircle -~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor); -*) -procedure _LapeExternalImage_DrawCircle(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawImage(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCircle(PCircle(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawImage(PSimbaImage(Params^[1])^, PPoint(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawCircleFilled -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor); -*) -procedure _LapeExternalImage_DrawCircleFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawBox(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCircleFilled(PCircle(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawBox(PBox(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawCircleInverted -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor); -*) -procedure _LapeExternalImage_DrawCircleInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawBoxFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCircleInverted(PCircle(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawBoxFilled(PBox(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawBox -~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawBox(B: TBox; Color: TColor); -*) -procedure _LapeExternalImage_DrawBox(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawBoxInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawBox(PBox(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawBoxInverted(PBox(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawBoxFilled -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawBoxFilled(B: TBox; Color: TColor); -*) -procedure _LapeExternalImage_DrawBoxFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawPolygon(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawBoxFilled(PBox(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawPolygon(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawBoxInverted -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawBoxInverted(B: TBox; Color: TColor); -*) -procedure _LapeExternalImage_DrawBoxInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawPolygonFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawBoxInverted(PBox(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawPolygonFilled(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); +end; + +procedure _LapeExternalImage_DrawPolygonInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaExternalImage(Params^[0])^.DrawPolygonInverted(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawQuad -~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawQuad(B: TBox; Color: TColor); -*) procedure _LapeExternalImage_DrawQuad(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawQuad(PQuad(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawQuad(PQuad(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawQuadFilled -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawQuadFilled(B: TBox; Color: TColor); -*) procedure _LapeExternalImage_DrawQuadFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawQuadFilled(PQuad(Params^[1])^, PColor(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawQuadFilled(PQuad(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawQuadInverted -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawQuadInverted(B: TBox; Color: TColor); -*) procedure _LapeExternalImage_DrawQuadInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawQuadInverted(PQuad(Params^[1])^, PInteger(Params^[2])^); + PSimbaExternalImage(Params^[0])^.DrawQuadInverted(PQuad(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -(* -TExternalImage.DrawQuadArray -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawQuadArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCircle(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawQuadArray(PQuadArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCircle(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.DrawBoxArray -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawBoxArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCircleFilled(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawBoxArray(PBoxArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCircleFilled(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.DrawPolygonArray -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawPolygonArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCircleInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawPolygonArray(P2DPointArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCircleInverted(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.DrawCircleArray -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCircleArray(Points: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawCircleArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCrosshairs(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCircleArray(PCircleArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCrosshairs(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.DrawCrossArray -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor = -1); -*) -procedure _LapeExternalImage_DrawCrossArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCross(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.DrawCrossArray(PPointArray(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawCross(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.Fill -~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.Fill(Color: TColor); -*) -procedure _LapeExternalImage_Fill(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawLine(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.Fill(PColor(Params^[1])^); + PSimbaExternalImage(Params^[0])^.DrawLine(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TExternalImage.Clear -~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.Clear; -*) -procedure _LapeExternalImage_Clear(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawLineGap(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.Clear(); + PSimbaExternalImage(Params^[0])^.DrawLineGap(PPoint(Params^[1])^, PPoint(Params^[2])^, PInteger(Params^[3])^, PInteger(Params^[4])^, PByte(Params^[5])^); end; -(* -TExternalImage.Clear -~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.Clear(Area: TBox); -*) -procedure _LapeExternalImage_ClearEx(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImageSetAlpha1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.Clear(PBox(Params^[1])^); + PSimbaExternalImage(Params^[0])^.SetAlpha(PByte(Params^[1])^); end; -(* -TExternalImage.ClearInverted -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TExternalImage.ClearInverted(Area: TBox); -*) -procedure _LapeExternalImage_ClearInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImageSetAlpha2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.ClearInverted(PBox(Params^[1])^); + PSimbaExternalImage(Params^[0])^.SetAlpha(PPointArray(Params^[1])^, PByte(Params^[2])^); end; -procedure _LapeExternalImage_InternalImage(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImageSetAlpha3(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Result)^ := PSimbaExternalImage(Params^[0])^.InternalImage(); + PSimbaExternalImage(Params^[0])^.SetAlpha(PColor(Params^[1])^, PByte(Params^[2])^); end; -procedure _LapeExternalImage_TryLock(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawATPA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PBoolean(Result)^ := PSimbaExternalImage(Params^[0])^.TryLock(); + PSimbaExternalImage(Params^[0])^.DrawATPA(P2DPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -procedure _LapeExternalImage_Lock(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawTPA(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.Lock(); + PSimbaExternalImage(Params^[0])^.DrawTPA(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; -procedure _LapeExternalImage_Unlock(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawQuadArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.Unlock(); + PSimbaExternalImage(Params^[0])^.DrawQuadArray(PQuadArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); end; -procedure _LapeExternalImage_SetMemory(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawBoxArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.SetMemory(PPointer(Params^[1])^, PInteger(Params^[2])^, PInteger(Params^[3])^); + PSimbaExternalImage(Params^[0])^.DrawBoxArray(PBoxArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); end; -procedure _LapeExternalImage_GetUserData(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawPolygonArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PPointer(Result)^ := PSimbaExternalImage(Params^[0])^.GetUserData(); + PSimbaExternalImage(Params^[0])^.DrawPolygonArray(P2DPointArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); end; -procedure _LapeExternalImage_SetUserData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeExternalImage_DrawCircleArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaExternalImage(Params^[0])^.DrawCircleArray(PPointArray(Params^[1])^, PInteger(Params^[2])^, PBoolean(Params^[3])^, PColor(Params^[4])^); +end; + +procedure _LapeExternalImage_DrawCrossArray(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaExternalImage(Params^[0])^.SetUserData(PPointer(Params^[1])^); + PSimbaExternalImage(Params^[0])^.DrawCrossArray(PPointArray(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); end; procedure ImportSimbaExternalImage(Compiler: TSimbaScript_Compiler); @@ -520,69 +400,75 @@ procedure ImportSimbaExternalImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('function TExternalImage.Create: TExternalImage; static;', @_LapeExternalImage_Create); addGlobalFunc('procedure TExternalImage.FreeOnTerminate(Value: Boolean);', @_LapeExternalImage_FreeOnTerminate); + addGlobalFunc('procedure TExternalImage.SetMemory(Data: PColorBGRA; AWidth, AHeight: Integer);', @_LapeExternalImage_SetMemory); + addGlobalFunc('procedure TExternalImage.BeginUpdate', @_LapeExternalImage_BeginUpdate); + addGlobalFunc('procedure TExternalImage.EndUpdate', @_LapeExternalImage_EndUpdate); + + addGlobalFunc('function TExternalImage.Width: Integer;', @_LapeExternalImage_Width); + addGlobalFunc('function TExternalImage.Height: Integer;', @_LapeExternalImage_Height); + + addClassVar('TExternalImage', 'UserData', 'Pointer', @_LapeExternalImage_GetUserData, @_LapeExternalImage_SetUserData); + addClassVar('TExternalImage', 'FontName', 'String', @_LapeExternalImage_FontName_Read, @_LapeExternalImage_FontName_Write); addClassVar('TExternalImage', 'FontSize', 'Single', @_LapeExternalImage_FontSize_Read, @_LapeExternalImage_FontSize_Write); addClassVar('TExternalImage', 'FontAntialiasing', 'Boolean', @_LapeExternalImage_FontAntialiasing_Read, @_LapeExternalImage_FontAntialiasing_Write); addClassVar('TExternalImage', 'FontBold', 'Boolean', @_LapeExternalImage_FontBold_Read, @_LapeExternalImage_FontBold_Write); addClassVar('TExternalImage', 'FontItalic', 'Boolean', @_LapeExternalImage_FontItalic_Read, @_LapeExternalImage_FontItalic_Write); - addClassVar('TExternalImage', 'UserData', 'Pointer', @_LapeExternalImage_GetUserData, @_LapeExternalImage_SetUserData); addGlobalFunc('function TExternalImage.TextWidth(Text: String): Integer;', @_LapeExternalImage_TextWidth); addGlobalFunc('function TExternalImage.TextHeight(Text: String): Integer;', @_LapeExternalImage_TextHeight); addGlobalFunc('function TExternalImage.TextSize(Text: String): TPoint;', @_LapeExternalImage_TextSize); addGlobalFunc('procedure TExternalImage.DrawText(Text: String; Position: TPoint; Color: TColor); overload', @_LapeExternalImage_DrawText); - addGlobalFunc('procedure TExternalImage.DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); overload', @_LapeExternalImage_DrawTextEx); + addGlobalFunc('procedure TExternalImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); overload', @_LapeExternalImage_DrawTextEx); addGlobalFunc('procedure TExternalImage.DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor);', @_LapeExternalImage_DrawTextLines); - addGlobalFunc('function TExternalImage.Width: Integer;', @_LapeExternalImage_Width); - addGlobalFunc('function TExternalImage.Height: Integer;', @_LapeExternalImage_Height); + addGlobalFunc('procedure TExternalImage.Fill(Color: TColor; Alpha: Byte)', @_LapeExternalImage_Fill); - addGlobalFunc('function TExternalImage.InternalImage: TImage;', @_LapeExternalImage_InternalImage); + addGlobalFunc('procedure TExternalImage.SetAlpha(Value: Byte); overload;', @_LapeExternalImageSetAlpha1); + addGlobalFunc('procedure TExternalImage.SetAlpha(Points: TPointArray; Value: Byte); overload;', @_LapeExternalImageSetAlpha2); + addGlobalFunc('procedure TExternalImage.SetAlpha(Color: TColor; Value: Byte); overload;', @_LapeExternalImageSetAlpha3); - addGlobalFunc('function TExternalImage.TryLock: Boolean;', @_LapeExternalImage_TryLock); - addGlobalFunc('procedure TExternalImage.Lock;', @_LapeExternalImage_Lock); - addGlobalFunc('procedure TExternalImage.Unlock;', @_LapeExternalImage_Unlock); - addGlobalFunc('procedure TExternalImage.SetMemory(Data: PColorBGRA; AWidth, AHeight: Integer);', @_LapeExternalImage_SetMemory); + addGlobalFunc('procedure TExternalImage.Clear; overload', @_LapeExternalImage_Clear1); + addGlobalFunc('procedure TExternalImage.Clear(Box: TBox); overload;', @_LapeExternalImage_Clear2); + addGlobalFunc('procedure TExternalImage.ClearInverted(Box: TBox);', @_LapeExternalImage_ClearInverted); + + addGlobalFunc('procedure TExternalImage.DrawATPA(ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0)', @_LapeExternalImage_DrawATPA); + addGlobalFunc('procedure TExternalImage.DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte = 0)', @_LapeExternalImage_DrawTPA); - addGlobalFunc('procedure TExternalImage.Draw(Image: TImage; Position: TPoint);', @_LapeExternalImage_Draw); + addGlobalFunc('procedure TExternalImage.DrawImage(Image: TImage; Position: TPoint; Alpha: Byte = 0);', @_LapeExternalImage_DrawImage); - addGlobalFunc('procedure TExternalImage.DrawATPA(ATPA: T2DPointArray; Color: TColor)', @_LapeExternalImage_DrawATPA); - addGlobalFunc('procedure TExternalImage.DrawTPA(TPA: TPointArray; Color: TColor);', @_LapeExternalImage_DrawTPA); + addGlobalFunc('procedure TExternalImage.DrawBox(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawBox); + addGlobalFunc('procedure TExternalImage.DrawBoxFilled(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawBoxFilled); + addGlobalFunc('procedure TExternalImage.DrawBoxInverted(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawBoxInverted); - addGlobalFunc('procedure TExternalImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Thickness: Integer; Color: TColor);', @_LapeExternalImage_DrawCrosshairs); - addGlobalFunc('procedure TExternalImage.DrawCross(ACenter: TPoint; Radius: Integer; Thickness: Integer; Color: TColor);', @_LapeExternalImage_DrawCross); + addGlobalFunc('procedure TExternalImage.DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawPolygon); + addGlobalFunc('procedure TExternalImage.DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawPolygonFilled); + addGlobalFunc('procedure TExternalImage.DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawPolygonInverted); - addGlobalFunc('procedure TExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor); overload', @_LapeExternalImage_DrawLine); - addGlobalFunc('procedure TExternalImage.DrawLine(Start, Stop: TPoint; Thickness: Integer; Color: TColor); overload', @_LapeExternalImage_DrawLineEx); + addGlobalFunc('procedure TExternalImage.DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawQuad); + addGlobalFunc('procedure TExternalImage.DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawQuadFilled); + addGlobalFunc('procedure TExternalImage.DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawQuadInverted); - addGlobalFunc('procedure TExternalImage.DrawPolygon(Points: TPointArray; Color: TColor);', @_LapeExternalImage_DrawPolygon); - addGlobalFunc('procedure TExternalImage.DrawPolygonFilled(Points: TPointArray; Color: TColor);', @_LapeExternalImage_DrawPolygonFilled); - addGlobalFunc('procedure TExternalImage.DrawPolygonInverted(Points: TPointArray; Color: TColor);', @_LapeExternalImage_DrawPolygonInverted); + addGlobalFunc('procedure TExternalImage.DrawCircle(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeExternalImage_DrawCircle); + addGlobalFunc('procedure TExternalImage.DrawCircleFilled(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeExternalImage_DrawCircleFilled); + addGlobalFunc('procedure TExternalImage.DrawCircleInverted(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeExternalImage_DrawCircleInverted); - addGlobalFunc('procedure TExternalImage.DrawCircle(Circle: TCircle; Color: TColor);', @_LapeExternalImage_DrawCircle); - addGlobalFunc('procedure TExternalImage.DrawCircleFilled(Circle: TCircle; Radius: Integer; Color: TColor);', @_LapeExternalImage_DrawCircleFilled); - addGlobalFunc('procedure TExternalImage.DrawCircleInverted(Circle: TCircle; Radius: Integer; Color: TColor);', @_LapeExternalImage_DrawCircleInverted); + addGlobalFunc('procedure TExternalImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawCrosshairs); + addGlobalFunc('procedure TExternalImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawCross); - addGlobalFunc('procedure TExternalImage.DrawBox(B: TBox; Color: TColor);', @_LapeExternalImage_DrawBox); - addGlobalFunc('procedure TExternalImage.DrawBoxFilled(B: TBox; Color: TColor);', @_LapeExternalImage_DrawBoxFilled); - addGlobalFunc('procedure TExternalImage.DrawBoxInverted(B: TBox; Color: TColor);', @_LapeExternalImage_DrawBoxInverted); + addGlobalFunc('procedure TExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawLine); + addGlobalFunc('procedure TExternalImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte = 0);', @_LapeExternalImage_DrawLineGap); - addGlobalFunc('procedure TExternalImage.DrawQuad(Quad: TQuad; Color: TColor);', @_LapeExternalImage_DrawQuad); - addGlobalFunc('procedure TExternalImage.DrawQuadFilled(Quad: TQuad; Color: TColor);', @_LapeExternalImage_DrawQuadFilled); - addGlobalFunc('procedure TExternalImage.DrawQuadInverted(Quad: TQuad; Color: TColor);', @_LapeExternalImage_DrawQuadInverted); + addGlobalFunc('procedure TExternalImage.DrawCircleAA(ACenter: TPoint; Radius: Integer; Color: TColor; Thickness: Single = 1.5)', @_LapeExternalImage_DrawCircleAA); + addGlobalFunc('procedure TExternalImage.DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single = 1.5)', @_LapeExternalImage_DrawLineAA); + addGlobalFunc('procedure TExternalImage.DrawEllipseAA(ACenter: TPoint; XRadius, YRadius: Integer; Color: TColor; Thickness: Single = 1.5)', @_LapeExternalImage_DrawEllipseAA); addGlobalFunc('procedure TExternalImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor = -1);', @_LapeExternalImage_DrawQuadArray); addGlobalFunc('procedure TExternalImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor = -1);', @_LapeExternalImage_DrawBoxArray); addGlobalFunc('procedure TExternalImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor = -1);', @_LapeExternalImage_DrawPolygonArray); - addGlobalFunc('procedure TExternalImage.DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor = -1);', @_LapeExternalImage_DrawCircleArray); - addGlobalFunc('procedure TExternalImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Thickness: Integer; Color: TColor = -1);', @_LapeExternalImage_DrawCrossArray); - - addGlobalFunc('procedure TExternalImage.Clear; overload', @_LapeExternalImage_Clear); - addGlobalFunc('procedure TExternalImage.Clear(Area: TBox); overload', @_LapeExternalImage_ClearEx); - addGlobalFunc('procedure TExternalImage.ClearInverted(Area: TBox);', @_LapeExternalImage_ClearInverted); - - addGlobalFunc('procedure TExternalImage.Fill(Color: TColor);', @_LapeExternalImage_Fill); + addGlobalFunc('procedure TExternalImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1);', @_LapeExternalImage_DrawCircleArray); + addGlobalFunc('procedure TExternalImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor = -1);', @_LapeExternalImage_DrawCrossArray); end; end; diff --git a/Source/script/imports/simba.import_image.pas b/Source/script/imports/simba.import_image.pas index f06ca1e3f..92a071a90 100644 --- a/Source/script/imports/simba.import_image.pas +++ b/Source/script/imports/simba.import_image.pas @@ -15,7 +15,7 @@ implementation uses Graphics, lptypes, - simba.image; + simba.image, simba.image_textdrawer; type PBitmap = ^TBitmap; @@ -27,13 +27,6 @@ implementation This is used manipulate and process an image such as resizing, rotating, bluring and much more. Or simply get/set a pixel color at a given (x,y) coord. - -Note: - - | For methods with a `Alpha` parameter "real" alpha blending is not performed. - | The background of the pixel is just mixed in. - | If `Alpha=127` half of the background will be blended in. - *) (* @@ -131,7 +124,7 @@ procedure _LapeImage_Height_Read(const Params: PParamArray; const Result: Pointe ~~~~~~~~~~~~~~~~~~~~~~ > procedure TImage.SetExternalData(AData: PColorBGRA; AWidth, AHeight: Integer); *) -procedure _LapeImage_SetPersistentMemory(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_SetExternalData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin PSimbaImage(Params^[0])^.SetExternalData(PPointer(Params^[1])^, PInteger(Params^[2])^, PInteger(Params^[3])^); end; @@ -141,7 +134,7 @@ procedure _LapeImage_SetPersistentMemory(const Params: PParamArray); LAPE_WRAPPE ~~~~~~~~~~~~~~~~~~~~~~~~ > procedure TImage.ResetExternalData; *) -procedure _LapeImage_ResetPersistentMemory(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_ResetExternalData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin PSimbaImage(Params^[0])^.ResetExternalData(); end; @@ -151,9 +144,9 @@ procedure _LapeImage_ResetPersistentMemory(const Params: PParamArray); LAPE_WRAP ~~~~~~~~~~~~~~~~~ > function TImage.SaveToFile(FileName: String; OverwriteIfExists: Boolean = False): Boolean; *) -procedure _LapeImage_SaveToFile(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_Save(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PBoolean(Result)^ := PSimbaImage(Params^[0])^.SaveToFile(PString(Params^[1])^, PBoolean(Params^[2])^); + PBoolean(Result)^ := PSimbaImage(Params^[0])^.Save(PString(Params^[1])^, PBoolean(Params^[2])^); end; (* @@ -171,9 +164,9 @@ procedure _LapeImage_SaveToString(const Params: PParamArray; const Result: Point ~~~~~~~~~~~~~~~~~~~ > procedure TImage.LoadFromFile(FileName: String); *) -procedure _LapeImage_LoadFromFile(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_Load1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.LoadFromFile(PString(Params^[1])^); + PSimbaImage(Params^[0])^.Load(PString(Params^[1])^); end; (* @@ -181,15 +174,15 @@ procedure _LapeImage_LoadFromFile(const Params: PParamArray); LAPE_WRAPPER_CALLI ~~~~~~~~~~~~~~~~~~~ > procedure TImage.LoadFromFile(FileName: String; Area: TBox); *) -procedure _LapeImage_LoadFromFileEx(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_Load2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.LoadFromFile(PString(Params^[1])^, PBox(Params^[2])^); + PSimbaImage(Params^[0])^.Load(PString(Params^[1])^, PBox(Params^[2])^); end; (* TImage.DrawATPA ~~~~~~~~~~~~~~~ -> procedure TImage.DrawATPA(const ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0); +> procedure TImage.DrawATPA(ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0); Draws every TPA in the ATPA. Color by default is -1 which will display each TPA in a different color. *) @@ -201,7 +194,7 @@ procedure _LapeImage_DrawATPA(const Params: PParamArray); LAPE_WRAPPER_CALLING_C (* TImage.DrawTPA ~~~~~~~~~~~~~~ -> procedure TImage.DrawTPA(const Points: TPointArray; Color: TColor; Alpha: Byte = 0); +> procedure TImage.DrawTPA(Points: TPointArray; Color: TColor; Alpha: Byte = 0); Draws a TPA the same color. *) @@ -335,9 +328,9 @@ procedure _LapeImage_ToLazBitmap(const Params: PParamArray; const Result: Pointe ~~~~~~~~~~~~~~~~~~~~~~~~ > procedure TImage.LoadFromLazBitmap(LazBitmap: TLazBitmap); *) -procedure _LapeImage_LoadFromLazBitmap(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_FromLazBitmap(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.LoadFromLazBitmap(PBitmap(Params^[1])^); + PSimbaImage(Params^[0])^.FromLazBitmap(PBitmap(Params^[1])^); end; (* @@ -435,46 +428,6 @@ procedure _LapeImage_Pad(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV PSimbaImage(Params^[0])^.Pad(PInteger(Params^[1])^); end; -(* -TImage.SetTransparentColor -~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.SetTransparentColor(Value: TColor); -*) -procedure _LapeImage_TransparentColor_Write(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.TransparentColor := PColor(Params^[1])^; -end; - -(* -TImage.GetTransparentColor -~~~~~~~~~~~~~~~~~~~~~~~~~~ -> function TImage.GetTransparentColor: TColor; -*) -procedure _LapeImage_TransparentColor_Read(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PColor(Result)^ := PSimbaImage(Params^[0])^.TransparentColor; -end; - -(* -TImage.GetTransparentColorActive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> function TImage.GetTransparentColorActive: Boolean; -*) -procedure _LapeImage_TransparentColorActive_Read(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PBoolean(Result)^ := PSimbaImage(Params^[0])^.TransparentColorActive; -end; - -(* -TImage.SetTransparentColorActive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.SetTransparentColorActive(Value: Boolean); -*) -procedure _LapeImage_TransparentColorActive_Write(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.TransparentColorActive := PBoolean(Params^[1])^; -end; - (* TImage.GetPixel ~~~~~~~~~~~~~~~ @@ -558,41 +511,51 @@ procedure _LapeImage_Free(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV (* TImage.DrawCross ~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); +> procedure TImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawCross(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawCross(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); + PSimbaImage(Params^[0])^.DrawCross(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; (* TImage.DrawCrosshairs ~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); +> procedure TImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawCrosshairs(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawCrosshairs(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); + PSimbaImage(Params^[0])^.DrawCrosshairs(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; (* TImage.DrawLine ~~~~~~~~~~~~~~~ -> procedure TImage.DrawLine(Start, Stop: TPoint; Color: TColor); +> procedure TImage.DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawLine(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawLine(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^); + PSimbaImage(Params^[0])^.DrawLine(PPoint(Params^[1])^, PPoint(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); +end; + +(* +TImage.DrawLineGap +~~~~~~~~~~~~~~~~~~ +> procedure TImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte = 0); +*) +procedure _LapeImage_DrawLineGap(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaImage(Params^[0])^.DrawLineGap(PPoint(Params^[1])^, PPoint(Params^[2])^, PInteger(Params^[3])^, PColor(Params^[4])^, PByte(Params^[5])^); end; (* TImage.DrawPolygon ~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawPolygon(Points: TPointArray; Color: TColor); +> procedure TImage.DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawPolygon(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygon(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawPolygon(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; (* @@ -608,31 +571,21 @@ procedure _LapeImage_DrawPolygonFilled(const Params: PParamArray); LAPE_WRAPPER_ (* TImage.DrawPolygonInverted ~~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawPolygonInverted(Points: TPointArray; Color: TColor); +> procedure TImage.DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawPolygonInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawPolygonInverted(PPointArray(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawPolygonInverted(PPointArray(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; (* TImage.DrawCircle ~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircle(Center: TPoint; Radius: Integer; Color: TColor); +> procedure TImage.DrawCircle(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawCircle1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawCircle(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); -end; - -(* -TImage.DrawCircle -~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircle(Circle: TCircle; Color: TColor); -*) -procedure _LapeImage_DrawCircle2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.DrawCircle(PCircle(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawCircle(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; (* @@ -645,44 +598,24 @@ procedure _LapeImage_DrawCircleFilled1(const Params: PParamArray); LAPE_WRAPPER_ PSimbaImage(Params^[0])^.DrawCircleFilled(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; -(* -TImage.DrawCircleFilled -~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircleFilled(Circle: TCircle; Color: TColor; Alpha: Byte = 0); -*) -procedure _LapeImage_DrawCircleFilled2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.DrawCircleFilled(PCircle(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); -end; - (* TImage.DrawCircleInverted ~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircleInverted(Center: TPoint; Radius: Integer; Color: TColor); +> procedure TImage.DrawCircleInverted(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawCircleInverted1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawCircleInverted(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^); -end; - -(* -TImage.DrawCircleInverted -~~~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircleInverted(Circle: TCircle; Color: TColor); -*) -procedure _LapeImage_DrawCircleInverted2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.DrawCircleInverted(PCircle(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawCircleInverted(PPoint(Params^[1])^, PInteger(Params^[2])^, PColor(Params^[3])^, PByte(Params^[4])^); end; (* TImage.DrawBox ~~~~~~~~~~~~~~ -> procedure TImage.DrawBox(B: TBox; Color: TColor); +> procedure TImage.DrawBox(B: TBox; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawBox(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawBox(PBox(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawBox(PBox(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; (* @@ -698,21 +631,21 @@ procedure _LapeImage_DrawBoxFilled(const Params: PParamArray); LAPE_WRAPPER_CALL (* TImage.DrawBoxInverted ~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawBoxInverted(B: TBox; Color: TColor); +> procedure TImage.DrawBoxInverted(B: TBox; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawBoxInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawBoxInverted(PBox(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawBoxInverted(PBox(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; (* TImage.DrawQuad ~~~~~~~~~~~~~~~ -> procedure TImage.DrawQuad(B: TBox; Color: TColor); +> procedure TImage.DrawQuad(B: TBox; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawQuad(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawQuad(PQuad(Params^[1])^, PColor(Params^[2])^); + PSimbaImage(Params^[0])^.DrawQuad(PQuad(Params^[1])^, PColor(Params^[2])^, PByte(Params^[3])^); end; (* @@ -728,11 +661,11 @@ procedure _LapeImage_DrawQuadFilled(const Params: PParamArray); LAPE_WRAPPER_CAL (* TImage.DrawQuadInverted ~~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawQuadInverted(B: TBox; Color: TColor); +> procedure TImage.DrawQuadInverted(B: TBox; Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_DrawQuadInverted(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawQuadInverted(PQuad(Params^[1])^, PInteger(Params^[2])^); + PSimbaImage(Params^[0])^.DrawQuadInverted(PQuad(Params^[1])^, PInteger(Params^[2])^, PByte(Params^[3])^); end; (* @@ -775,16 +708,6 @@ procedure _LapeImage_DrawCircleArray1(const Params: PParamArray); LAPE_WRAPPER_C PSimbaImage(Params^[0])^.DrawCircleArray(PPointArray(Params^[1])^, PInteger(Params^[2])^, PBoolean(Params^[3])^, PColor(Params^[4])^); end; -(* -TImage.DrawCircleArray -~~~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor = -1); -*) -procedure _LapeImage_DrawCircleArray2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.DrawCircleArray(PCircleArray(Params^[1])^, PBoolean(Params^[2])^, PColor(Params^[3])^); -end; - (* TImage.DrawCrossArray ~~~~~~~~~~~~~~~~~~~~~ @@ -798,11 +721,11 @@ procedure _LapeImage_DrawCrossArray(const Params: PParamArray); LAPE_WRAPPER_CAL (* TImage.Fill ~~~~~~~~~~~ -> procedure TImage.Fill(Color: TColor); +> procedure TImage.Fill(Color: TColor; Alpha: Byte = 0); *) procedure _LapeImage_Fill(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.Fill(PColor(Params^[1])^); + PSimbaImage(Params^[0])^.Fill(PColor(Params^[1])^, PByte(Params^[2])^); end; (* @@ -836,23 +759,13 @@ procedure _LapeImage_ClearInverted(const Params: PParamArray); LAPE_WRAPPER_CALL end; (* -TImage.Draw -~~~~~~~~~~~ -> procedure TImage.Draw(Image: TImage; X, Y: Integer); -*) -procedure _LapeImage_Draw1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.Draw(PSimbaImage(Params^[1])^, PInteger(Params^[2])^, PInteger(Params^[3])^); -end; - -(* -TImage.Draw -~~~~~~~~~~~ -> procedure TImage.Draw(Image: TImage; Position: TPoint); +TImage.DrawImage +~~~~~~~~~~~~~~~~ +> procedure TImage.DrawImage(Image: TImage; Position: TPoint; Alpha: Byte = 0); *) -procedure _LapeImage_Draw2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_DrawImage(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.Draw(PSimbaImage(Params^[1])^, PPoint(Params^[2])^); + PSimbaImage(Params^[0])^.DrawImage(PSimbaImage(Params^[1])^, PPoint(Params^[2])^, PByte(Params^[3])^); end; (* @@ -1018,11 +931,11 @@ procedure _LapeImage_DrawText(const Params: PParamArray); LAPE_WRAPPER_CALLING_C (* TImage.DrawText ~~~~~~~~~~~~~~~ -> procedure TImage.DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); +> procedure TImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignmentSet; Color: TColor); *) procedure _LapeImage_DrawTextEx(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.DrawText(PString(Params^[1])^, PBox(Params^[2])^, PBoolean(Params^[3])^, PColor(Params^[4])^); + PSimbaImage(Params^[0])^.DrawText(PString(Params^[1])^, PBox(Params^[2])^, ETextDrawAlignSet(Params^[3]^), PColor(Params^[4])^); end; (* @@ -1057,6 +970,36 @@ procedure _LapeImage_Equals(const Params: PParamArray; const Result: Pointer); L PBoolean(Result)^ := PSimbaImage(Params^[0])^.Equals(PSimbaImage(Params^[1])^); end; +(* +TImage.SetAlpha +~~~~~~~~~~~~~~~ +> procedure TImage.SetAlpha(Value: Byte); +*) +procedure _LapeImage_SetAlpha1(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaImage(Params^[0])^.SetAlpha(PByte(Params^[1])^); +end; + +(* +TImage.SetAlpha +~~~~~~~~~~~~~~~ +> procedure TImage.SetAlpha(Points: TPointArray; Value: Byte); +*) +procedure _LapeImage_SetAlpha2(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaImage(Params^[0])^.SetAlpha(PPointArray(Params^[1])^, PByte(Params^[2])^); +end; + +(* +TImage.SetAlpha +~~~~~~~~~~~~~~~ +> procedure TImage.SetAlpha(Color: TColor; Value: Byte); +*) +procedure _LapeImage_SetAlpha3(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +begin + PSimbaImage(Params^[0])^.SetAlpha(PColor(Params^[1])^, PByte(Params^[2])^); +end; + (* TImage.PixelDifference ~~~~~~~~~~~~~~~~~~~~~~ @@ -1102,9 +1045,9 @@ procedure _LapeImage_PixelDifferenceToleranceTPA(const Params: PParamArray; cons ~~~~~~~~~~~~~~~~~~~~~ > procedure TImage.LoadFromString(Str: String); *) -procedure _LapeImage_LoadFromString(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_FromString(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.LoadFromString(PString(Params^[1])^); + PSimbaImage(Params^[0])^.FromString(PString(Params^[1])^); end; (* @@ -1112,19 +1055,9 @@ procedure _LapeImage_LoadFromString(const Params: PParamArray); LAPE_WRAPPER_CAL ~~~~~~~~~~~~~~~~~~~ > procedure TImage.LoadFromData(AWidth, AHeight: Integer; Memory: PColorBGRA; DataWidth: Integer); *) -procedure _LapeImage_LoadFromData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_FromData(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV begin - PSimbaImage(Params^[0])^.LoadFromData(PInteger(Params^[1])^, PInteger(Params^[2])^, PPointer(Params^[3])^, PInteger(Params^[4])^); -end; - -(* -TImage.LoadFromImage -~~~~~~~~~~~~~~~~~~~~ -> procedure TImage.LoadFromImage(Image: TImage); -*) -procedure _LapeImage_LoadFromImage(const Params: PParamArray); LAPE_WRAPPER_CALLING_CONV -begin - PSimbaImage(Params^[0])^.LoadFromImage(PSimbaImage(Params^[1])^); + PSimbaImage(Params^[0])^.FromData(PInteger(Params^[1])^, PInteger(Params^[2])^, PPointer(Params^[3])^, PInteger(Params^[4])^); end; (* @@ -1220,15 +1153,15 @@ procedure _LapeImage_LoadFonts(const Params: PParamArray; const Result: Pointer) end; (* -TImage.GetFontNames -~~~~~~~~~~~~~~~~~~~ -> function TImage.GetFontNames: TStringArray; static; +TImage.FontNames +~~~~~~~~~~~~~~~~ +> function TImage.FontNames: TStringArray; static; Returns all the available font names. *) -procedure _LapeImage_LoadedFontNames(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeImage_FontNames(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PStringArray(Result)^ := TSimbaImage.LoadedFontNames(); + PStringArray(Result)^ := TSimbaImage.FontNames(); end; (* @@ -1251,16 +1184,6 @@ procedure _LapeImage_DrawHSLCircle(const Params: PParamArray); LAPE_WRAPPER_CALL PSimbaImage(Params^[0])^.DrawHSLCircle(PPoint(Params^[1])^, PInteger(Params^[2])^); end; -(* -TImage.RowPtrs -~~~~~~~~~~~~~~ -> function TImage.RowPtrs: TImageRowPtrs; -*) -procedure _LapeImage_RowPtrs(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - TSimbaImageRowPtrs(Result^) := PSimbaImage(Params^[0])^.RowPtrs(); -end; - (* TImage.DrawLineAA ~~~~~~~~~~~~~~~~~ @@ -1349,29 +1272,26 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addClass('TImage'); addGlobalType('array of TImage', 'TImageArray'); - addGlobalType('array of PColorBGRA', 'TImageRowPtrs'); addGlobalType('enum(WIDTH, HEIGHT, LINE)', 'EImageMirrorStyle'); addGlobalType('enum(MEAN, MIN_MAX)', 'EImageThreshMethod'); - addClassVar('TImage', 'Data', 'PColorBGRA', @_LapeImage_Data_Read); - addClassVar('TImage', 'Name', 'String', @_LapeImage_Name_Read, @_LapeImage_Name_Write); - addClassVar('TImage', 'Width', 'Integer', @_LapeImage_Width_Read); - addClassVar('TImage', 'Height', 'Integer', @_LapeImage_Height_Read); - addClassVar('TImage', 'Center', 'TPoint', @_LapeImage_Center_Read); - addClassVar('TImage', 'TransparentColor', 'TColor', @_LapeImage_TransparentColor_Read, @_LapeImage_TransparentColor_Write); - addClassVar('TImage', 'TransparentColorActive', 'Boolean', @_LapeImage_TransparentColorActive_Read, @_LapeImage_TransparentColorActive_Write); + addGlobalType('enum(LEFT, CENTER, RIGHT, JUSTIFY, TOP, VERTICAL_CENTER, BASE_LINE, BOTTOM)', 'ETextDrawAlign'); + addGlobalType('set of ETextDrawAlign', 'ETextDrawAlignSet'); + addClassVar('TImage', 'Name', 'String', @_LapeImage_Name_Read, @_LapeImage_Name_Write); addClassVar('TImage', 'FontName', 'String', @_LapeImage_FontName_Read, @_LapeImage_FontName_Write); addClassVar('TImage', 'FontSize', 'Single', @_LapeImage_FontSize_Read, @_LapeImage_FontSize_Write); addClassVar('TImage', 'FontAntialiasing', 'Boolean', @_LapeImage_FontAntialiasing_Read, @_LapeImage_FontAntialiasing_Write); addClassVar('TImage', 'FontBold', 'Boolean', @_LapeImage_FontBold_Read, @_LapeImage_FontBold_Write); addClassVar('TImage', 'FontItalic', 'Boolean', @_LapeImage_FontItalic_Read, @_LapeImage_FontItalic_Write); - addGlobalFunc('function TImage.RowPtrs: TImageRowPtrs', @_LapeImage_RowPtrs); - - addGlobalFunc('function TImage.LoadedFontNames: TStringArray; static;', @_LapeImage_LoadedFontNames); + addGlobalFunc('function TImage.FontNames: TStringArray; static;', @_LapeImage_FontNames); addGlobalFunc('function TImage.LoadFonts(Dir: String): Boolean; static;', @_LapeImage_LoadFonts); + addGlobalFunc('function TImage.Width: Integer', @_LapeImage_Width_Read); + addGlobalFunc('function TImage.Height: Integer', @_LapeImage_Height_Read); + addGlobalFunc('function TImage.Data: PColorBGRA', @_LapeImage_Data_Read); + addGlobalFunc('function TImage.Center: TPoint', @_LapeImage_Center_Read); addGlobalFunc('function TImage.InImage(X, Y: Integer): Boolean', @_LapeImage_InImage); addGlobalFunc('function TImage.Create: TImage; static; overload', @_LapeImage_Create); @@ -1382,6 +1302,10 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('function TImage.Equals(Other: TImage): Boolean;', @_LapeImage_Equals); + addGlobalFunc('procedure TImage.SetAlpha(Value: Byte); overload;', @_LapeImage_SetAlpha1); + addGlobalFunc('procedure TImage.SetAlpha(Points: TPointArray; Value: Byte); overload;', @_LapeImage_SetAlpha2); + addGlobalFunc('procedure TImage.SetAlpha(Color: TColor; Value: Byte); overload;', @_LapeImage_SetAlpha3); + addGlobalFunc('procedure TImage.SetPixel(X, Y: Integer; Color: TColor);', @_LapeImage_SetPixel); addGlobalFunc('function TImage.GetPixel(X, Y: Integer): TColor;', @_LapeImage_GetPixel); @@ -1398,41 +1322,38 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('function TImage.TextHeight(Text: String): Integer;', @_LapeImage_TextHeight); addGlobalFunc('function TImage.TextSize(Text: String): TPoint;', @_LapeImage_TextSize); addGlobalFunc('procedure TImage.DrawText(Text: String; Position: TPoint; Color: TColor); overload', @_LapeImage_DrawText); - addGlobalFunc('procedure TImage.DrawText(Text: String; Box: TBox; Center: Boolean; Color: TColor); overload', @_LapeImage_DrawTextEx); + addGlobalFunc('procedure TImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); overload', @_LapeImage_DrawTextEx); addGlobalFunc('procedure TImage.DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor);', @_LapeImage_DrawTextLines); - addGlobalFunc('procedure TImage.DrawATPA(const ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0);', @_LapeImage_DrawATPA); - addGlobalFunc('procedure TImage.DrawTPA(const TPA: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawTPA); + addGlobalFunc('procedure TImage.DrawATPA(ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0);', @_LapeImage_DrawATPA); + addGlobalFunc('procedure TImage.DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawTPA); - addGlobalFunc('procedure TImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor);', @_LapeImage_DrawCrosshairs); - addGlobalFunc('procedure TImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor);', @_LapeImage_DrawCross); + addGlobalFunc('procedure TImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawCrosshairs); + addGlobalFunc('procedure TImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawCross); - addGlobalFunc('procedure TImage.DrawLine(Start, Stop: TPoint; Color: TColor)', @_LapeImage_DrawLine); + addGlobalFunc('procedure TImage.DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte = 0)', @_LapeImage_DrawLine); + addGlobalFunc('procedure TImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeImage_DrawLineGap); - addGlobalFunc('procedure TImage.DrawPolygon(Points: TPointArray; Color: TColor);', @_LapeImage_DrawPolygon); + addGlobalFunc('procedure TImage.DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawPolygon); addGlobalFunc('procedure TImage.DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawPolygonFilled); - addGlobalFunc('procedure TImage.DrawPolygonInverted(Points: TPointArray; Color: TColor);', @_LapeImage_DrawPolygonInverted); + addGlobalFunc('procedure TImage.DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawPolygonInverted); - addGlobalFunc('procedure TImage.DrawCircle(Center: TPoint; Radius: Integer; Color: TColor); overload', @_LapeImage_DrawCircle1); - addGlobalFunc('procedure TImage.DrawCircle(Circle: TCircle; Color: TColor); overload', @_LapeImage_DrawCircle2); - addGlobalFunc('procedure TImage.DrawCircleFilled(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); overload', @_LapeImage_DrawCircleFilled1); - addGlobalFunc('procedure TImage.DrawCircleFilled(Circle: TCircle; Color: TColor; Alpha: Byte = 0); overload', @_LapeImage_DrawCircleFilled2); - addGlobalFunc('procedure TImage.DrawCircleInverted(Center: TPoint; Radius: Integer; Color: TColor); overload', @_LapeImage_DrawCircleInverted1); - addGlobalFunc('procedure TImage.DrawCircleInverted(Circle: TCircle; Color: TColor); overload', @_LapeImage_DrawCircleInverted2); + addGlobalFunc('procedure TImage.DrawCircle(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeImage_DrawCircle1); + addGlobalFunc('procedure TImage.DrawCircleFilled(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeImage_DrawCircleFilled1); + addGlobalFunc('procedure TImage.DrawCircleInverted(Center: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0)', @_LapeImage_DrawCircleInverted1); - addGlobalFunc('procedure TImage.DrawBox(B: TBox; Color: TColor);', @_LapeImage_DrawBox); + addGlobalFunc('procedure TImage.DrawBox(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawBox); addGlobalFunc('procedure TImage.DrawBoxFilled(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawBoxFilled); - addGlobalFunc('procedure TImage.DrawBoxInverted(B: TBox; Color: TColor);', @_LapeImage_DrawBoxInverted); + addGlobalFunc('procedure TImage.DrawBoxInverted(B: TBox; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawBoxInverted); - addGlobalFunc('procedure TImage.DrawQuad(Quad: TQuad; Color: TColor);', @_LapeImage_DrawQuad); + addGlobalFunc('procedure TImage.DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawQuad); addGlobalFunc('procedure TImage.DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawQuadFilled); - addGlobalFunc('procedure TImage.DrawQuadInverted(Quad: TQuad; Color: TColor);', @_LapeImage_DrawQuadInverted); + addGlobalFunc('procedure TImage.DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte = 0);', @_LapeImage_DrawQuadInverted); addGlobalFunc('procedure TImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor = -1);', @_LapeImage_DrawQuadArray); addGlobalFunc('procedure TImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor = -1);', @_LapeImage_DrawBoxArray); addGlobalFunc('procedure TImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor = -1);', @_LapeImage_DrawPolygonArray); - addGlobalFunc('procedure TImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1); overload;', @_LapeImage_DrawCircleArray1); - addGlobalFunc('procedure TImage.DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor = -1); overload;', @_LapeImage_DrawCircleArray2); + addGlobalFunc('procedure TImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1);', @_LapeImage_DrawCircleArray1); addGlobalFunc('procedure TImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor = -1);', @_LapeImage_DrawCrossArray); addGlobalFunc('procedure TImage.DrawHSLCircle(ACenter: TPoint; Radius: Integer)', @_LapeImage_DrawHSLCircle); @@ -1441,15 +1362,14 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('procedure TImage.Clear(Area: TBox); overload', @_LapeImage_ClearEx); addGlobalFunc('procedure TImage.ClearInverted(Area: TBox);', @_LapeImage_ClearInverted); - addGlobalFunc('procedure TImage.Draw(Image: TImage; X, Y: Integer); overload', @_LapeImage_Draw1); - addGlobalFunc('procedure TImage.Draw(Image: TImage; Position: TPoint); overload', @_LapeImage_Draw2); + addGlobalFunc('procedure TImage.DrawImage(Image: TImage; Position: TPoint; Alpha: Byte = 0)', @_LapeImage_DrawImage); addGlobalFunc('procedure TImage.DrawMatrix(Matrix: TIntegerMatrix); overload', @_LapeImage_DrawMatrixI); addGlobalFunc('procedure TImage.DrawMatrix(Matrix: TSingleMatrix; ColorMapID: Integer = 0); overload', @_LapeImage_DrawMatrixF); addGlobalFunc('procedure TImage.SetSize(AWidth, AHeight: Integer);', @_LapeImage_SetSize); - addGlobalFunc('procedure TImage.SetPersistentMemory(Memory: PtrUInt; AWidth, AHeight: Integer);', @_LapeImage_SetPersistentMemory); - addGlobalFunc('procedure TImage.ResetPersistentMemory;', @_LapeImage_ResetPersistentMemory); + addGlobalFunc('procedure TImage.SetExternalData(Memory: PtrUInt; AWidth, AHeight: Integer);', @_LapeImage_SetExternalData); + addGlobalFunc('procedure TImage.ResetExternalData;', @_LapeImage_ResetExternalData); addGlobalFunc('function TImage.ResizeNN(AWidth, AHeight: Integer): TImage', @_LapeImage_ResizeNN); addGlobalFunc('function TImage.ResizeBilinear(AWidth, AHeight: Integer): TImage', @_LapeImage_ResizeBilinear); @@ -1457,7 +1377,7 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('function TImage.RotateNN(Radians: Single; Expand: Boolean): TImage', @_LapeImage_RotateNN); addGlobalFunc('function TImage.RotateBilinear(Radians: Single; Expand: Boolean): TImage', @_LapeImage_RotateBilinear); - addGlobalFunc('procedure TImage.Fill(Color: TColor);', @_LapeImage_Fill); + addGlobalFunc('procedure TImage.Fill(Color: TColor; Alpha: Byte = 0);', @_LapeImage_Fill); addGlobalFunc('procedure TImage.ReplaceColor(OldColor, NewColor: TColor);', @_LapeImage_ReplaceColor); addGlobalFunc('procedure TImage.ReplaceColors(OldColors, NewColors: TColorArray);', @_LapeImage_ReplaceColors); @@ -1482,17 +1402,16 @@ procedure ImportSimbaImage(Compiler: TSimbaScript_Compiler); addGlobalFunc('function TImage.ThresholdSauvola(Radius: Integer; Invert: Boolean = False; R: Single = 128; K: Single = 0.5): TImage', @_LapeImage_ThresholdSauvola); addGlobalFunc('procedure TImage.Pad(Amount: Integer)', @_LapeImage_Pad); - addGlobalFunc('procedure TImage.LoadFromFile(FileName: String); overload', @_LapeImage_LoadFromFile); - addGlobalFunc('procedure TImage.LoadFromFile(FileName: String; Area: TBox); overload', @_LapeImage_LoadFromFileEx); - addGlobalFunc('procedure TImage.LoadFromString(Str: String)', @_LapeImage_LoadFromString); - addGlobalFunc('procedure TImage.LoadFromData(AWidth, AHeight: Integer; AData: PColorBGRA; DataWidth: Integer)', @_LapeImage_LoadFromData); - addGlobalFunc('procedure TImage.LoadFromImage(Image: TImage);', @_LapeImage_LoadFromImage); - - addGlobalFunc('function TImage.SaveToFile(FileName: String; OverwriteIfExists: Boolean = False): Boolean;', @_LapeImage_SaveToFile); + addGlobalFunc('procedure TImage.Load(FileName: String); overload', @_LapeImage_Load1); + addGlobalFunc('procedure TImage.Load(FileName: String; Area: TBox); overload', @_LapeImage_Load2); + addGlobalFunc('function TImage.Save(FileName: String; OverwriteIfExists: Boolean = False): Boolean;', @_LapeImage_Save); addGlobalFunc('function TImage.SaveToString: String;', @_LapeImage_SaveToString); + addGlobalFunc('procedure TImage.FromString(Str: String)', @_LapeImage_FromString); + addGlobalFunc('procedure TImage.FromData(AWidth, AHeight: Integer; AData: PColorBGRA; DataWidth: Integer)', @_LapeImage_FromData); + addGlobalFunc('function TImage.ToLazBitmap: TLazBitmap;', @_LapeImage_ToLazBitmap); - addGlobalFunc('procedure TImage.LoadFromLazBitmap(LazBitmap: TLazBitmap);', @_LapeImage_LoadFromLazBitmap); + addGlobalFunc('procedure TImage.FromLazBitmap(LazBitmap: TLazBitmap);', @_LapeImage_FromLazBitmap); addGlobalFunc('function TImage.Compare(Other: TImage): Single;', @_LapeImage_Compare); addGlobalFunc('procedure TImage.SaveUnfreedImages(Directory: String); static;', @_LapeImage_SaveUnfreedImages); diff --git a/Source/script/imports/simba.import_matchtemplate.pas b/Source/script/imports/simba.import_matchtemplate.pas index e6da4339f..49f76fccf 100644 --- a/Source/script/imports/simba.import_matchtemplate.pas +++ b/Source/script/imports/simba.import_matchtemplate.pas @@ -34,7 +34,7 @@ implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~ > function TMatchTemplateCache.Create(Image, Template: TIntegerMatrix; Formula: ETMFormula): TMatchTemplateCache; static; *) -procedure _LapeMatchTemplateCache_Create(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateCache_Create1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PMatchTemplateCacheBase(Result)^ := MatchTemplateCache(PIntegerMatrix(Params^[0])^, PIntegerMatrix(Params^[1])^, PTMFormula(Params^[2])^); end; @@ -44,9 +44,9 @@ procedure _LapeMatchTemplateCache_Create(const Params: PParamArray; const Result ~~~~~~~~~~~~~~~~~~~~~~~~~~ > function TMatchTemplateCache.Create(Image, Template: TImage; Formula: ETMFormula): TMatchTemplateCache; static; *) -procedure _LapeMatchTemplateCache_CreateEx(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateCache_Create2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PMatchTemplateCacheBase(Result)^ := MatchTemplateCache(PSimbaImage(Params^[0])^.ToMatrixBGR(), PSimbaImage(Params^[1])^.ToMatrixBGR(), PTMFormula(Params^[2])^); + PMatchTemplateCacheBase(Result)^ := MatchTemplateCache(PSimbaImage(Params^[0])^, PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); end; (* @@ -64,27 +64,17 @@ procedure _LapeMatchTemplateCache_FreeOnTerminate(const Params: PParamArray); LA ~~~~~~~~~~~~~~~~~ > function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; *) -procedure _LapeMatchTemplateMaskCache(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateMaskCache1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PSingleMatrix(Result)^ := MatchTemplateMask(PMatchTemplateCacheBase(Params^[0])^, PIntegerMatrix(Params^[1])^, PTMFormula(Params^[2])^); end; -(* -MatchTemplateMask -~~~~~~~~~~~~~~~~~ -> function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TImage; Formula: ETMFormula): TSingleMatrix; -*) -procedure _LapeMatchTemplateMaskCacheEx(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV -begin - PSingleMatrix(Result)^ := MatchTemplateMask(PMatchTemplateCacheBase(Params^[0])^, PSimbaImage(Params^[1])^.ToMatrixBGR(), PTMFormula(Params^[2])^); -end; - (* MatchTemplateMask ~~~~~~~~~~~~~~~~~ > function MatchTemplateMask(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; *) -procedure _LapeMatchTemplateMask(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateMask1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PSingleMatrix(Result)^ := MatchTemplateMask(PIntegerMatrix(Params^[0])^, PIntegerMatrix(Params^[1])^, PTMFormula(Params^[2])^); end; @@ -94,29 +84,39 @@ procedure _LapeMatchTemplateMask(const Params: PParamArray; const Result: Pointe ~~~~~~~~~~~~~ > function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; *) -procedure _LapeMatchTemplate(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplate1(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin PSingleMatrix(Result)^ := MatchTemplate(PIntegerMatrix(Params^[0])^, PIntegerMatrix(Params^[1])^, PTMFormula(Params^[2])^); end; (* -TImage.MatchTemplate -~~~~~~~~~~~~~~~~~~~~ -> function TImage.MatchTemplate(Template: TImage; Formula: ETMFormula): TSingleMatrix; +MatchTemplateMask +~~~~~~~~~~~~~~~~~ +> function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TImage; Formula: ETMFormula): TSingleMatrix; *) -procedure _LapeMufasaBitmap_MatchTemplate(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateMaskCache2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PSingleMatrix(Result)^ := PSimbaImage(Params^[0])^.MatchTemplate(PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); + PSingleMatrix(Result)^ := MatchTemplateMask(PMatchTemplateCacheBase(Params^[0])^, PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); end; (* -TImage.MatchTemplateMask -~~~~~~~~~~~~~~~~~~~~~~~~ -> function TImage.MatchTemplateMask(Template: TImage; Formula: ETMFormula): TSingleMatrix; +MatchTemplate +~~~~~~~~~~~~~ +> function MatchTemplate(Image, Template: TImage; Formula: ETMFormula): TSingleMatrix; +*) +procedure _LapeMatchTemplate2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +begin + PSingleMatrix(Result)^ := MatchTemplate(PSimbaImage(Params^[0])^, PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); +end; + +(* +MatchTemplateMask +~~~~~~~~~~~~~~~~~ +> function MatchTemplateMask(Image, Template: TImage; Formula: ETMFormula): TSingleMatrix; *) -procedure _LapeMufasaBitmap_MatchTemplateMask(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV +procedure _LapeMatchTemplateMask2(const Params: PParamArray; const Result: Pointer); LAPE_WRAPPER_CALLING_CONV begin - PSingleMatrix(Result)^ := PSimbaImage(Params^[0])^.MatchTemplateMask(PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); + PSingleMatrix(Result)^ := MatchTemplateMask(PSimbaImage(Params^[0])^, PSimbaImage(Params^[1])^, PTMFormula(Params^[2])^); end; procedure ImportMatchTemplate(Compiler: TSimbaScript_Compiler); @@ -130,18 +130,19 @@ procedure ImportMatchTemplate(Compiler: TSimbaScript_Compiler); addClass('TMatchTemplateCache'); addGlobalType('(TM_CCORR, TM_CCORR_NORMED, TM_CCOEFF, TM_CCOEFF_NORMED, TM_SQDIFF, TM_SQDIFF_NORMED)', 'ETMFormula'); - addGlobalFunc('function TMatchTemplateCache.Create(Image, Template: TIntegerMatrix; Formula: ETMFormula): TMatchTemplateCache; static; overload', @_LapeMatchTemplateCache_Create); - addGlobalFunc('function TMatchTemplateCache.Create(Image, Template: TImage; Formula: ETMFormula): TMatchTemplateCache; static; overload', @_LapeMatchTemplateCache_CreateEx); + addGlobalFunc('function TMatchTemplateCache.Create(Image, Template: TIntegerMatrix; Formula: ETMFormula): TMatchTemplateCache; static; overload', @_LapeMatchTemplateCache_Create1); + addGlobalFunc('function TMatchTemplateCache.Create(Image, Template: TImage; Formula: ETMFormula): TMatchTemplateCache; static; overload', @_LapeMatchTemplateCache_Create2); addGlobalFunc('procedure TMatchTemplateCache.FreeOnTerminate(Enable: Boolean);', @_LapeMatchTemplateCache_FreeOnTerminate); - addGlobalFunc('function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMaskCache); - addGlobalFunc('function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TImage; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMaskCacheEx); - addGlobalFunc('function MatchTemplateMask(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMask); - - addGlobalFunc('function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix', @_LapeMatchTemplate); + // TIntegerMatrix + addGlobalFunc('function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMaskCache1); + addGlobalFunc('function MatchTemplateMask(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMask1); + addGlobalFunc('function MatchTemplate(Image, Template: TIntegerMatrix; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplate1); - addGlobalFunc('function TImage.MatchTemplate(Template: TImage; Formula: ETMFormula): TSingleMatrix;', @_LapeMufasaBitmap_MatchTemplate); - addGlobalFunc('function TImage.MatchTemplateMask(Template: TImage; Formula: ETMFormula): TSingleMatrix;', @_LapeMufasaBitmap_MatchTemplateMask); + // TSimbaImage + addGlobalFunc('function MatchTemplateMask(Cache: TMatchTemplateCache; Template: TImage; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMaskCache2); + addGlobalFunc('function MatchTemplateMask(Image, Template: TImage; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplateMask2); + addGlobalFunc('function MatchTemplate(Image, Template: TImage; Formula: ETMFormula): TSingleMatrix; overload', @_LapeMatchTemplate2); ImportingSection := ''; end; diff --git a/Source/script/imports/simba.import_target.pas b/Source/script/imports/simba.import_target.pas index fe55f7903..ec3eab8fa 100644 --- a/Source/script/imports/simba.import_target.pas +++ b/Source/script/imports/simba.import_target.pas @@ -403,7 +403,7 @@ procedure ImportTarget(Compiler: TSimbaScript_Compiler); ' Image: TImage;', 'begin', ' Image := TImage.CreateFromTarget(Target, Bounds);', - ' Self.Draw(Image, P);', + ' Self.DrawImage(Image, P);', ' Image.Free();', 'end;' ]); diff --git a/Source/script/simba.script_pluginmethods.pas b/Source/script/simba.script_pluginmethods.pas index 9604054ae..8e1536507 100644 --- a/Source/script/simba.script_pluginmethods.pas +++ b/Source/script/simba.script_pluginmethods.pas @@ -47,14 +47,11 @@ interface ExternalImage_Create: function(FreeOnTerminate: Boolean): Pointer; cdecl; ExternalImage_SetMemory: procedure(Img: Pointer; Data: PColorBGRA; AWidth, AHeight: Integer); cdecl; - ExternalImage_TryLock: function(Img: Pointer): Boolean; cdecl; - ExternalImage_Lock: procedure(Img: Pointer); cdecl; - ExternalImage_UnLock: procedure(Img: Pointer); cdecl; - ExternalImage_AddCallbackOnUnlock: procedure(Img: Pointer; Callback: TSimbaExternalImageCallback); cdecl; - ExternalImage_RemoveCallbackOnUnlock: procedure(Img: Pointer; Callback: TSimbaExternalImageCallback); cdecl; + ExternalImage_Resize: procedure(Img: Pointer; NewWidth, NewHeight: Integer); cdecl; ExternalImage_SetUserData: procedure(Img: Pointer; UserData: Pointer); cdecl; ExternalImage_GetUserData: function(Img: Pointer): Pointer; cdecl; - // Extend this but do not remove, reorder or change datatypes. + + // Safe to extend this but do not modify! end; var @@ -282,39 +279,19 @@ procedure Plugin_ExternalImage_SetMemory(Img: Pointer; Data: PColorBGRA; AWidth, TSimbaExternalImage(Img).SetMemory(Data, AWidth, AHeight); end; -function Plugin_ExternalImage_TryLock(Img: Pointer): Boolean; cdecl; -begin - Result := TSimbaExternalImage(Img).TryLock(); -end; - -procedure Plugin_ExternalImage_Lock(Img: Pointer); cdecl; -begin - TSimbaExternalImage(Img).Lock(); -end; - -procedure Plugin_ExternalImage_UnLock(Img: Pointer); cdecl; -begin - TSimbaExternalImage(Img).UnLock(); -end; - -procedure Plugin_ExternalImage_AddCallbackOnUnlock(Img: Pointer; Callback: TSimbaExternalImageCallback); cdecl; -begin - TSimbaExternalImage(Img).AddUnlockCallback(Callback); -end; - -procedure Plugin_ExternalImage_RemoveCallbackOnUnlock(Img: Pointer; Callback: TSimbaExternalImageCallback); cdecl; +procedure Plugin_ExternalImage_Resize(Img: Pointer; NewWidth, NewHeight: Integer); cdecl; begin - TSimbaExternalImage(Img).RemoveUnlockCallback(Callback); + TSimbaExternalImage(Img).Resize(NewWidth, NewHeight); end; procedure Plugin_ExternalImage_SetUserData(Img: Pointer; UserData: Pointer); cdecl; begin - TSimbaExternalImage(Img).SetUserData(UserData); + TSimbaExternalImage(Img).UserData := UserData; end; function Plugin_ExternalImage_GetUserData(Img: Pointer): Pointer; cdecl; begin - Result := TSimbaExternalImage(Img).GetUserData(); + Result := TSimbaExternalImage(Img).UserData; end; initialization @@ -344,13 +321,9 @@ initialization SetArrayLength := @Plugin_SetArrayLength; GetArrayLength := @Plugin_GetArrayLength; - ExternalImage_Create := @Plugin_ExternalImage_Create; - ExternalImage_SetMemory := @Plugin_ExternalImage_SetMemory; - ExternalImage_TryLock := @Plugin_ExternalImage_TryLock; - ExternalImage_Lock := @Plugin_ExternalImage_Lock; - ExternalImage_Unlock := @Plugin_ExternalImage_UnLock; - ExternalImage_AddCallbackOnUnlock := @Plugin_ExternalImage_AddCallbackOnUnlock; - ExternalImage_RemoveCallbackOnUnlock := @Plugin_ExternalImage_RemoveCallbackOnUnlock; + ExternalImage_Create := @Plugin_ExternalImage_Create; + ExternalImage_SetMemory := @Plugin_ExternalImage_SetMemory; + ExternalImage_Resize := @Plugin_ExternalImage_Resize; ExternalImage_GetUserData := @Plugin_ExternalImage_GetUserData; ExternalImage_SetUserData := @Plugin_ExternalImage_SetUserData; end; diff --git a/Source/shapebuilders/shapebuilder_boxedge.inc b/Source/shapebuilders/shapebuilder_boxedge.inc new file mode 100644 index 000000000..43a31c1c8 --- /dev/null +++ b/Source/shapebuilders/shapebuilder_boxedge.inc @@ -0,0 +1,23 @@ +{$IF NOT DECLARED(_Row)} + {$ERROR _BoxEdge requires "procedure _Row(Y: Integer; X1, X2: Integer)"} +{$ENDIF} + +procedure _BuildBoxEdge(Box: TBox); +var + Y: Integer; +begin + if (Box.X1 = Box.X2) and (Box.Y1 = Box.Y2) then + _Row(Box.Y1, Box.X1, Box.X1) + else + begin + _Row(Box.Y1, Box.X1, Box.X2); // top horz + _Row(Box.Y2, Box.X1, Box.X2); // bottom horz + + for Y := Box.Y1 + 1 to Box.Y2 - 1 do + begin + _Row(Y, Box.X1, Box.X1); // left vert + _Row(Y, Box.X2, Box.X2); // right vert + end; + end; +end; + diff --git a/Source/shapebuilders/shapebuilder_boxfilled.inc b/Source/shapebuilders/shapebuilder_boxfilled.inc new file mode 100644 index 000000000..74aeaaef9 --- /dev/null +++ b/Source/shapebuilders/shapebuilder_boxfilled.inc @@ -0,0 +1,12 @@ +{$IF NOT DECLARED(_Row)} + {$ERROR _BoxFilled requires "procedure _Row(Y: Integer; X1, X2: Integer)"} +{$ENDIF} + +procedure _BuildBoxFilled(Box: TBox); +var + Y: Integer; +begin + for Y := Box.Y1 to Box.Y2 do + _Row(Y, Box.X1, Box.X2); +end; + diff --git a/Source/image/circle.inc b/Source/shapebuilders/shapebuilder_circle.inc similarity index 75% rename from Source/image/circle.inc rename to Source/shapebuilders/shapebuilder_circle.inc index 2bec10b02..59b819ba4 100644 --- a/Source/image/circle.inc +++ b/Source/shapebuilders/shapebuilder_circle.inc @@ -1,8 +1,10 @@ // https://zingl.github.io/bresenham.js -// Requires method `procedure _SetPixel(X, Y: Integer);` +{$IF NOT DECLARED(_SetPixel)} + {$ERROR _CircleFilled requires "procedure _SetPixel(X, Y: Integer);"} +{$ENDIF} -procedure _Circle(CenterX, CenterY, Radius: Integer); +procedure _BuildCircle(CenterX, CenterY, Radius: Integer); var X, Y, Err: Integer; begin diff --git a/Source/image/circlefilled.inc b/Source/shapebuilders/shapebuilder_circlefilled.inc similarity index 55% rename from Source/image/circlefilled.inc rename to Source/shapebuilders/shapebuilder_circlefilled.inc index 487742662..7811da0b0 100644 --- a/Source/image/circlefilled.inc +++ b/Source/shapebuilders/shapebuilder_circlefilled.inc @@ -1,6 +1,8 @@ -// Requires method `procedure _Row(const Y: Integer; X1, X2: Integer);` +{$IF NOT DECLARED(_Row)} + {$ERROR _CircleFilled requires "procedure _Row(Y: Integer; X1, X2: Integer)"} +{$ENDIF} -procedure _CircleFilled(CenterX, CenterY, Radius: Integer); +procedure _BuildCircleFilled(CenterX, CenterY, Radius: Integer); var X, Y: Integer; Radius2: Integer; diff --git a/Source/shapebuilders/shapebuilder_line.inc b/Source/shapebuilders/shapebuilder_line.inc new file mode 100644 index 000000000..8166aa361 --- /dev/null +++ b/Source/shapebuilders/shapebuilder_line.inc @@ -0,0 +1,38 @@ +{$IF NOT DECLARED(_Pixel)} + {$ERROR _BuildLine requires "procedure _Pixel(X, Y: Integer)"} +{$ENDIF} + +procedure _BuildLine(Start, Stop: TPoint); +var + dx,dy,step,I: Integer; + rx,ry,x,y: Single; +begin + dx := (Stop.X - Start.X); + dy := (Stop.Y - Start.Y); + if (Abs(dx) > Abs(dy)) then + step := Abs(dx) + else + step := Abs(dy); + + if (step = 0) then + begin + rx := dx; + ry := dy; + end else + begin + rx := dx / step; + ry := dy / step; + end; + x := Start.X; + y := Start.Y; + + _Pixel(Round(x), Round(y)); + for I := 1 to step do + begin + x := x + rx; + y := y + ry; + + _Pixel(Round(x), Round(y)); + end; +end; + diff --git a/Source/shapebuilders/shapebuilder_linegap.inc b/Source/shapebuilders/shapebuilder_linegap.inc new file mode 100644 index 000000000..5497eecbc --- /dev/null +++ b/Source/shapebuilders/shapebuilder_linegap.inc @@ -0,0 +1,47 @@ +{$IF NOT DECLARED(_Pixel)} + {$ERROR _BuildLineGap requires "procedure _Pixel(X, Y: Integer)"} +{$ENDIF} + +procedure _BuildLineGap(Start, Stop: TPoint; GapSize: Integer); +var + dx,dy,step,I,Gap: Integer; + rx,ry,x,y: Single; +begin + dx := (Stop.X - Start.X); + dy := (Stop.Y - Start.Y); + if (Abs(dx) > Abs(dy)) then + step := Abs(dx) + else + step := Abs(dy); + + if (step = 0) then + begin + rx := dx; + ry := dy; + end else + begin + rx := dx / step; + ry := dy / step; + end; + x := Start.X; + y := Start.Y; + Gap := 0; + + _Pixel(Round(x), Round(y)); + for I := 1 to step do + begin + x := x + rx; + y := y + ry; + + Inc(Gap); + if (Gap > 0) then + begin + _Pixel(Round(x), Round(y)); + if (Gap > GapSize) then + Gap := -GapSize; + end; + end; + _Pixel(Round(x), Round(y)); +end; + + diff --git a/Source/image/polygonfilled.inc b/Source/shapebuilders/shapebuilder_polygonfilled.inc similarity index 91% rename from Source/image/polygonfilled.inc rename to Source/shapebuilders/shapebuilder_polygonfilled.inc index 2aa50fdb2..65be52f80 100644 --- a/Source/image/polygonfilled.inc +++ b/Source/shapebuilders/shapebuilder_polygonfilled.inc @@ -1,4 +1,6 @@ -// Requires method `procedure _Row(const Y: Integer; X1, X2: Integer);` +{$IF NOT DECLARED(_Row)} + {$ERROR _PolygonFilled requires "procedure _Row(Y: Integer; X1, X2: Integer)"} +{$ENDIF} (* Calculates the intersection points of a vertical (ACoordIsX = true) or horizontal @@ -66,7 +68,7 @@ end; the even-odd rule http://www.tutorialspoint.com/computer_graphics/polygon_filling_algorithm *) -procedure _PolygonFilled(APoints: TPointArray; ARect: TRect); +procedure _BuildPolygonFilled(APoints: TPointArray; ARect: TRect); var scanlineY, scanLineY1, scanLineY2: Integer; lPoints, pts: TPointFArray; @@ -105,18 +107,18 @@ begin lPoints := GetLinePolygonIntersectionPoints(scanlineY, pts, false); if Length(lPoints) < 2 then begin - inc(scanlineY); + Inc(scanlineY); Continue; end; // Draw lines between intersection points, skip every second pair I := 0; while I < High(lPoints) do begin - _Row(Round(lPoints[I].Y), round(lPoints[I].X), round(lPoints[I+1].X)); - inc(I, 2); + _Row(Round(lPoints[I].Y), Round(lPoints[I].X), Round(lPoints[I+1].X)); + Inc(I, 2); end; // Proceed to next scan line - inc(scanlineY); + Inc(scanlineY); end; end; diff --git a/Source/simba.base.pas b/Source/simba.base.pas index ee3658a0e..6bae509f2 100644 --- a/Source/simba.base.pas +++ b/Source/simba.base.pas @@ -232,16 +232,21 @@ TColorHSL = record H,S,L: Single; end; + TColorBGR = packed record + B,G,R: Byte; + end; + PColorBGR = ^TColorBGR; + TColorARGB = packed record case Byte of 0: (A, R, G, B: Byte); - 1: (AsInteger: Integer); + 1: (AsInteger: UInt32); end; TColorBGRA = packed record case Byte of 0: (B, G, R, A: Byte); - 1: (AsInteger: Integer); + 1: (AsInteger: UInt32); end; PColorARGB = ^TColorARGB; diff --git a/Source/simba.externalimage.pas b/Source/simba.externalimage.pas index 2d5944e37..3d469b0c1 100644 --- a/Source/simba.externalimage.pas +++ b/Source/simba.externalimage.pas @@ -4,7 +4,7 @@ License: GNU General Public License (https://www.gnu.org/licenses/gpl-3.0) -------------------------------------------------------------------------- - provides a way to draw on a image which data is externally allocated. + Provides a way to draw on an image which data is externally allocated. } unit simba.externalimage; @@ -14,47 +14,55 @@ interface uses Classes, SysUtils, Graphics, - simba.base, simba.baseclass, simba.image, simba.simplelock; + simba.base, simba.baseclass, simba.image, simba.image_utils, simba.image_textdrawer, + simba.simplelock; type - TSimbaExternalImageCallback = procedure(Image: Pointer; UserData: Pointer); cdecl; - PSimbaExternalImage = ^TSimbaExternalImage; TSimbaExternalImage = class(TSimbaBaseClass) protected - FImage: TSimbaImage; FLock: TSimpleEnterableLock; - FLockCount: Integer; + + FBackBuffer: TSimbaImage; + + FData: PColorBGRA; + FWidth, FHeight: Integer; FUserData: Pointer; + FInUpdate: Integer; + + FDirty: Boolean; + FDirtyBox: TBox; + + procedure CheckInUpdate; inline; - FUnlockCallbacks: array of TSimbaExternalImageCallback; + procedure addDirty(b: TBox); inline; + procedure addAllDirty; inline; + + procedure Flush; function GetFontAntialiasing: Boolean; function GetFontBold: Boolean; function GetFontItalic: Boolean; function GetFontName: String; function GetFontSize: Single; + function GetUserData: Pointer; + procedure SetUserData(UserData: Pointer); procedure SetFontAntialiasing(Value: Boolean); procedure SetFontBold(Value: Boolean); procedure SetFontItalic(Value: Boolean); procedure SetFontName(Value: String); procedure SetFontSize(Value: Single); public - constructor Create; + constructor Create; reintroduce; destructor Destroy; override; - procedure AddUnlockCallback(Proc: TSimbaExternalImageCallback); - procedure RemoveUnlockCallback(Proc: TSimbaExternalImageCallback); - - function InternalImage: TSimbaImage; + procedure BeginUpdate; + procedure EndUpdate; - function TryLock: Boolean; - procedure Lock; - procedure Unlock; - - function Width: Integer; inline; - function Height: Integer; inline; + property Width: Integer read FWidth; + property Height: Integer read FHeight; + property UserData: Pointer read GetUserData write SetUserData; property FontName: String read GetFontName write SetFontName; property FontSize: Single read GetFontSize write SetFontSize; @@ -62,581 +70,537 @@ TSimbaExternalImage = class(TSimbaBaseClass) property FontBold: Boolean read GetFontBold write SetFontBold; property FontItalic: Boolean read GetFontItalic write SetFontItalic; - procedure SetUserData(UserData: Pointer); - function GetUserData: Pointer; - procedure SetMemory(Data: PColorBGRA; AWidth, AHeight: Integer); + procedure Resize(NewWidth, NewHeight: Integer); + + procedure SetAlpha(Value: Byte); overload; + procedure SetAlpha(Points: TPointArray; Value: Byte); overload; + procedure SetAlpha(Color: TColor; Value: Byte); overload; + + // Point + procedure DrawATPA(ATPA: T2DPointArray; Color: TColor = -1; Alpha: Byte = 0); + procedure DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte = 0); + + // Line + procedure DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte = 0); + procedure DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte = 0); + + procedure Clear; overload; + procedure Clear(Box: TBox); overload; + procedure ClearInverted(Box: TBox); + + procedure Fill(Color: TColor; Alpha: Byte = 0); function TextWidth(Text: String): Integer; function TextHeight(Text: String): Integer; function TextSize(Text: String): TPoint; procedure DrawText(Text: String; Position: TPoint; Color: TColor); overload; - procedure DrawText(Text: String; Box: TBox; ACenter: Boolean; Color: TColor); overload; + procedure DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); overload; procedure DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor); - procedure Fill(Color: TColor); - - procedure Clear; - procedure Clear(Area: TBox); - procedure ClearInverted(Area: TBox); - - procedure Draw(Image: TSimbaImage; Position: TPoint); - - procedure DrawATPA(ATPA: T2DPointArray; Color: TColor = -1); - procedure DrawTPA(Points: TPointArray; Color: TColor); + // Image + procedure DrawImage(Image: TSimbaImage; Location: TPoint; Alpha: Byte = 0); - procedure DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); - procedure DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); + // Box + procedure DrawBox(Box: TBox; Color: TColor; Alpha: Byte = 0); + procedure DrawBoxFilled(Box: TBox; Color: TColor; Alpha: Byte = 0); + procedure DrawBoxInverted(Box: TBox; Color: TColor; Alpha: Byte = 0); - procedure DrawLine(Start, Stop: TPoint; Color: TColor); + // Poly + procedure DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte = 0); + procedure DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte = 0); + procedure DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte = 0); - procedure DrawPolygon(Points: TPointArray; Color: TColor); - procedure DrawPolygonFilled(Points: TPointArray; Color: TColor); - procedure DrawPolygonInverted(Points: TPointArray; Color: TColor); + // Quad + procedure DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte = 0); + procedure DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte = 0); + procedure DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte = 0); - procedure DrawCircle(Circle: TCircle; Color: TColor); - procedure DrawCircleFilled(Circle: TCircle; Color: TColor); - procedure DrawCircleInverted(Circle: TCircle; Color: TColor); + // Circle + procedure DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); + procedure DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte = 0); - procedure DrawBox(B: TBox; Color: TColor); - procedure DrawBoxFilled(B: TBox; Color: TColor); - procedure DrawBoxInverted(B: TBox; Color: TColor); - - procedure DrawQuad(Quad: TQuad; Color: TColor); - procedure DrawQuadFilled(Quad: TQuad; Color: TColor); - procedure DrawQuadInverted(Quad: TQuad; Color: TColor); + // Antialiased + procedure DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single = 1.5); + procedure DrawEllipseAA(ACenter: TPoint; XRadius, YRadius: Integer; Color: TColor; Thickness: Single = 1.5); + procedure DrawCircleAA(ACenter: TPoint; Radius: Integer; Color: TColor; Thickness: Single = 1.5); + // Arrays procedure DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor = -1); procedure DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor = -1); procedure DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor = -1); - procedure DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor = -1); + procedure DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor = -1); procedure DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor = -1); end; implementation -function TSimbaExternalImage.GetFontAntialiasing: Boolean; +uses + Math, + simba.box, simba.quad, + simba.array_point, simba.array_pointarray, simba.array_box; + +procedure TSimbaExternalImage.CheckInUpdate; begin - Lock(); - try - Result := FImage.FontAntialiasing; - finally - Unlock(); + if (FInUpdate <= 0) then + SimbaException('Not in BeginUpdate/EndUpdate'); +end; + +procedure TSimbaExternalImage.addDirty(b: TBox); +begin + if (FInUpdate <= 0) then + SimbaException('Not in BeginUpdate/EndUpdate'); + + if FDirty then + FDirtyBox := FDirtyBox.Combine(b.Normalize()) + else + begin + FDirty := True; + FDirtyBox := b.Normalize(); end; end; +procedure TSimbaExternalImage.addAllDirty; +begin + if (FInUpdate <= 0) then + SimbaException('Not in BeginUpdate/EndUpdate'); + + FDirty := True; + FDirtyBox.X1 := -$FFFFFF; + FDirtyBox.Y1 := -$FFFFFF; + FDirtyBox.X2 := $FFFFFF; + FDirtyBox.Y2 := $FFFFFF; +end; + +procedure TSimbaExternalImage.Flush; +var + Y: Integer; +begin + if not FDirty then + Exit; + + FDirty := False; + FDirtyBox := FDirtyBox.Expand(1).Clip(TBox.Create(0, 0, FWidth-1, FHeight-1)); + + for Y := FDirtyBox.Y1 to FDirtyBox.Y2 do + Move(FBackBuffer.Data[Y * FWidth + FDirtyBox.X1], FData[Y * FWidth + FDirtyBox.X1], (FDirtyBox.X2 - FDirtyBox.X1) * SizeOf(TColorBGRA)); +end; + +function TSimbaExternalImage.GetFontAntialiasing: Boolean; +begin + Result := FBackBuffer.FontAntialiasing; +end; + function TSimbaExternalImage.GetFontBold: Boolean; begin - Lock(); - try - Result := FImage.FontBold; - finally - Unlock(); - end; + Result := FBackBuffer.FontBold; end; function TSimbaExternalImage.GetFontItalic: Boolean; begin - Lock(); - try - Result := FImage.FontItalic; - finally - Unlock(); - end; + Result := FBackBuffer.FontItalic; end; function TSimbaExternalImage.GetFontName: String; begin - Lock(); - try - Result := FImage.FontName; - finally - Unlock(); - end; + Result := FBackBuffer.FontName; end; function TSimbaExternalImage.GetFontSize: Single; begin - Lock(); - try - Result := FImage.FontSize; - finally - Unlock(); - end; + Result := FBackBuffer.FontSize; end; procedure TSimbaExternalImage.SetFontAntialiasing(Value: Boolean); begin - Lock(); - try - FImage.FontAntialiasing := Value; - finally - Unlock(); - end; + FBackBuffer.FontAntialiasing := Value; end; procedure TSimbaExternalImage.SetFontBold(Value: Boolean); begin - Lock(); - try - FImage.FontBold := Value; - finally - Unlock(); - end; + FBackBuffer.FontBold := Value; end; procedure TSimbaExternalImage.SetFontItalic(Value: Boolean); begin - Lock(); - try - FImage.FontItalic := Value; - finally - Unlock(); - end; + FBackBuffer.FontItalic := Value; end; procedure TSimbaExternalImage.SetFontName(Value: String); begin - Lock(); - try - FImage.FontName := Value; - finally - Unlock(); - end; + FBackBuffer.FontName := Value; end; procedure TSimbaExternalImage.SetFontSize(Value: Single); begin - Lock(); - try - FImage.FontSize := Value; - finally - Unlock(); - end; + FBackBuffer.FontSize := Value; end; constructor TSimbaExternalImage.Create; begin inherited Create(); - FImage := TSimbaImage.Create(); + FBackBuffer := TSimbaImage.Create(); + FBackBuffer.DefaultPixel.A := ALPHA_TRANSPARENT; end; destructor TSimbaExternalImage.Destroy; begin - if Assigned(FImage) then - FreeAndNil(FImage); + if Assigned(FBackBuffer) then + FreeAndNil(FBackBuffer); inherited Destroy(); end; -procedure TSimbaExternalImage.AddUnlockCallback(Proc: TSimbaExternalImageCallback); +procedure TSimbaExternalImage.BeginUpdate; begin FLock.Enter(); - FUnlockCallbacks += [Proc]; + + Inc(FInUpdate); + if (FInUpdate > 1) then + Exit; + + FDirty := False; +end; + +procedure TSimbaExternalImage.EndUpdate; +begin + if (FInUpdate <= 0) then + SimbaException('Not in BeginUpdate'); + + Dec(FInUpdate); + if (FInUpdate > 0) then + Exit; + + Flush(); + FLock.Leave(); end; -procedure TSimbaExternalImage.RemoveUnlockCallback(Proc: TSimbaExternalImageCallback); +procedure TSimbaExternalImage.SetMemory(Data: PColorBGRA; AWidth, AHeight: Integer); +begin + BeginUpdate(); + try + FData := Data; + FWidth := AWidth; + FHeight := AHeight; + + FBackBuffer.SetSize(AWidth, AHeight); + finally + EndUpdate(); + end; +end; + +procedure TSimbaExternalImage.Resize(NewWidth, NewHeight: Integer); var - i: Integer; + Y: Integer; begin - FLock.Enter(); - for i := High(FUnlockCallbacks) downto 0 do - if (FUnlockCallbacks[i] = Proc) then - Delete(FUnlockCallbacks, i, 1); - FLock.Leave(); + BeginUpdate(); + try + FWidth := NewWidth; + FHeight := NewHeight; + + FBackBuffer.SetSize(FWidth, FHeight); + for Y := 0 to FHeight - 1 do + Move(FBackBuffer.Data[Y * FWidth], FData[Y * FWidth], FWidth * SizeOf(TColorBGRA)); + finally + EndUpdate(); + end; end; -function TSimbaExternalImage.TryLock: Boolean; +procedure TSimbaExternalImage.SetAlpha(Value: Byte); begin - Result := FLock.TryEnter(); - if Result then - Inc(FLockCount); + addAllDirty(); + + FBackBuffer.SetAlpha(Value); end; -procedure TSimbaExternalImage.Lock; +procedure TSimbaExternalImage.SetAlpha(Points: TPointArray; Value: Byte); begin - FLock.Enter(); - Inc(FLockCount); + addAllDirty(); + + FBackBuffer.SetAlpha(Points, Value); end; -procedure TSimbaExternalImage.Unlock; -var - i: Integer; +procedure TSimbaExternalImage.SetAlpha(Color: TColor; Value: Byte); begin - Dec(FLockCount); - if (FLockCount = 0) then - for i := 0 to High(FUnlockCallbacks) do - FUnlockCallbacks[i](Self, FUserData); - FLock.Leave(); + addAllDirty(); + + FBackBuffer.SetAlpha(Color, Value); end; -function TSimbaExternalImage.InternalImage: TSimbaImage; +procedure TSimbaExternalImage.DrawATPA(ATPA: T2DPointArray; Color: TColor; Alpha: Byte); begin - Result := FImage; + addDirty(ATPA.Bounds()); + + FBackBuffer.DrawATPA(ATPA, Color, Alpha); end; -function TSimbaExternalImage.Width: Integer; +procedure TSimbaExternalImage.DrawTPA(TPA: TPointArray; Color: TColor; Alpha: Byte); begin - Result := FImage.Width; + addDirty(TPA.Bounds()); + + FBackBuffer.DrawTPA(TPA, Color, Alpha); end; -function TSimbaExternalImage.Height: Integer; +procedure TSimbaExternalImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor; Alpha: Byte); begin - Result := FImage.Height; + addDirty(TBox.Create(ACenter, Size, Size)); + + FBackBuffer.DrawCrosshairs(ACenter, Size, Color, Alpha); end; -procedure TSimbaExternalImage.SetMemory(Data: PColorBGRA; AWidth, AHeight: Integer); +procedure TSimbaExternalImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.ResetExternalData(); - FImage.SetExternalData(Data, AWidth, AHeight); - finally - Unlock(); - end; + addDirty(TBox.Create(ACenter, Radius, Radius)); + + FBackBuffer.DrawCross(ACenter, Radius, Color, Alpha); end; -procedure TSimbaExternalImage.SetUserData(UserData: Pointer); +procedure TSimbaExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor; Alpha: Byte); begin - FUserData := UserData; + addDirty(TBox.Create(Start.X, Start.Y, Stop.X, Stop.Y)); + + FBackBuffer.DrawLine(Start, Stop, Color, Alpha); end; -function TSimbaExternalImage.GetUserData: Pointer; +procedure TSimbaExternalImage.DrawLineGap(Start, Stop: TPoint; GapSize: Integer; Color: TColor; Alpha: Byte); begin - Result := FUserData; + addDirty(TBox.Create(Start.X, Start.Y, Stop.X, Stop.Y)); + + FBackBuffer.DrawLineGap(Start, Stop, GapSize, Color, Alpha); end; -function TSimbaExternalImage.TextWidth(Text: String): Integer; +procedure TSimbaExternalImage.Clear; begin - Lock(); - try - Result := FImage.TextWidth(Text); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.Clear(); end; -function TSimbaExternalImage.TextHeight(Text: String): Integer; +procedure TSimbaExternalImage.Clear(Box: TBox); begin - Lock(); - try - Result := FImage.TextHeight(Text); - finally - Unlock(); - end; + addDirty(Box); + + FBackBuffer.Clear(Box); end; -function TSimbaExternalImage.TextSize(Text: String): TPoint; +procedure TSimbaExternalImage.ClearInverted(Box: TBox); begin - Lock(); - try - Result := FImage.TextSize(Text); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.ClearInverted(Box); end; -procedure TSimbaExternalImage.DrawText(Text: String; Position: TPoint; Color: TColor); +procedure TSimbaExternalImage.Fill(Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawText(Text, Position, Color); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.Fill(Color, Alpha); end; -procedure TSimbaExternalImage.DrawText(Text: String; Box: TBox; ACenter: Boolean; Color: TColor); +procedure TSimbaExternalImage.SetUserData(UserData: Pointer); begin - Lock(); - try - FImage.DrawText(Text, Box, ACenter, Color); - finally - Unlock(); - end; + FUserData := UserData; end; -procedure TSimbaExternalImage.DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor); +function TSimbaExternalImage.GetUserData: Pointer; begin - Lock(); - try - FImage.DrawTextLines(Text, Position, Color); - finally - Unlock(); - end; + Result := FUserData; end; -procedure TSimbaExternalImage.Fill(Color: TColor); +function TSimbaExternalImage.TextWidth(Text: String): Integer; begin - Lock(); - try - FImage.Fill(Color); - finally - Unlock(); - end; + Result := FBackBuffer.TextWidth(Text); end; -procedure TSimbaExternalImage.Clear; +function TSimbaExternalImage.TextHeight(Text: String): Integer; begin - Lock(); - try - FImage.Clear(); - finally - Unlock(); - end; + Result := FBackBuffer.TextHeight(Text); end; -procedure TSimbaExternalImage.Clear(Area: TBox); +function TSimbaExternalImage.TextSize(Text: String): TPoint; begin - Lock(); - try - FImage.Clear(Area); - finally - Unlock(); - end; + Result := FBackBuffer.TextSize(Text); end; -procedure TSimbaExternalImage.ClearInverted(Area: TBox); +procedure TSimbaExternalImage.DrawText(Text: String; Position: TPoint; Color: TColor); begin - Lock(); - try - FImage.ClearInverted(Area); - finally - Unlock(); - end; + CheckInUpdate(); + + FBackBuffer.DrawText(Text, Position, Color); + addDirty(FBackBuffer.TextDrawer.DrawnBox); end; -procedure TSimbaExternalImage.Draw(Image: TSimbaImage; Position: TPoint); +procedure TSimbaExternalImage.DrawText(Text: String; Box: TBox; Alignments: ETextDrawAlignSet; Color: TColor); begin - Lock(); - try - FImage.Draw(Image, Position); - finally - Unlock(); - end; + CheckInUpdate(); + + FBackBuffer.DrawText(Text, Box, Alignments, Color); + addDirty(FBackBuffer.TextDrawer.DrawnBox); end; -procedure TSimbaExternalImage.DrawATPA(ATPA: T2DPointArray; Color: TColor); +procedure TSimbaExternalImage.DrawTextLines(Text: TStringArray; Position: TPoint; Color: TColor); begin - Lock(); - try - FImage.DrawATPA(ATPA, Color); - finally - Unlock(); - end; + CheckInUpdate(); + + FBackBuffer.DrawTextLines(Text, Position, Color); + addDirty(FBackBuffer.TextDrawer.DrawnBox); end; -procedure TSimbaExternalImage.DrawTPA(Points: TPointArray; Color: TColor); +procedure TSimbaExternalImage.DrawImage(Image: TSimbaImage; Location: TPoint; Alpha: Byte); begin - Lock(); - try - FImage.DrawTPA(Points, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(Location.X, Location.Y, Location.X + Image.Width, Location.Y + Image.Height)); + + FBackBuffer.DrawImage(Image, Location, Alpha); end; -procedure TSimbaExternalImage.DrawCrosshairs(ACenter: TPoint; Size: Integer; Color: TColor); +procedure TSimbaExternalImage.DrawBox(Box: TBox; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawCrosshairs(ACenter, Size, Color); - finally - Unlock(); - end; + addDirty(Box); + + FBackBuffer.DrawBox(Box, Color, Alpha); end; -procedure TSimbaExternalImage.DrawCross(ACenter: TPoint; Radius: Integer; Color: TColor); +procedure TSimbaExternalImage.DrawBoxFilled(Box: TBox; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawCross(ACenter, Radius, Color); - finally - Unlock(); - end; + addDirty(Box); + + FBackBuffer.DrawBoxFilled(Box, Color, Alpha); end; -procedure TSimbaExternalImage.DrawLine(Start, Stop: TPoint; Color: TColor); +procedure TSimbaExternalImage.DrawBoxInverted(Box: TBox; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawLine(Start, Stop, Color); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.DrawBoxInverted(Box, Color, Alpha); end; -procedure TSimbaExternalImage.DrawPolygon(Points: TPointArray; Color: TColor); +procedure TSimbaExternalImage.DrawPolygon(Points: TPointArray; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawPolygon(Points, Color); - finally - Unlock(); - end; + addDirty(Points.Bounds); + + FBackBuffer.DrawPolygon(Points, Color, Alpha); end; -procedure TSimbaExternalImage.DrawPolygonFilled(Points: TPointArray; Color: TColor); +procedure TSimbaExternalImage.DrawPolygonFilled(Points: TPointArray; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawPolygonFilled(Points, Color); - finally - Unlock(); - end; + addDirty(Points.Bounds); + + FBackBuffer.DrawPolygonFilled(Points, Color, Alpha); end; -procedure TSimbaExternalImage.DrawPolygonInverted(Points: TPointArray; Color: TColor); +procedure TSimbaExternalImage.DrawPolygonInverted(Points: TPointArray; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawPolygonInverted(Points, Color); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.DrawPolygonInverted(Points, Color, Alpha); end; -procedure TSimbaExternalImage.DrawCircle(Circle: TCircle; Color: TColor); +procedure TSimbaExternalImage.DrawQuad(Quad: TQuad; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawCircle(Circle, Color); - finally - Unlock(); - end; + addDirty(Quad.Bounds); + + FBackBuffer.DrawQuad(Quad, Color, Alpha); end; -procedure TSimbaExternalImage.DrawCircleFilled(Circle: TCircle; Color: TColor); +procedure TSimbaExternalImage.DrawQuadFilled(Quad: TQuad; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawCircleFilled(Circle, Color); - finally - Unlock(); - end; + addDirty(Quad.Bounds); + + FBackBuffer.DrawQuadFilled(Quad, Color, Alpha); end; -procedure TSimbaExternalImage.DrawCircleInverted(Circle: TCircle; Color: TColor); +procedure TSimbaExternalImage.DrawQuadInverted(Quad: TQuad; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawCircleInverted(Circle, Color); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.DrawQuadInverted(Quad, Color, Alpha); end; -procedure TSimbaExternalImage.DrawBox(B: TBox; Color: TColor); +procedure TSimbaExternalImage.DrawCircle(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawBox(B, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(ACenter, Radius, Radius)); + + FBackBuffer.DrawCircle(ACenter, Radius, Color, Alpha); end; -procedure TSimbaExternalImage.DrawBoxFilled(B: TBox; Color: TColor); +procedure TSimbaExternalImage.DrawCircleFilled(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawBoxFilled(B, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(ACenter, Radius, Radius)); + + FBackBuffer.DrawCircleFilled(ACenter, Radius, Color, Alpha); end; -procedure TSimbaExternalImage.DrawBoxInverted(B: TBox; Color: TColor); +procedure TSimbaExternalImage.DrawCircleInverted(ACenter: TPoint; Radius: Integer; Color: TColor; Alpha: Byte); begin - Lock(); - try - FImage.DrawBoxInverted(B, Color); - finally - Unlock(); - end; + addAllDirty(); + + FBackBuffer.DrawCircleInverted(ACenter, Radius, Color, Alpha); end; -procedure TSimbaExternalImage.DrawQuad(Quad: TQuad; Color: TColor); +procedure TSimbaExternalImage.DrawLineAA(Start, Stop: TPoint; Color: TColor; Thickness: Single); begin - Lock(); - try - FImage.DrawQuad(Quad, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(Ceil(Start.X - Thickness), Ceil(Start.Y - Thickness), Ceil(Stop.X - Thickness), Ceil(Stop.Y - Thickness))); + addDirty(TBox.Create(Ceil(Start.X + Thickness), Ceil(Start.Y + Thickness), Ceil(Stop.X + Thickness), Ceil(Stop.Y + Thickness))); + + FBackBuffer.DrawLineAA(Start, Stop, Color, Thickness); end; -procedure TSimbaExternalImage.DrawQuadFilled(Quad: TQuad; Color: TColor); +procedure TSimbaExternalImage.DrawEllipseAA(ACenter: TPoint; XRadius, YRadius: Integer; Color: TColor; Thickness: Single); begin - Lock(); - try - FImage.DrawQuadFilled(Quad, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(ACenter, Ceil(XRadius + Thickness), Ceil(YRadius + Thickness))); + + FBackBuffer.DrawEllipseAA(ACenter, XRadius, YRadius, Color, Thickness); end; -procedure TSimbaExternalImage.DrawQuadInverted(Quad: TQuad; Color: TColor); +procedure TSimbaExternalImage.DrawCircleAA(ACenter: TPoint; Radius: Integer; Color: TColor; Thickness: Single); begin - Lock(); - try - FImage.DrawQuadInverted(Quad, Color); - finally - Unlock(); - end; + addDirty(TBox.Create(ACenter, Ceil(Radius + Thickness), Ceil(Radius + Thickness))); + + FBackBuffer.DrawCircleAA(ACenter, Radius, Color, Thickness); end; procedure TSimbaExternalImage.DrawQuadArray(Quads: TQuadArray; Filled: Boolean; Color: TColor); +var + I: Integer; begin - Lock(); - try - FImage.DrawQuadArray(Quads, Filled, Color); - finally - Unlock(); - end; + for I := 0 to High(Quads) do + addDirty(Quads[I].Bounds()); + + FBackBuffer.DrawQuadArray(Quads, Filled, Color); end; procedure TSimbaExternalImage.DrawBoxArray(Boxes: TBoxArray; Filled: Boolean; Color: TColor); begin - Lock(); - try - FImage.DrawBoxArray(Boxes, Filled, Color); - finally - Unlock(); - end; + addDirty(Boxes.Merge()); + + FBackBuffer.DrawBoxArray(Boxes, Filled, Color); end; procedure TSimbaExternalImage.DrawPolygonArray(Polygons: T2DPointArray; Filled: Boolean; Color: TColor); begin - Lock(); - try - FImage.DrawPolygonArray(Polygons, Filled, Color); - finally - Unlock(); - end; + addDirty(Polygons.Bounds()); + + FBackBuffer.DrawPolygonArray(Polygons, Filled, Color); end; -procedure TSimbaExternalImage.DrawCircleArray(Circles: TCircleArray; Filled: Boolean; Color: TColor); +procedure TSimbaExternalImage.DrawCircleArray(Centers: TPointArray; Radius: Integer; Filled: Boolean; Color: TColor); begin - Lock(); - try - FImage.DrawCircleArray(Circles, Filled, Color); - finally - Unlock(); - end; + addDirty(Centers.Bounds().Expand(Radius)); + + FBackBuffer.DrawCircleArray(Centers, Radius, Filled, Color); end; procedure TSimbaExternalImage.DrawCrossArray(Points: TPointArray; Radius: Integer; Color: TColor); begin - Lock(); - try - FImage.DrawCrossArray(Points, Radius, Color); - finally - Unlock(); - end; + addDirty(Points.Bounds().Expand(Radius)); + + FBackBuffer.DrawCrossArray(Points, Radius, Color); end; end. diff --git a/Source/simba.finder.pas b/Source/simba.finder.pas index eae7e8b42..1c0252ecd 100644 --- a/Source/simba.finder.pas +++ b/Source/simba.finder.pas @@ -465,7 +465,7 @@ function TSimbaFinder.FindTemplate(Templ: TSimbaImage; MinMatch: Single; Bounds: if GetDataAsBitmap(Bounds, Bitmap) then try - Mat := Bitmap.MatchTemplate(Templ, TM_CCOEFF_NORMED); + Mat := MatchTemplate(Bitmap, Templ, TM_CCOEFF_NORMED); Best := Mat.ArgMax(); if (Mat[Best.Y, Best.X] >= MinMatch) then diff --git a/Source/simba.fonthelpers.pas b/Source/simba.fonthelpers.pas index 24f107df7..fdf692844 100644 --- a/Source/simba.fonthelpers.pas +++ b/Source/simba.fonthelpers.pas @@ -17,6 +17,8 @@ function GetFontSize(Control: TWinControl; IncAmount: Integer = 0): Integer; function IsFontFixed(FontName: String): Boolean; function GetFixedFonts: TStringArray; +function GetDefaultFontName: String; + implementation uses @@ -111,5 +113,15 @@ function GetFixedFonts: TStringArray; end; end; +function GetDefaultFontName: String; +begin + with TBitmap.Create() do + try + Result := GetFontData(Canvas.Font.Reference.Handle).Name; + finally + Free(); + end; +end; + end. diff --git a/Source/simba.geometry.pas b/Source/simba.geometry.pas index 5bf25e34f..02398ec19 100644 --- a/Source/simba.geometry.pas +++ b/Source/simba.geometry.pas @@ -51,9 +51,12 @@ TSimbaGeometry = class class function LinesIntersect(const P1, P2, Q1, Q2: TPoint; out Where: TPoint): Boolean; static; overload; inline; class function PointInTriangle(const P, P1, P2, P3: TPoint): Boolean; static; inline; class function PointInBox(const P: TPoint; const Box: TBox): Boolean; static; inline; - class function PointInQuad(const P: TPoint; const A,B,C,D: TPoint): Boolean; static; inline; - class function PointInPolygon(const P: TPoint; const Polygon: TPointArray): Boolean; static; inline; - class function PointInCircle(const P, Center: TPoint; Radius: Double): Boolean; static; inline; + class function PointInQuad(const P: TPoint; const A,B,C,D: TPoint): Boolean; static; overload; inline; + class function PointInQuad(const X, Y: Integer; const A,B,C,D: TPoint): Boolean; static; overload; inline; + class function PointInPolygon(const P: TPoint; const Polygon: TPointArray): Boolean; static; overload; inline; + class function PointInPolygon(const X, Y: Integer; const Polygon: TPointArray): Boolean; static; overload; inline; + class function PointInCircle(const X, Y, CenterX, CenterY: Integer; const Radius: Double): Boolean; static; overload; inline; + class function PointInCircle(const P, Center: TPoint; const Radius: Double): Boolean; static; overload; inline; class function PointInEllipse(const P, Center: TPoint; const YRadius, XRadius: Double): Boolean; static; class function RotatePointFast(const P: TPoint; Degrees: Integer; X, Y: Double): TPoint; static; @@ -305,7 +308,22 @@ class function TSimbaGeometry.PointInQuad(const P: TPoint; const A, B, C, D: TPo Result := PointInTriangle(P, A,C,B) or PointInTriangle(P, A,D,C); end; -class function TSimbaGeometry.PointInCircle(const P, Center: TPoint; Radius: Double): Boolean; +class function TSimbaGeometry.PointInQuad(const X, Y: Integer; const A, B, C, D: TPoint): Boolean; +var + P: TPoint; +begin + P.X := X; + P.Y := Y; + + Result := PointInTriangle(P, A,C,B) or PointInTriangle(P, A,D,C); +end; + +class function TSimbaGeometry.PointInCircle(const X, Y, CenterX, CenterY: Integer; const Radius: Double): Boolean; +begin + Result := Sqr(X - CenterX) + Sqr(Y - CenterY) <= Sqr(Radius); +end; + +class function TSimbaGeometry.PointInCircle(const P, Center: TPoint; const Radius: Double): Boolean; begin Result := Sqr(P.X - Center.X) + Sqr(P.Y - Center.Y) <= Sqr(Radius); end; @@ -544,4 +562,27 @@ class function TSimbaGeometry.PointInPolygon(const P: TPoint; const Polygon: TPo end; end; +class function TSimbaGeometry.PointInPolygon(const X, Y: Integer; const Polygon: TPointArray): Boolean; +var + I, J: Integer; +begin + Result := False; + + if (Length(Polygon) >= 3) then + begin + J := Length(Polygon) - 1; + for I := 0 to J do + begin + if ((Polygon[I].Y <= Y) and (Y < Polygon[J].Y)) or // an upward crossing + ((Polygon[J].Y <= Y) and (Y < Polygon[I].Y)) then // a downward crossing + begin + (* compute the edge-ray intersect at the x-coordinate *) + if (X - Polygon[I].X < ((Polygon[J].X - Polygon[I].X) * (Y - Polygon[I].Y) / (Polygon[J].Y - Polygon[I].Y))) then + Result := not Result; + end; + J := I; + end; + end; +end; + end. diff --git a/Source/simba.ide_showdeclaration.pas b/Source/simba.ide_showdeclaration.pas index 844d77f7b..1981d3792 100644 --- a/Source/simba.ide_showdeclaration.pas +++ b/Source/simba.ide_showdeclaration.pas @@ -6,7 +6,8 @@ interface uses Classes, SysUtils, - simba.base, simba.ide_codetools_parser, simba.ide_codetools_insight; + simba.base, + simba.ide_codetools_parser, simba.ide_codetools_insight, simba.ide_codetools_includes; procedure FindAndShowDeclaration(Script, ScriptFileName: String; CaretPos: Integer; What: String); @@ -85,12 +86,14 @@ procedure ShowDeclaration(Declaration: TDeclaration); end; begin - if (Declaration.Parser is TPluginParser) then + if (Declaration.Parser is TCodetoolsPlugin) then begin DebugLn([EDebugLn.FOCUS], 'Declared internally in plugin: %s', [Declaration.Lexer.FileName]); DebugLn([EDebugLn.FOCUS], GetDeclarationText()); - end - else + + Exit; + end; + if (Declaration.Lexer.FileName = '') or FileExists(Declaration.Lexer.FileName) then begin if FileExists(Declaration.Lexer.FileName) then @@ -104,12 +107,12 @@ procedure ShowDeclaration(Declaration: TDeclaration); if CanSetFocus() then SetFocus(); end; - end - else - begin - DebugLn([EDebugLn.FOCUS], 'Declared internally in Simba: %s', [Declaration.Lexer.FileName]); - DebugLn([EDebugLn.FOCUS], GetDeclarationText()); + + Exit; end; + + DebugLn([EDebugLn.FOCUS], 'Declared internally in Simba: %s', [Declaration.Lexer.FileName]); + DebugLn([EDebugLn.FOCUS], GetDeclarationText()); end; procedure ShowSimbaDeclaration(Header: String; FileName: String); diff --git a/Source/simba.scriptcommunication.pas b/Source/simba.scriptcommunication.pas index cb77c3aa2..1fa381c86 100644 --- a/Source/simba.scriptcommunication.pas +++ b/Source/simba.scriptcommunication.pas @@ -187,18 +187,17 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Show; RunInMainThread(@Execute); end; -// Chunked data is sent. Row by row. +// Chunked data (row by row) is sent. procedure TSimbaScriptInstanceCommunication.DebugImage_Update; +var + Bitmap: TBitmap; + Width, Height: Integer; - // Modified from LazImage_FromData in simba.image_lazbridge - // Could maybe do this off main thread with only Begin/EndUpdate synced procedure Execute; var - Width, Height: Integer; Source, Dest: PByte; SourceUpper: PtrUInt; DestBytesPerLine, SourceBytesPerLine: Integer; - SourcePtr, DestPtr: PByte; procedure BGR; var @@ -207,18 +206,7 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Update; for Y := 0 to Height - 1 do begin FInputStream.Read(Source^, SourceBytesPerLine); - - SourcePtr := Source; - DestPtr := Dest; - - while (PtrUInt(SourcePtr) < SourceUpper) do - begin - PColorRGB(DestPtr)^ := PColorRGB(SourcePtr)^; // Can just use first three bytes - - Inc(SourcePtr, SizeOf(TColorBGRA)); - Inc(DestPtr, SizeOf(TColorRGB)); - end; - + LazImage_CopyRow_BGR(PColorBGRA(Source), SourceUpper, PColorBGR(Dest)); Inc(Dest, DestBytesPerLine); end; end; @@ -230,8 +218,7 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Update; for Y := 0 to Height - 1 do begin FInputStream.Read(Source^, SourceBytesPerLine); - - Move(Source^, Dest^, DestBytesPerLine); + LazImage_CopyRow_BGRA(PColorBGRA(Source), SourceUpper, PColorBGRA(Dest)); Inc(Dest, DestBytesPerLine); end; end; @@ -243,29 +230,12 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Update; for Y := 0 to Height - 1 do begin FInputStream.Read(Source^, SourceBytesPerLine); - - SourcePtr := Source; - DestPtr := Dest; - - while (PtrUInt(SourcePtr) < SourceUpper) do - begin - PUInt32(DestPtr)^ := SwapEndian(PUInt32(SourcePtr)^); - - Inc(SourcePtr, SizeOf(TColorBGRA)); - Inc(DestPtr, SizeOf(TColorARGB)); - end; - + LazImage_CopyRow_ARGB(PColorBGRA(Source), SourceUpper, PColorARGB(Dest)); Inc(Dest, DestBytesPerLine); end; end; - var - Bitmap: TBitmap; begin - FInputStream.Read(Width, SizeOf(Integer)); - FInputStream.Read(Height, SizeOf(Integer)); - - Bitmap := SimbaDebugImageForm.ImageBox.Background; Bitmap.BeginUpdate(); Bitmap.SetSize(Width, Height); @@ -276,10 +246,12 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Update; Source := GetMem(SourceBytesPerLine); SourceUpper := PtrUInt(Source + SourceBytesPerLine); - case LazImage_PixelFormat(Bitmap) of - 'BGR': BGR(); - 'BGRA': BGRA(); - 'ARGB': ARGB(); + case SimbaDebugImageForm.ImageBox.PixelFormat of + ELazPixelFormat.BGR: BGR(); + ELazPixelFormat.BGRA: BGRA(); + ELazPixelFormat.ARGB: ARGB(); + else + SimbaException('Not supported'); end; FreeMem(Source); @@ -288,6 +260,11 @@ procedure TSimbaScriptInstanceCommunication.DebugImage_Update; end; begin + Bitmap := SimbaDebugImageForm.ImageBox.Background; + + FInputStream.Read(Width, SizeOf(Integer)); + FInputStream.Read(Height, SizeOf(Integer)); + RunInMainThread(@Execute); end; diff --git a/Source/simba.target.pas b/Source/simba.target.pas index 7b4b29e3f..87f1abf55 100644 --- a/Source/simba.target.pas +++ b/Source/simba.target.pas @@ -11,7 +11,7 @@ interface uses Classes, SysUtils, - simba.base, simba.image, simba.externalimage, + simba.base, simba.image, simba.image_utils, simba.externalimage, simba.target_eios, simba.target_window, simba.target_image, simba.target_plugin; type @@ -311,6 +311,7 @@ function TSimbaTarget.GetImage(ABounds: TBox): TSimbaImage; if GetImageData(ABounds, Data, DataWidth) then try Result := TSimbaImage.CreateFromData(ABounds.Width, ABounds.Height, Data, DataWidth); + Result.SetAlpha(ALPHA_OPAQUE); finally FreeImageData(Data); end diff --git a/Source/targets/simba.target_plugin.pas b/Source/targets/simba.target_plugin.pas index a8422e19e..bcdf65fbc 100644 --- a/Source/targets/simba.target_plugin.pas +++ b/Source/targets/simba.target_plugin.pas @@ -30,8 +30,8 @@ TSimbaPluginTarget = record GetImageData: function(Target: Pointer; X, Y, Width, Height: Int32; var Data: PColorBGRA; var DataWidth: Int32): Boolean; cdecl; MousePressed: function(Target: Pointer; Button: Int32): Boolean; cdecl; - MousePosition: function(Target: Pointer): TPoint; cdecl; - MouseTeleport: procedure(Target: Pointer; P: TPoint); cdecl; + MousePosition: procedure(Target: Pointer; out X, Y: Integer); cdecl; + MouseTeleport: procedure(Target: Pointer; X, Y: Int32); cdecl; MouseUp: procedure(Target: Pointer; Button: Int32); cdecl; MouseDown: procedure(Target: Pointer; Button: Int32); cdecl; MouseScroll: procedure(Target: Pointer; Scrolls: Int32); cdecl; @@ -128,6 +128,7 @@ function LoadPluginTarget(FileName, Args: String; out DebugImage: TSimbaExternal CheckExported('SimbaPluginTarget_RequestWithDebugImage', RequestWithDebugImage); Target := Result.RequestWithDebugImage(PChar(Args), DebugImage); + WriteLn('Target is: ', HexStr(Target)); end; end; @@ -167,7 +168,7 @@ function PluginTarget_MousePosition(Target: Pointer): TPoint; begin CheckExported('SimbaPluginTarget_MousePosition', MousePosition); - Result := MousePosition(Target); + MousePosition(Target, Result.X, Result.Y); end; end; @@ -177,7 +178,7 @@ procedure PluginTarget_MouseTeleport(Target: Pointer; P: TPoint); begin CheckExported('SimbaPluginTarget_MouseTeleport', MouseTeleport); - MouseTeleport(Target, P); + MouseTeleport(Target, P.X, P.Y); end; end; diff --git a/Tests/finder_bitmap.simba b/Tests/finder_bitmap.simba index 8bfa45510..072f324d8 100644 --- a/Tests/finder_bitmap.simba +++ b/Tests/finder_bitmap.simba @@ -13,9 +13,9 @@ var ]; var - //img: TImage := TImage.CreateFromString(100, 100, 'meJztnV1vFFUYxxO94MLEK70wmugHMNV4B4Fg1AtjjAGCkoDxExi1KkKbVmprBSlQqtWoRIMvSVFQQnlpCooiWrDEl4/kg3/8979zznP2bGdnurts8stks53dmfntOc8855kz06df2/d0Hlev34jy67Vlw/vU4uVfjXMXL5PTFy6SzE2n2Xnoi+0Ts2Trvmny7Oh+smlokqx/a3SFN4fJwNtjCUqKoqu0rupcmahecmVU5AqiOsoVbUS5fHWJeN+gfr6bv0C89Xd/foq8/MFxsuP9Y2TbOzPkueGD5JnhSdLgZ3B0hUY/6x595O6t9xt4ce+OxwqWHh7ZY5QUVXB18ZerRtiiWnI1emKxTlfmh64gCrTkqqkl2wRc3TWwBa7sBXQZXr9LuzJR7XIVtpaoq4KiB3Y+FXVVRhRdmR+4wg9UxhVEtcWV11oSrmx9rlZwle56aCc//3aNXFm6TvDXe+57EP0Ozcm2deHSz1ganp+PTl8iusW3jp8m6ocB3FA/z+7dTx7fN0XQ47zWgng+sHeMwCrhygNjwyBHVI4rw77fXGFDHeiq0FpCV2g5d9x5hxq7+WYzURqom7oC2vVobM1dea3Fc2Xc/Ok37TJuvdOKqBxX7H0EotbcFSi0FuZUOa7SXQ/jEQZnI7SnuQEiFTqgLe2z84s/Es/P9LdnQWHrr370DVE/mmc+s3uSqJONeybIhqExYlpoYP3QMHETzv9jVNMwBV0tuTLsI3Bl5LsKN911rkCrrkimq3CLQ1/PV+RqhWpcUVcVrqKb6wpXOC6vP/545XfiuWIwL8TzT+Z/IkdPngfRDb3+2QnjxemvSIMfGbOoE2/Mok5cP+JEwVjGy9LhCkfad5XvKtSVdoW0yj5lUR0ftxcJV15/7zRX3n6Wd4UzIDJ2o6tdpWM4jkvHuZ4udYVIbqdjxHNkCybKwjiWhXh+eG7e8PYhHBdvGT9Ecmp0piJSg5I+BcJ1CitU6qowHlwrV15VoSCq6TolXVFX6ApgH9CXaaxmV15VQT3krFPeFYi6Yu8j0ZyqNleFqkLUVWKdtKvvzy8amkOageiaGud5LtDxoDmfOz1PZk4tkNETi+ndeGl27vmpj4mOhRviuRPDvarCwyODJGed8q7UEsD7zKk4HlwrV9Ea1H9NZbBAsM5gSVeqKxRFV9Cl+afnKr0PoLwrrav8362KrgxZp/in1bmisb4rgjh8ZuESWfjpClE/Oq5JfOHHZy4B1qmihYWQbe/NkM0jB0gip1pxFRuneK4KPDS+m/Rddawr6uq7up1dIQ5rfNYaSzSwG+nctbyrzDFgW1zlx/byrk6dXTDaG6+6yxWuXpkWyzDhhy+YM6grQFFr5SpWSajJFbJxk4MsN8yvjM5x5VcS2uMKtThkpACuLGGzJefA2D5wqfOsrF3pZ7W+16qrre9OEzf/TLryKwmVuyrUprrIVVBJqNYVeyL96GuNVzmuhr52CzJtdJVTSajIlc6qUlGGngc7xxVIVxLKuEItTq97UoLWpgrzQHKucxVcNdXVLleWKiRGx8Srva/OlYF2BWm95CpRey/jKjq/KO3KdgOuzDBc2Yumutroqmkumq691+8KrdFE4UfsTFct1d5Ri2vVlY6vtU544sw5w7qALY+ePG+ubGn7M/zNraXhuSozdlYKl/yi+GfMNXBVyM06zRWI1edTl1arcAWwA7g6T2Md5arxLBBfoQZX7H0EorrOlerKj+06t1avX4T1GUNzM/t+TvJPXPx64fCn5Il3jpCNIxPEm3vmzfHI0dV0fhF1VeTKsK9lbtbtrqCrUlekB1wZfVf5rsJan3rQeK53LWk9WdfXaxx6XTWnPrNr9jjROK/XVTePHyDqcMO+MbJ+ZDTO6N4oA+MTcQJ79bvysve+q6irqK6+K89VqKvzXSGeaz6p10w1nqfv2w3vr/TuBXj7uxUKul759Cuwc+YL4AU3sPX9afDk4VnyxP4j5PGDU1E27z8QZdPkJNk4PkFgb21d4a6uUFeOKNV1+7gKdWWKoq7b1tUqqMdVOLfWdbW0TDTme/OTNc5r/eHDHxZJTo5qQyQuE+yYPgY0p91+ZJZsm5ohDHSpNjk5RXAG6RxXXuvKdEVd3evKhs9wZQfLco3nytOFMXhOZ+wBV6g2sACYcBXVZR/Jj13VucIR6bUGz1X0eTuGrhPOgce8CPs5MAHejtpSUywN7Y+JvKtDqMFVofbed+W50nkR6O801ncVvQ+FilRU17lCPNeYrMerHn7/40+icV7R9aPPcDjX+BwezSW8+8c1/vMCh8ExuKHPwcg5an1Whr6fzk9qcIV8nrX3ql3h6UaeJdynr65wJ2xHuSItuZr+9uzqXIE3jp0k+kyD0BV1JXK5vqvQldcm08+Q0RiurjTOew41X83xFj5DDDNJNP7z2QWG1g9xLyfw3KpP3I0OvNjVd5V2pQ0MYIyJCm0ZV9Zc4co6ON63F+1yxTlv9bsCLFy00RXObvYmcqcedhXeI+/V99QVsMGLLZeW/zJXtjRRSzduLY2c/ujNIfEc6rlA51vqPQjqVn2qwz1fniGet4bn/xyYNUq6Koz1+q48V+CWpeW/0LRqcGWbgCv7jeDKXuS7QtCu3xV7H4GoGlwhTmJ6qpHpSs9xrbqKPGRVjlFzA0pQdKx3/cbfiq7m5aveNQ51qOcCmLSOb0uUEFFO/P78IpaGug3vpVJXI3MLhuaumq+GzwIq6cowLRzr1eaqECfzXVEXRNXvKkp1rvTZNaxU57syKOp2cMXeR3hPUHWucP7KwXOyvPxPFM9bTs1QHSoc1GucvNj4HDy91uZdz9XcVcfgg5/MkZ5xhfv6GSdzXCFM9aor3oWnz4ePPgMh3xV09aqrwvPh19CVd6T5eA4bXKl28ebVeXAWsMQAL1C+WPffo/WxLKD9VM8LmtNqZqVxXsfXmpeG9asOd1XIo+px5dX6qnCl31/GFdBSD42VcVW4D7TgKlEXba8rbyurdsXeRyIJYWe7isbtdLE68d+IEmgeddOe819CcuY1aV06Z65v17lCG2Me1Ruumm5o1boaLqtV5ipn//uucva8VVfpcXHOtpqOWaLosSve+Nq7Vot9wL2lHDjni+q76klX+j3tctWSqK5w5X1bJ7vSfFJjL0a7mZuz9qDjXK/m6Y1NFL2vU8fIei0sfB6s/m+18Dt3HTlO9Hn7W0YOGf8CJqsXFg=='); - img := TImage.CreateFromString('IMG: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'); - templ := TImage.CreateFromString('IMG:eJwrzM9MY2Bg4AJhZoZT//QN2P8B2Qf+Behxmx6QMD2obCpxwFT5gOnBnY9BfGUgPCB9QFkCKKRseghIHgBJAiUOmR6SAIqbHlMGajjNAAGMAKwxHfU='); + img := TImage.CreateFromString('IMG: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'); + templ := TImage.CreateFromString('IMG:AQAAAAoAAAAKAAAANAAAADUAAAA2AAAAKAAAAAAAAAD4JIgAAQAAAFDjPwEAAAAAwEvxCAAAAADAS/EIAAAAAMBL8QgAAAAAaOZ3AAEAAAABAAAACgAAAAoAAAAgAAAAAAAAAAAAAAAAAAAAAwAAACAIEAgICAAIGAEAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEu4FAHicTYwhEgAwCMPiKmr7/7cOKrj1EIEAcBG4NWSj6ckOtCqRVaKqercKcyDfI5s/D0QgAU14nE2MwQ0AQAjCugZLEPaf7pSHOeKjWhUuAbeGbDI92kFWSXFKVFXvVmEO4ntk8+cBO38GNHicTYzBEQAwCMKyCSOwCvtPU+XhlfMRjQoXQ1pDCZ4e7cCrJMclqqp3qzAHzj1K+PMAimAHynicY2CAg/8wBMT/wQgq8B9MgQFYGiz1H6bwP1QIQkPZDMgAAPu8It4='); + templBrightnessed: TImage; TPA: TPointArray; P: TPoint; @@ -51,7 +51,6 @@ begin // test tolerance templBrightnessed := templ.Brightness(25); - templBrightnessed.SetTransparentColor(1644825); // find all with tolerance, entire client TPA := Finder.FindImageEx(templBrightnessed, 10, -1); diff --git a/Tests/finder_color.simba b/Tests/finder_color.simba index f611247c2..9260c15c3 100644 --- a/Tests/finder_color.simba +++ b/Tests/finder_color.simba @@ -28,7 +28,7 @@ var I: Integer; begin Bitmap := TImage.Create(500, 500); - Bitmap.DrawHSLCircle(Bitmap.GetCenter(), 200); + Bitmap.DrawHSLCircle(Bitmap.Center(), 200); Target.SetImage(Bitmap); diff --git a/Tests/finder_dtm.simba b/Tests/finder_dtm.simba index f6d3aaa5c..a306507d8 100644 --- a/Tests/finder_dtm.simba +++ b/Tests/finder_dtm.simba @@ -19,7 +19,8 @@ var P: TPoint; begin dtm.FromString('DTM:eJxjY2Bg2MLFwLARiLcD8TYg3gTEO4BYnZmBQQWKtYFYC4g1gDhAj5tB34AdTMMwiE8qAAD40Aht'); - img := TImage.CreateFromString('IMG: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'); + img := TImage.CreateFromString('IMG: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'); + Target.SetImage(img); // find all, entire client diff --git a/Tests/finder_dtmrotated.simba b/Tests/finder_dtmrotated.simba index 745c277f1..ca851bfd8 100644 --- a/Tests/finder_dtmrotated.simba +++ b/Tests/finder_dtmrotated.simba @@ -19,7 +19,7 @@ var degs: TDoubleArray; begin dtm.FromString('DTM:eJxjY2Bg2MLFwLARiLcD8TYg3gTEO4BYnZmBQQWKtYFYC4g1gDhAj5tB34AdTMMwiE8qAAD40Aht'); - img := TImage.CreateFromString('IMG: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'); + img := TImage.CreateFromString('IMG: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'); imgRotated := img.RotateNN(DegToRad(270), False); Target.SetImage(imgRotated); diff --git a/Tests/img_pixeldifference.simba b/Tests/img_pixeldifference.simba new file mode 100644 index 000000000..0fe5f965d --- /dev/null +++ b/Tests/img_pixeldifference.simba @@ -0,0 +1,26 @@ +{$assertions on} + +var + img1, img2: TImage; +begin + img1 := TImage.CreateFromString('IMG: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'); + img2 := img1.Copy(); + + img2.DrawBoxFilled([20,20,24,24], Colors.BLACK); + + // Only difference should be the box we just drawn + Assert(img1.PixelDifference(img2) = 5*5); + Assert(img1.PixelDifferenceTPA(img2).Length() = 5*5); + Assert(img1.PixelDifferenceTPA(img2).Bounds() = [20,20,24,24]); + + img2.Free(); + img2 := img1.Mirror(EImageMirrorStyle.WIDTH); + + // The more tolerance, the less difference there should be. + Assert(img1.PixelDifference(img2, 2) = 7338); + Assert(img1.PixelDifference(img2, 6) = 1478); + Assert(img1.PixelDifference(img2, 12) = 0); + + img1.Free(); + img2.Free(); +end. diff --git a/Tests/matchtemplatemask.simba b/Tests/matchtemplatemask.simba index ead9bc184..be92012eb 100644 --- a/Tests/matchtemplatemask.simba +++ b/Tests/matchtemplatemask.simba @@ -48,9 +48,10 @@ begin end; begin - img := TImage.CreateFromString('IMG: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'); + img := TImage.CreateFromString('IMG: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'); + templ := img.Copy(50, 50, 100, 100); - templ.DrawCircleInverted([templ.GetCenter().X, templ.GetCenter().Y, 20], 0); + templ.DrawCircleInverted(templ.Center(), 20, 0); try test(TM_CCOEFF, [83, 1], [50, 50]); diff --git a/Tests/matchtemplatemask2.simba b/Tests/matchtemplatemask2.simba index 947b3a523..9eb4ae9ca 100644 --- a/Tests/matchtemplatemask2.simba +++ b/Tests/matchtemplatemask2.simba @@ -3,7 +3,7 @@ var mat: TSingleMatrix; s: Single; begin - img := TImage.CreateFromString('IMG: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'); + img := TImage.CreateFromString('IMG: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'); img.FreeOnTerminate(True); templ := img.copy(8,8,22,22); templ.FreeOnTerminate(True); diff --git a/Tests/tpa_minareacircle.simba b/Tests/tpa_minareacircle.simba index 45e199936..34b258306 100644 --- a/Tests/tpa_minareacircle.simba +++ b/Tests/tpa_minareacircle.simba @@ -11,7 +11,7 @@ var I: Integer; C: TCircle; begin - Img := TImage.CreateFromString('IMG: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'); + Img := TImage.CreateFromString('IMG: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'); TPA := Img.Finder.FindColor($C0C0C0,0); ATPA := TPA.Cluster(15); diff --git a/Third-Party/fpqoi_simba.pas b/Third-Party/fpqoi_simba.pas deleted file mode 100644 index 713c84162..000000000 --- a/Third-Party/fpqoi_simba.pas +++ /dev/null @@ -1,606 +0,0 @@ -{ - This file is part of the Free Pascal run time library. - Copyright (c) 2022 by the Free Pascal development team - - QOI reader implementation - - See the file COPYING.FPC, included in this distribution, - for details about the copyright. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - **********************************************************************} -{$mode objfpc}{$h+} - -unit fpqoi_simba; - -interface - -uses - Classes, SysUtils, FPImage; - -type - PQoiHeader = ^TQoiHeader; - TQoiHeader = packed record - magic : array [0..3] of char; { magic bytes 'qoif' } - width : DWord; { image width in pixels (BE)} - height : DWord; { image height in pixels (BE)} - channels : byte; { 3 = RGB, 4 = RGBA } - colorspace : byte; { 0 = sRGB with linear alpha } - { 1 = all channels linear } - end; - - PQoiPixel = ^TQoiPixel; - TQoiPixel = packed record - r,g,b,a : byte; - end; - - TFPReaderQoi = class(TFPCustomImageReader) - Private - QoiHeader: TQoiHeader; // The header as read from the stream. - function getUseAlpha:Boolean; - protected - // required by TFPCustomImageReader - procedure InternalRead (Stream:TStream; Img:TFPCustomImage); override; - function InternalCheck (Stream:TStream) : Boolean; override; - public - property UseAlpha : Boolean read getUseAlpha; - end; - - TFPWriterQoi = class(TFPCustomImageWriter) - private - QoiHeader: TQoiHeader; - procedure setUseAlpha(useAlpha:Boolean); - function getUseAlpha:Boolean; - protected - function SaveHeader(Stream:TStream; Img: TFPCustomImage):Boolean; virtual; - procedure InternalWrite (Stream:TStream; Img: TFPCustomImage); override; - public - constructor Create; override; - property useAlpha:Boolean read getUseAlpha write setUseAlpha; - end; - -implementation - -uses - Graphics; - -{$R-} -{$O-} - -const - qoChannelRGB = 3; - qoChannelRGBA = 4; - -function Swap32(a: DWord): DWord; -var h, l : DWord; -begin - a := RolDWord(a,16); - h := a shr 8; - h := h and $ff00ff; - l := a and $ff00ff; - l := l shl 8; - - Result := h or l; -end; - -function QoiPixelIndex(const px: TQoiPixel): DWord; inline; -begin - Result := (DWord(px.r)*3+DWord(px.g)*5+DWord(px.b)*7+DWord(px.a)*11) and 63; -end; - -function RGBAToFPColor(const RGBA: TQoiPixel): TFPcolor; inline; -begin - with Result, RGBA do - begin - Red := (R shl 8) or R; - Green := (G shl 8) or G; - Blue := (B shl 8) or B; - Alpha := (A shl 8) or A; - end; -end; - -function TFPReaderQoi.getUseAlpha:boolean; -begin - Result := (QoiHeader.channels=qoChannelRGBA); -end; - -function TFPReaderQoi.InternalCheck (Stream:TStream) : boolean; -var - n: Int64; -begin - Result:=False; - if Stream=nil then - exit; - n:=SizeOf(TQoiHeader); - Result:=Stream.Read(QoiHeader,n)=n; - if Result then - begin - {$IFDEF ENDIAN_LITTLE} - QoiHeader.width:=Swap32(QoiHeader.width); - QoiHeader.height:=Swap32(QoiHeader.height); - {$ENDIF} - Result := (QoiHeader.magic = 'qoif'); // Just check magic number - end; -end; - -// NOTE: It is assumed that signature and IDHDR chunk already have been read. -procedure TFPReaderQoi.InternalRead (Stream:TStream; Img:TFPCustomImage); -var iP, q : qword; - b, run : byte; - g : shortint; - px : TQoiPixel; - arr : array [0..63] of TQoiPixel; - iA : DWord; {index in pixel array} - - p: pbyte; orgSize, imgSize : qword; - Row, Col, w, h : DWord; - aLine : pbyte; - - -begin - with QoiHeader do - begin - Img.SetSize (Width, Height); - imgSize := Width * Height; - w:=Width; - h:=Height; - end; - - - orgSize:=Stream.size; - orgSize:=orgSize-sizeof(TQoiHeader); - getmem(aLine,orgSize); - - q:=Stream.Read(aLine^,orgSize); - if orgSize>q then orgSize:=q; - - - ip:=0; - q:=0; - p:=aLine; - - DWord(px):=0; - px.a:=255; - - {initalize previosly seen pixel array} - //fillchar(arr,sizeof(arr),0); - iA:=QoiPixelIndex(px); - //for iA:=0 to 63 do - arr[iA]:=px; - - Row:=0; - Col:=0; - - - {actual decoding loop} - while (orgSize> ip) and (imgSize>q) do - begin - b:=p^; - inc(p); - inc(ip); - - case (b shr 6) of - 0: begin { pixel from previos pixel array} - - if b = p^ then {deal with end of encoding} - begin - if b = 0 then - begin - dec(p); - for iA:=0 to 6 do - begin - b:=p^; - inc(p); - inc(ip); - if b<>0 then break; - end; - if b<>0 then - begin - {invalid encoding} - break; - end; - b:=p^; - inc(p); - inc(ip); - if b = 1 then - begin - //writeln('end of encoding '); - {success - no more encoded pixels} - break; - end else - begin - {invalid encoding} - break; - end; - - end else - begin - {invalid encoding} - break; - end; - end; - - {pixel from array} - iA:= b and 63; - px:=arr[iA]; - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - - end; - - 1: begin { diff } - b:=b and 63; - px.r:=px.r+ byte(b shr 4) and 3+shortint(-2); - px.g:=px.g+ byte(b shr 2) and 3+shortint(-2); - px.b:=px.b+ byte(b shr 0) and 3+shortint(-2); - - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - iA:=QoiPixelIndex(px); - arr[iA]:=px; - - end; - - 2: begin { luma } - g:=b and 63 - 32; - b:=p^; - inc(p); - inc(ip); - px.g:=px.g + g; - px.r:=px.r+g+shortint((b shr 4)-8); - px.b:=px.b+g+shortint((b and 15)-8); - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - iA:=QoiPixelIndex(px); - arr[iA]:=px; - - - end; - - 3: begin - run:=b and 63+1; - case run of - 64: begin { rgba } - px.r:=p^; - inc(p); - px.g:=p^; - inc(p); - px.b:=p^; - inc(p); - px.a:=p^; - inc(p); - inc(ip,4); - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - iA:=QoiPixelIndex(px); - arr[iA]:=px; - - end; - 63: begin { rgb } - px.r:=p^; - inc(p); - px.g:=p^; - inc(p); - px.b:=p^; - inc(p); - inc(ip,3); - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - iA:=QoiPixelIndex(px); - arr[iA]:=px; - - end; - otherwise { run - repeat previos pixel} - repeat - img.Colors[Col,Row] := RGBAToFPColor( px ); - inc(q); - inc(Col); - if Col = w then begin inc(Row); Col:=0; if Row>=h then break; end; - dec(run); - - until run =0; - end; - end; - - end; {case of } - end; { while do} - freeMem(aLine); -end; - -constructor TFPWriterQoi.create; -begin - inherited create; - with QoiHeader do - begin - magic:='qoif'; - channels:=qoChannelRGB; - colorspace:=0; - end; -end; - -procedure TFPWriterQoi.setUseAlpha(useAlpha:boolean); -begin - if useAlpha then QoiHeader.channels := qoChannelRGBA else QoiHeader.channels:=qoChannelRGB; -end; - -function TFPWriterQoi.getUseAlpha:boolean; -begin - Result:= (QoiHeader.channels=qoChannelRGBA); -end; - -function TFPWriterQoi.SaveHeader(Stream:TStream; Img : TFPCustomImage):boolean; -begin - Result:=False; - with QoiHeader do - begin - Width:=Img.Width; - Height:=Img.Height; - //writeln('Save width ',width, ' height ', height); - end; - - {$IFDEF ENDIAN_LITTLE} - QoiHeader.width:=Swap32(QoiHeader.width); - QoiHeader.height:=Swap32(QoiHeader.height); - {$ENDIF} - - //writeln('Save width 2 ',QoiHeader.width, ' height ', QoiHeader.height); - Stream.Write(QoiHeader,sizeof(TQoiHeader)); - - {$IFDEF ENDIAN_LITTLE} - QoiHeader.width:=Swap32(QoiHeader.width); - QoiHeader.height:=Swap32(QoiHeader.height); - {$ENDIF} - Result:=true; -end; - -procedure TFPWriterQoi.InternalWrite (Stream:TStream; Img:TFPCustomImage); -var - Row,Col,RowSize:sizeuint; - h, w, orgSize, mSize : sizeuint; - aLine, p: PByte; - - color : TFPColor; - - -var iP, iq, imgSize : qword; - //q: PQoiPixel; - b, run : byte; - g, dr, dg, db, dr_dg, db_dg : byte;// shortint; - px, cx : TQoiPixel; - arr : array [0..63] of TQoiPixel; - iA : DWord; {index in pixel array} - //endOf : qword; - -begin - mSize:= img.Width * sizeof(TQoiPixel)+ img.Width; - RowSize:= img.Width * sizeof(TQoiPixel)+ img.Width + 8+sizeof(TQoiPixel)*64+64; - - SaveHeader(Stream,Img); { write the headers } - - GetMem(aLine,RowSize); - - p:=aLine; - - DWord(px):=0; - px.a:=255; - - {initalize previosly seen pixel array} - fillchar(arr,sizeof(arr),0); - iA:=QoiPixelIndex(px); - //for iA:=0 to 63 do - //arr[iA]:=px; - - Row:=0; - Col:=0; - h:=Img.Height; - w:=Img.Width; - iq:=0; - ip:=0; - imgSize:= h*w; - if imgSize > 0 then - while (imgSize)> iq do - begin - color:=img.colors[Col,Row]; - cx.r:=color.Red shr 8; - cx.g:=color.Green shr (8); - cx.b:=color.Blue shr (8); - cx.a:=color.Alpha shr (8); - - iA:=QoiPixelIndex (cx); - - if DWord(cx)=DWord(px) then { run } - begin - run:=0; - //inc (q); - inc (iq); - - inc(col); - if col = w then begin inc(row); col:=0; end; - - if (col < w) and (row= (iq+1)) - and (DWord(cx)=DWord(px)) do - begin - inc (run); - inc (iq); - - inc(col); - if col = w then begin - inc(row); col:=0; - if (col >= w) or (row>=h) then break; - end; - - - - color:=img.colors[Col,Row]; - cx.r:=color.Red shr 8; - cx.g:=color.Green shr (8); - cx.b:=color.Blue shr (8); - cx.a:=color.Alpha shr (8); - - - - if run = 61 then break; - end; - end; - - b:=($ff xor 63) or run; - p^:=b; - inc(p); - inc(ip); - - end else - if DWord(arr[iA]) = DWord(cx) then { index } - begin - - px:=cx; - p^:=byte(iA); - inc(p); - inc(ip); - //inc(q); - inc(iq); - - inc(col); - if col = w then begin inc(row); col:=0; end; - - - end else - if px.a <> cx.a then { rgba } - begin - b:=$ff; - p^:=b; - inc(p); - px:=cx; - //PQoiPixel(p)^:=cx; - //inc(p,4); - - p^:=cx.r;inc(p); - p^:=cx.g;inc(p); - p^:=cx.b;inc(p); - p^:=cx.a;inc(p); - - inc(ip,5); - //inc(q); - inc (iq); - - inc(col); - if col = w then begin inc(row); col:=0; end; - - arr[iA]:=cx; - - end else - begin - dr := (cx.r - px.r); - dg := (cx.g - px.g); - db := (cx.b - px.b); - - px:=cx; - - dr_dg := dr-dg+8; - db_dg := db-dg+8; - - dr:=dr+2; - dg:=dg+2; - db:=db+2; - g:=dg+30; - - //inc(q); - inc (iq); - inc(col); - if col = w then begin inc(row); col:=0; end; - - arr[iA]:=cx; - - if (dr and ($ff xor 3))+(dg and ($ff xor 3))+(db and ($ff xor 3)) = 0 then { diff } - begin - b:=64 or (dr shl 4) or (dg shl 2)or (db ) ; - p^:=b; - inc(p); - inc(ip); - - end else - if ((g) and ($ff xor 63)) + (dr_dg and ($ff xor 15))+ (db_dg and ($ff xor 15))=0 then { luma } - begin - b:=128 or g; - p^:=b; - inc(p); - b:=(dr_dg shl 4) or db_dg; - p^:=b; - inc(p); - inc(ip,2); - - end else {rgb} - begin - b:=$fe; - p^:=b; - inc(p); - //PQoiPixel(p)^:=cx; - //inc(p,3); - - - p^:=cx.r;inc(p); - p^:=cx.g;inc(p); - p^:=cx.b;inc(p); - - inc(ip,4); - end; - - end; - if ip >= mSize then - begin - {save data } - orgSize:=ip; - Stream.Write(aLine[0],orgSize); - ip:=0; - p:=aLine; - end; - end; - - - {mark end of encoding} - { - endof:=qword(1) shl 56; - pqword(p)^:=endof; - inc(p,8); - } - p^:=0; inc(p); - p^:=0; inc(p); - p^:=0; inc(p); - p^:=0; inc(p); - p^:=0; inc(p); - p^:=0; inc(p); - p^:=0; inc(p); - p^:=1; inc(p); - - inc(ip,8); - - orgSize:=ip; - Stream.Write(aLine[0],orgSize); - - FreeMem(aLine); -end; - -end. - diff --git a/Third-Party/syncompletion_simba.pas b/Third-Party/syncompletion_simba.pas index 2aea25d66..67cbe1395 100644 --- a/Third-Party/syncompletion_simba.pas +++ b/Third-Party/syncompletion_simba.pas @@ -1475,8 +1475,11 @@ procedure TSynBaseCompletion.Execute(s: string; x, y: integer); CurrentString := s; - if p >= 0 then - Position := p; + //if p >= 0 then + // Position := p; + + TheForm.Position := 0; + TheForm.Scroll.Position := 0; if Assigned(OnExecute) then OnExecute(Self);