123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- unit GR32_VectorMaps;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is GR32_VectorMaps
- *
- * The Initial Developer of the Original Code is
- * Michael Hansen <[email protected]>
- *
- * Portions created by the Initial Developer are Copyright (C) 2000-2009
- * the Initial Developer. All Rights Reserved.
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- uses
- {$if defined(UseInlining)}
- Types,
- {$ifend}
- GR32;
- type
- TFixedVector = TFixedPoint;
- PFixedVector = ^TFixedVector;
- TFloatVector = TFloatPoint;
- PFloatVector = ^TFloatVector;
- TArrayOfFixedVector = array of TFixedVector;
- PArrayOfFixedVector = ^TArrayOfFixedVector;
- TArrayOfFloatVector = array of TFloatVector;
- PArrayOfFloatVector = ^TArrayOfFixedVector;
- type
- TVectorCombineMode = (vcmAdd, vcmReplace, vcmCustom);
- TVectorCombineEvent= procedure(F, P: TFixedVector; var B: TFixedVector) of object;
- TVectorMap = class(TCustomMap)
- private
- FVectors: TArrayOfFixedVector;
- FOnVectorCombine: TVectorCombineEvent;
- FVectorCombineMode: TVectorCombineMode;
- function GetVectors: PFixedPointArray;
- function GetFixedVector(X,Y: Integer): TFixedVector;
- function GetFixedVectorS(X,Y: Integer): TFixedVector;
- function GetFixedVectorX(X,Y: TFixed): TFixedVector;
- function GetFixedVectorXS(X,Y: TFixed): TFixedVector;
- function GetFloatVector(X,Y: Integer): TFloatVector;
- function GetFloatVectorS(X,Y: Integer): TFloatVector;
- function GetFloatVectorF(X,Y: Single): TFloatVector;
- function GetFloatVectorFS(X,Y: Single): TFloatVector;
- procedure SetFixedVector(X,Y: Integer; const Point: TFixedVector);
- procedure SetFixedVectorS(X,Y: Integer; const Point: TFixedVector);
- procedure SetFixedVectorX(X,Y: TFixed; const Point: TFixedVector);
- procedure SetFixedVectorXS(X,Y: TFixed; const Point: TFixedVector);
- procedure SetFloatVector(X,Y: Integer; const Point: TFloatVector);
- procedure SetFloatVectorS(X,Y: Integer; const Point: TFloatVector);
- procedure SetFloatVectorF(X,Y: Single; const Point: TFloatVector);
- procedure SetFloatVectorFS(X,Y: Single; const Point: TFloatVector);
- procedure SetVectorCombineMode(const Value: TVectorCombineMode);
- protected
- procedure ChangeSize(var Width, Height: Integer; NewWidth,
- NewHeight: Integer); override;
- public
- destructor Destroy; override;
- procedure Clear;
- procedure Merge(DstLeft, DstTop: Integer; Src: TVectorMap; SrcRect: TRect);
- property Vectors: PFixedPointArray read GetVectors;
- function BoundsRect: TRect;
- function GetTrimmedBounds: TRect;
- function Empty: Boolean; override;
- procedure LoadFromFile(const FileName: string);
- procedure SaveToFile(const FileName: string);
- property FixedVector[X, Y: Integer]: TFixedVector read GetFixedVector write SetFixedVector; default;
- property FixedVectorS[X, Y: Integer]: TFixedVector read GetFixedVectorS write SetFixedVectorS;
- property FixedVectorX[X, Y: TFixed]: TFixedVector read GetFixedVectorX write SetFixedVectorX;
- property FixedVectorXS[X, Y: TFixed]: TFixedVector read GetFixedVectorXS write SetFixedVectorXS;
- property FloatVector[X, Y: Integer]: TFloatVector read GetFloatVector write SetFloatVector;
- property FloatVectorS[X, Y: Integer]: TFloatVector read GetFloatVectorS write SetFloatVectorS;
- property FloatVectorF[X, Y: Single]: TFloatVector read GetFloatVectorF write SetFloatVectorF;
- property FloatVectorFS[X, Y: Single]: TFloatVector read GetFloatVectorFS write SetFloatVectorFS;
- published
- property VectorCombineMode: TVectorCombineMode read FVectorCombineMode write SetVectorCombineMode;
- property OnVectorCombine: TVectorCombineEvent read FOnVectorCombine write FOnVectorCombine;
- end;
- implementation
- uses
- SysUtils,
- GR32_Lowlevel,
- GR32_Math;
- resourcestring
- RCStrCantAllocateVectorMap = 'Can''t allocate VectorMap!';
- RCStrBadFormat = 'Bad format - Photoshop .msh expected!';
- RCStrFileNotFound = 'File not found!';
- RCStrSrcIsEmpty = 'Src is empty!';
- RCStrBaseIsEmpty = 'Base is empty!';
- { TVectorMap }
- function CombineVectorsReg(const A, B: TFixedVector; Weight: TFixed): TFixedVector;
- begin
- Result.X := FixedCombine(Weight, B.X, A.X);
- Result.Y := FixedCombine(Weight, B.Y, A.Y);
- end;
- procedure CombineVectorsMem(const A: TFixedVector;var B: TFixedVector; Weight: TFixed);
- begin
- B.X := FixedCombine(Weight, B.X, A.X);
- B.Y := FixedCombine(Weight, B.Y, A.Y);
- end;
- function TVectorMap.BoundsRect: TRect;
- begin
- Result := MakeRect(0, 0, Width, Height);
- end;
- procedure TVectorMap.ChangeSize(var Width, Height: Integer;
- NewWidth, NewHeight: Integer);
- begin
- inherited;
- FVectors := nil;
- Width := 0;
- Height := 0;
- SetLength(FVectors, NewWidth * NewHeight);
- if (NewWidth > 0) and (NewHeight > 0) then
- begin
- if FVectors = nil then
- raise Exception.Create(RCStrCantAllocateVectorMap);
- FillLongword(FVectors[0], NewWidth * NewHeight * 2, 0);
- end;
- Width := NewWidth;
- Height := NewHeight;
- end;
- procedure TVectorMap.Clear;
- begin
- FillLongword(FVectors[0], Width * Height * 2, 0);
- end;
- destructor TVectorMap.Destroy;
- begin
- Lock;
- try
- SetSize(0, 0);
- finally
- Unlock;
- end;
- inherited;
- end;
- function TVectorMap.GetVectors: PFixedPointArray;
- begin
- Result := @FVectors[0];
- end;
- function TVectorMap.GetFloatVector(X, Y: Integer): TFloatVector;
- begin
- Result := FloatPoint(FVectors[X + Y * Width]);
- end;
- function TVectorMap.GetFloatVectorF(X, Y: Single): TFloatVector;
- begin
- Result := FloatPoint(GetFixedVectorX(Fixed(X), Fixed(Y)));
- end;
- function TVectorMap.GetFloatVectorFS(X, Y: Single): TFloatVector;
- begin
- Result := FloatPoint(GetFixedVectorXS(Fixed(X), Fixed(Y)));
- end;
- function TVectorMap.GetFloatVectorS(X, Y: Integer): TFloatVector;
- begin
- if (X >= 0) and (Y >= 0) and
- (X < Width) and (Y < Height) then
- Result := GetFloatVector(X,Y)
- else
- begin
- Result.X := 0;
- Result.Y := 0;
- end;
- end;
- function TVectorMap.GetFixedVector(X, Y: Integer): TFixedVector;
- begin
- Result := FVectors[X + Y * Width];
- end;
- function TVectorMap.GetFixedVectorS(X, Y: Integer): TFixedVector;
- begin
- if (X >= 0) and (Y >= 0) and
- (X < Width) and (Y < Height) then
- Result := GetFixedVector(X,Y)
- else
- begin
- Result.X := 0;
- Result.Y := 0;
- end;
- end;
- function TVectorMap.GetFixedVectorX(X, Y: TFixed): TFixedVector;
- const
- Next = SizeOf(TFixedVector);
- var
- WX,WY: TFixed;
- W, H: Integer;
- P: Pointer;
- begin
- WX := TFixedRec(X).Int;
- WY := TFixedRec(Y).Int;
- W := Width;
- H := Height;
- if (WX >= 0) and (WX <= W - 1) and (WY >= 0) and (WY <= H - 1) then
- begin
- P := @FVectors[WX + WY * W];
- if (WY = H - 1) then
- W := 0
- else
- W := W * Next;
- if (WX = W - 1) then
- H := 0
- else
- H := Next;
- WX := TFixedRec(X).Frac;
- WY := TFixedRec(Y).Frac;
- Result := CombineVectorsReg(CombineVectorsReg(PFixedPoint(P)^,
- PFixedPoint(NativeUInt(P) + NativeUInt(H))^, WX), CombineVectorsReg(
- PFixedPoint(NativeUInt(P) + NativeUInt(W))^,
- PFixedPoint(NativeUInt(P) + NativeUInt(W + H))^, WX), WY);
- end else
- begin
- Result.X := 0;
- Result.Y := 0;
- end;
- end;
- function TVectorMap.GetFixedVectorXS(X, Y: TFixed): TFixedVector;
- var
- WX,WY: TFixed;
- begin
- WX := TFixedRec(X).Frac;
- X := TFixedRec(X).Int;
- WY := TFixedRec(Y).Frac;
- Y := TFixedRec(Y).Int;
- Result := CombineVectorsReg(CombineVectorsReg(FixedVectorS[X,Y], FixedVectorS[X + 1,Y], WX),
- CombineVectorsReg(FixedVectorS[X,Y + 1], FixedVectorS[X + 1,Y + 1], WX), WY);
- end;
- function TVectorMap.Empty: Boolean;
- begin
- Result := false;
- if (Width = 0) or (Height = 0) or (FVectors = nil) then Result := True;
- end;
- const
- MeshIdent = 'yfqLhseM';
- type
- {TVectorMap supports the photoshop liquify mesh fileformat .msh}
- TPSLiquifyMeshHeader = record
- Pad0 : cardinal;
- Ident : array [0..7] of Char;
- Pad1 : cardinal;
- Width : cardinal;
- Height: cardinal;
- end;
- procedure TVectorMap.LoadFromFile(const FileName: string);
- procedure ConvertVertices;
- var
- I: Integer;
- begin
- for I := 0 to Length(FVectors) - 1 do
- begin
- //Not a mistake! Converting physical mem. directly to avoid temporary floating point buffer
- //Do no change to PFloat.. the type is relative to the msh format.
- FVectors[I].X := Fixed(PSingle(@FVectors[I].X)^);
- FVectors[I].Y := Fixed(PSingle(@FVectors[I].Y)^);
- end;
- end;
- var
- Header: TPSLiquifyMeshHeader;
- MeshFile: File;
- begin
- If FileExists(Filename) then
- try
- AssignFile(MeshFile, FileName);
- Reset(MeshFile, 1);
- BlockRead(MeshFile, Header, SizeOf(TPSLiquifyMeshHeader));
- if LowerCase(string(Header.Ident)) <> LowerCase(MeshIdent) then
- Exception.Create(RCStrBadFormat);
- with Header do
- begin
- SetSize(Width, Height);
- BlockRead(MeshFile, FVectors[0], Width * Height * SizeOf(TFixedVector));
- ConvertVertices;
- end;
- finally
- CloseFile(MeshFile);
- end
- else Exception.Create(RCStrFileNotFound);
- end;
- procedure TVectorMap.Merge(DstLeft, DstTop: Integer; Src: TVectorMap; SrcRect: TRect);
- var
- I,J,P: Integer;
- DstRect: TRect;
- Progression: TFixedVector;
- ProgressionX, ProgressionY: TFixed;
- CombineCallback: TVectorCombineEvent;
- DstPtr : PFixedPointArray;
- SrcPtr : PFixedPoint;
- begin
- if Src.Empty then Exception.Create(RCStrSrcIsEmpty);
- if Empty then Exception.Create(RCStrBaseIsEmpty);
- GR32.IntersectRect(SrcRect, Src.BoundsRect, SrcRect);
- DstRect.Left := DstLeft;
- DstRect.Top := DstTop;
- DstRect.Right := DstLeft + (SrcRect.Right - SrcRect.Left);
- DstRect.Bottom := DstTop + (SrcRect.Bottom - SrcRect.Top);
- GR32.IntersectRect(DstRect, BoundsRect, DstRect);
- if GR32.IsRectEmpty(DstRect) then Exit;
- P := SrcRect.Top * Src.Width;
- Progression.Y := - FixedOne;
- case Src.FVectorCombineMode of
- vcmAdd:
- begin
- for I := DstRect.Top to DstRect.Bottom do
- begin
- // Added ^ for FPC
- DstPtr := @GetVectors^[I * Width];
- SrcPtr := @Src.GetVectors^[SrcRect.Left + P];
- for J := DstRect.Left to DstRect.Right do
- begin
- Inc(SrcPtr^.X, DstPtr[J].X);
- Inc(SrcPtr^.Y, DstPtr[J].Y);
- Inc(SrcPtr);
- end;
- Inc(P, Src.Width);
- end;
- end;
- vcmReplace:
- begin
- for I := DstRect.Top to DstRect.Bottom do
- begin
- // Added ^ for FPC
- DstPtr := @GetVectors^[I * Width];
- SrcPtr := @Src.GetVectors^[SrcRect.Left + P];
- for J := DstRect.Left to DstRect.Right do
- begin
- SrcPtr^.X := DstPtr[J].X;
- SrcPtr^.Y := DstPtr[J].Y;
- Inc(SrcPtr);
- end;
- Inc(P, Src.Width);
- end;
- end;
- else
- CombineCallback := Src.FOnVectorCombine;
- ProgressionX := Fixed(2 / (DstRect.Right - DstRect.Left - 1));
- ProgressionY := Fixed(2 / (DstRect.Bottom - DstRect.Top - 1));
- for I := DstRect.Top to DstRect.Bottom do
- begin
- Progression.X := - FixedOne;
- // Added ^ for FPC
- DstPtr := @GetVectors^[I * Width];
- SrcPtr := @Src.GetVectors^[SrcRect.Left + P];
- for J := DstRect.Left to DstRect.Right do
- begin
- CombineCallback(SrcPtr^, Progression, DstPtr[J]);
- Inc(SrcPtr);
- Inc(Progression.X, ProgressionX);
- end;
- Inc(P, Src.Width);
- Inc(Progression.Y, ProgressionY);
- end;
- end;
- end;
- procedure TVectorMap.SaveToFile(const FileName: string);
- procedure ConvertVerticesX;
- var
- I: Integer;
- begin
- for I := 0 to Length(FVectors) - 1 do
- begin
- //Not a mistake! Converting physical mem. directly to avoid temporary floating point buffer
- //Do no change to PFloat.. the type is relative to the msh format.
- FVectors[I].X := Fixed(PSingle(@FVectors[I].X)^);
- FVectors[I].Y := Fixed(PSingle(@FVectors[I].Y)^);
- end;
- end;
- procedure ConvertVerticesF;
- var
- I: Integer;
- {$if (defined(CompilerVersion)) and (CompilerVersion = 31)}
- f: single;
- {$ifend}
- begin
- for I := 0 to Length(FVectors) - 1 do
- begin
- //Not a mistake! Converting physical mem. directly to avoid temporary floating point buffer
- //Do no change to PFloat.. the type is relative to the msh format.
- // Workaround for Delphi 10.1 Internal Error C6949 ...
- {$if (defined(CompilerVersion)) and (CompilerVersion = 31)}
- f := FVectors[I].X * FixedToFloat;
- FVectors[I].X := PInteger(@f)^;
- f := FVectors[I].Y * FixedToFloat;
- FVectors[I].Y := PInteger(@f)^;
- {$else}
- PSingle(@FVectors[I].X)^ := FVectors[I].X * FixedToFloat;
- PSingle(@FVectors[I].Y)^ := FVectors[I].Y * FixedToFloat;
- {$ifend}
- end;
- end;
- var
- Header: TPSLiquifyMeshHeader;
- MeshFile: File;
- Pad: Cardinal;
- begin
- try
- AssignFile(MeshFile, FileName);
- Rewrite(MeshFile, 1);
- with Header do
- begin
- Pad0 := $02000000;
- Ident := MeshIdent;
- Pad1 := $00000002;
- Width := Self.Width;
- Height := Self.Height;
- end;
- BlockWrite(MeshFile, Header, SizeOf(TPSLiquifyMeshHeader));
- with Header do
- begin
- ConvertVerticesF;
- BlockWrite(MeshFile, FVectors[0], Length(FVectors) * SizeOf(TFixedVector));
- ConvertVerticesX;
- end;
- if Odd(Length(FVectors) * SizeOf(TFixedVector) - 1) then
- begin
- Pad := $00000000;
- BlockWrite(MeshFile, Pad, 4);
- BlockWrite(MeshFile, Pad, 4);
- end;
- finally
- CloseFile(MeshFile);
- end;
- end;
- procedure TVectorMap.SetFloatVector(X, Y: Integer; const Point: TFloatVector);
- begin
- FVectors[X + Y * Width] := FixedPoint(Point);
- end;
- procedure TVectorMap.SetFloatVectorF(X, Y: Single; const Point: TFloatVector);
- begin
- SetFixedVectorX(Fixed(X), Fixed(Y), FixedPoint(Point));
- end;
- procedure TVectorMap.SetFloatVectorFS(X, Y: Single; const Point: TFloatVector);
- begin
- SetFixedVectorXS(Fixed(X), Fixed(Y), FixedPoint(Point));
- end;
- procedure TVectorMap.SetFloatVectorS(X, Y: Integer; const Point: TFloatVector);
- begin
- if (X >= 0) and (X < Width) and
- (Y >= 0) and (Y < Height) then
- FVectors[X + Y * Width] := FixedPoint(Point);
- end;
- procedure TVectorMap.SetFixedVector(X, Y: Integer; const Point: TFixedVector);
- begin
- FVectors[X + Y * Width] := Point;
- end;
- procedure TVectorMap.SetFixedVectorS(X, Y: Integer; const Point: TFixedVector);
- begin
- if (X >= 0) and (X < Width) and
- (Y >= 0) and (Y < Height) then
- FVectors[X + Y * Width] := Point;
- end;
- procedure TVectorMap.SetFixedVectorX(X, Y: TFixed; const Point: TFixedVector);
- var
- flrx, flry, celx, cely: Integer;
- P: PFixedPoint;
- begin
- flrx := TFixedRec(X).Frac;
- celx := flrx xor $FFFF;
- flry := TFixedRec(Y).Frac;
- cely := flry xor $FFFF;
- P := @FVectors[TFixedRec(X).Int + TFixedRec(Y).Int * Width];
- CombineVectorsMem(Point, P^, FixedMul(celx, cely)); Inc(P);
- CombineVectorsMem(Point, P^, FixedMul(flrx, cely)); Inc(P, Width);
- CombineVectorsMem(Point, P^, FixedMul(flrx, flry)); Dec(P);
- CombineVectorsMem(Point, P^, FixedMul(celx, flry));
- end;
- procedure TVectorMap.SetFixedVectorXS(X, Y: TFixed; const Point: TFixedVector);
- var
- flrx, flry, celx, cely: Integer;
- P: PFixedPoint;
- begin
- if (X < -$10000) or (Y < -$10000) then Exit;
- flrx := TFixedRec(X).Frac;
- X := TFixedRec(X).Int;
- flry := TFixedRec(Y).Frac;
- Y := TFixedRec(Y).Int;
- if (X >= Width) or (Y >= Height) then Exit;
- celx := flrx xor $FFFF;
- cely := flry xor $FFFF;
- P := @FVectors[X + Y * Width];
- if (X >= 0) and (Y >= 0)then
- begin
- CombineVectorsMem(Point, P^, FixedMul(celx, cely) ); Inc(P);
- CombineVectorsMem(Point, P^, FixedMul(flrx, cely) ); Inc(P, Width);
- CombineVectorsMem(Point, P^, FixedMul(flrx, flry) ); Dec(P);
- CombineVectorsMem(Point, P^, FixedMul(celx, flry) );
- end
- else
- begin
- if (X >= 0) and (Y >= 0) then CombineVectorsMem(Point, P^, FixedMul(celx, cely)); Inc(P);
- if (X < Width - 1) and (Y >= 0) then CombineVectorsMem(Point, P^, FixedMul(flrx, cely)); Inc(P, Width);
- if (X < Width - 1) and (Y < Height - 1) then CombineVectorsMem(Point, P^, FixedMul(flrx, flry)); Dec(P);
- if (X >= 0) and (Y < Height - 1) then CombineVectorsMem(Point, P^, FixedMul(celx, flry));
- end;
- end;
- procedure TVectorMap.SetVectorCombineMode(const Value: TVectorCombineMode);
- begin
- if FVectorCombineMode <> Value then
- begin
- FVectorCombineMode := Value;
- Changed;
- end;
- end;
- function TVectorMap.GetTrimmedBounds: TRect;
- var
- J: Integer;
- VectorPtr : PFixedVector;
- label
- TopDone, BottomDone, LeftDone, RightDone;
- begin
- with Result do
- begin
- //Find Top
- Top := 0;
- VectorPtr := @Vectors[Top];
- repeat
- if Int64(VectorPtr^) <> 0 then goto TopDone;
- Inc(VectorPtr);
- Inc(Top);
- until Top = Self.Width * Self.Height;
- TopDone: Top := Top div Self.Width;
- //Find Bottom
- Bottom := Self.Width * Self.Height - 1;
- VectorPtr := @Vectors[Bottom];
- repeat
- if Int64(VectorPtr^) <> 0 then goto BottomDone;
- Dec(VectorPtr);
- Dec(Bottom);
- until Bottom < 0;
- BottomDone: Bottom := Bottom div Self.Width - 1;
- //Find Left
- Left := 0;
- repeat
- J := Top;
- repeat
- if Int64(FixedVector[Left, J]) <> 0 then goto LeftDone;
- Inc(J);
- until J >= Bottom;
- Inc(Left)
- until Left >= Self.Width;
- LeftDone:
- //Find Right
- Right := Self.Width - 1;
- repeat
- J := Bottom;
- repeat
- if Int64(FixedVector[Right, J]) <> 0 then goto RightDone;
- Dec(J);
- until J <= Top;
- Dec(Right)
- until Right <= Left;
- end;
- RightDone:
- if GR32.IsRectEmpty(Result) then
- Result := MakeRect(0, 0, 0, 0);
- end;
- end.
|