Componentes.Terceros.jcl/official/1.96/source/visclx/JclQGraphics.pas

1512 lines
41 KiB
ObjectPascal

{**************************************************************************************************}
{ WARNING: JEDI preprocessor generated unit. Do not edit. }
{**************************************************************************************************}
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ 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. }
{ }
{ The Original Code is JclGraphics.pas. }
{ }
{ The resampling algorithms and methods used in this library were adapted by Anders Melander from }
{ the article "General Filtered Image Rescaling" by Dale Schumacher which appeared in the book }
{ Graphics Gems III, published by Academic Press, Inc. Additional improvements were done by David }
{ Ullrich and Josha Beukema. }
{ }
{ (C)opyright 1997-1999 Anders Melander }
{ }
{ The Initial Developers of the Original Code are Alex Denissov, Wim De Cleen, Anders Melander }
{ and Mike Lischke. Portions created by these individuals are Copyright (C) of these individuals. }
{ All Rights Reserved. }
{ }
{ Contributors: }
{ Alexander Radchenko }
{ Charlie Calvert }
{ Marcel van Brakel }
{ Marcin Wieczorek }
{ Matthias Thoma (mthoma) }
{ Petr Vones (pvones) }
{ Robert Marquardt (marquardt) }
{ Robert Rossmair (rrossmair) }
{ Dejoy Den (dejoy) }
{ }
{**************************************************************************************************}
// For history, see end of file
unit JclQGraphics;
{$I jcl.inc}
interface
uses
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF MSWINDOWS}
Classes, SysUtils,
Types, QGraphics, JclQGraphUtils,
JclBase;
type
EJclGraphicsError = class(EJclError);
TDynDynIntegerArrayArray = array of TDynIntegerArray;
TDynPointArray = array of TPoint;
TDynDynPointArrayArray = array of TDynPointArray;
TPointF = record
X: Single;
Y: Single;
end;
TDynPointArrayF = array of TPointF;
{ TJclBitmap32 draw mode }
TDrawMode = (dmOpaque, dmBlend);
{ stretch filter }
TStretchFilter = (sfNearest, sfLinear, sfSpline);
TConversionKind = (ckRed, ckGreen, ckBlue, ckAlpha, ckUniformRGB, ckWeightedRGB);
{ resampling support types }
TResamplingFilter =
(rfBox, rfTriangle, rfHermite, rfBell, rfSpline, rfLanczos3, rfMitchell);
{ Matrix declaration for transformation }
// modify Jan 28, 2001 for use under BCB5
// the compiler show error 245 "language feature ist not available"
// we must take a record and under this we can use the static array
// Note: the sourcecode modify general from M[] to M.A[] !!!!!
// TMatrix3d = array [0..2, 0..2] of Extended; // 3x3 double precision
TMatrix3d = record
A: array [0..2, 0..2] of Extended;
end;
TDynDynPointArrayArrayF = array of TDynPointArrayF;
TScanLine = array of Integer;
TScanLines = array of TScanLine;
TLUT8 = array [Byte] of Byte;
TGamma = array [Byte] of Byte;
TColorChannel = (ccRed, ccGreen, ccBlue, ccAlpha);
TGradientDirection = (gdVertical, gdHorizontal);
TPolyFillMode = (fmAlternate, fmWinding);
TJclRegionCombineOperator = (coAnd, coDiff, coOr, coXor);
TJclRegionBitmapMode = (rmInclude, rmExclude);
TJclRegionKind = (rkNull, rkSimple, rkComplex, rkError);
// modify Jan 28, 2001 for use under BCB5
// the compiler show error 245 "language feature ist not available"
// wie must take a record and under this we can use the static array
// Note: for init the array we used initialisation at the end of this unit
//
// const
// IdentityMatrix: TMatrix3d = (
// (1, 0, 0),
// (0, 1, 0),
// (0, 0, 1));
var
IdentityMatrix: TMatrix3d;
// Classes
type
TJclTransformation = class(TObject)
public
function GetTransformedBounds(const Src: TRect): TRect; virtual; abstract;
procedure PrepareTransform; virtual; abstract;
procedure Transform(DstX, DstY: Integer; out SrcX, SrcY: Integer); virtual; abstract;
procedure Transform256(DstX, DstY: Integer; out SrcX256, SrcY256: Integer); virtual; abstract;
end;
TJclLinearTransformation = class(TJclTransformation)
private
FMatrix: TMatrix3d;
protected
A: Integer;
B: Integer;
C: Integer;
D: Integer;
E: Integer;
F: Integer;
public
constructor Create; virtual;
function GetTransformedBounds(const Src: TRect): TRect; override;
procedure PrepareTransform; override;
procedure Transform(DstX, DstY: Integer; out SrcX, SrcY: Integer); override;
procedure Transform256(DstX, DstY: Integer; out SrcX256, SrcY256: Integer); override;
procedure Clear;
procedure Rotate(Cx, Cy, Alpha: Extended); // degrees
procedure Skew(Fx, Fy: Extended);
procedure Scale(Sx, Sy: Extended);
procedure Translate(Dx, Dy: Extended);
property Matrix: TMatrix3d read FMatrix write FMatrix;
end;
// Bitmap Functions
procedure Stretch(NewWidth, NewHeight: Cardinal; Filter: TResamplingFilter;
Radius: Single; Source: TGraphic; Target: TBitmap); overload;
procedure Stretch(NewWidth, NewHeight: Cardinal; Filter: TResamplingFilter;
Radius: Single; Bitmap: TBitmap); overload;
{$IFDEF MSWINDOWS}
procedure DrawBitmap(DC: HDC; Bitmap: HBITMAP; X, Y, Width, Height: Integer);
function ExtractIconCount(const FileName: string): Integer;
function BitmapToIcon(Bitmap: HBITMAP; cx, cy: Integer): HICON;
function IconToBitmap(Icon: HICON): HBITMAP;
{$ENDIF MSWINDOWS}
{$IFDEF MSWINDOWS}
function FillGradient(DC: HDC; ARect: TRect; ColorCount: Integer;
StartColor, EndColor: TColor; ADirection: TGradientDirection): Boolean; overload;
{$ENDIF MSWINDOWS}
implementation
uses
Math,
{$IFDEF MSWINDOWS}
CommCtrl, ShellApi,
{$ENDIF MSWINDOWS}
JclLogic;
type
TRGBInt = record
R: Integer;
G: Integer;
B: Integer;
end;
PBGRA = ^TBGRA;
TBGRA = packed record
B: Byte;
G: Byte;
R: Byte;
A: Byte;
end;
PPixelArray = ^TPixelArray;
TPixelArray = array [0..0] of TBGRA;
TBitmapFilterFunction = function(Value: Single): Single;
PContributor = ^TContributor;
TContributor = record
Weight: Integer; // Pixel Weight
Pixel: Integer; // Source Pixel
end;
TContributors = array of TContributor;
// list of source pixels contributing to a destination pixel
TContributorEntry = record
N: Integer;
Contributors: TContributors;
end;
TContributorList = array of TContributorEntry;
TJclGraphicAccess = class(TGraphic);
const
DefaultFilterRadius: array [TResamplingFilter] of Single =
(0.5, 1.0, 1.0, 1.5, 2.0, 3.0, 2.0);
_RGB: TColor32 = $00FFFFFF;
var
{ Gamma bias for line/pixel antialiasing/shape correction }
GAMMA_TABLE: TGamma;
threadvar
// globally used cache for current image (speeds up resampling about 10%)
CurrentLineR: array of Integer;
CurrentLineG: array of Integer;
CurrentLineB: array of Integer;
//=== Helper functions =======================================================
function IntToByte(Value: Integer): Byte;
begin
Result := Math.Max(0, Math.Min(255, Value));
end;
//=== Internal low level routines ============================================
procedure FillLongword(var X; Count: Integer; Value: Longword);
{asm
// EAX = X
// EDX = Count
// ECX = Value
TEST EDX, EDX
JLE @@EXIT
PUSH EDI
MOV EDI, EAX // Point EDI to destination
MOV EAX, ECX
MOV ECX, EDX
REP STOSD // Fill count dwords
POP EDI
@@EXIT:
end;}
var
P: PLongword;
begin
P := @X;
while Count > 0 do
begin
P^ := Value;
Inc(P);
Dec(Count);
end;
end;
function Clamp(Value: Integer): TColor32;
begin
if Value < 0 then
Result := 0
else
if Value > 255 then
Result := 255
else
Result := Value;
end;
procedure TestSwap(var A, B: Integer);
{asm
// EAX = [A]
// EDX = [B]
MOV ECX, [EAX] // ECX := [A]
CMP ECX, [EDX] // ECX <= [B]? Exit
JLE @@EXIT
//Replaced on more fast code
//XCHG ECX, [EDX] // ECX <-> [B];
//MOV [EAX], ECX // [A] := ECX
PUSH EBX
MOV EBX,[EDX] // EBX := [B]
MOV [EAX],EBX // [A] := EBX
MOV [EDX],ECX // [B] := ECX
POP EBX
@@EXIT:
end;}
var
X: Integer;
begin
X := A; // optimization
if X > B then
begin
A := B;
B := X;
end;
end;
function TestClip(var A, B: Integer; Size: Integer): Boolean;
begin
TestSwap(A, B); // now A = min(A,B) and B = max(A, B)
if A < 0 then
A := 0;
if B >= Size then
B := Size - 1;
Result := B >= A;
end;
function Constrain(Value, Lo, Hi: Integer): Integer;
begin
if Value <= Lo then
Result := Lo
else
if Value >= Hi then
Result := Hi
else
Result := Value;
end;
// Filter functions for stretching of TBitmaps
// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
function BitmapHermiteFilter(Value: Single): Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 1 then
Result := (2 * Value - 3) * Sqr(Value) + 1
else
Result := 0;
end;
// This filter is also known as 'nearest neighbour' Filter.
function BitmapBoxFilter(Value: Single): Single;
begin
if (Value > -0.5) and (Value <= 0.5) then
Result := 1.0
else
Result := 0.0;
end;
// aka 'linear' or 'bilinear' filter
function BitmapTriangleFilter(Value: Single): Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 1.0 then
Result := 1.0 - Value
else
Result := 0.0;
end;
function BitmapBellFilter(Value: Single): Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 0.5 then
Result := 0.75 - Sqr(Value)
else
if Value < 1.5 then
begin
Value := Value - 1.5;
Result := 0.5 * Sqr(Value);
end
else
Result := 0.0;
end;
// B-spline filter
function BitmapSplineFilter(Value: Single): Single;
var
Temp: Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 1.0 then
begin
Temp := Sqr(Value);
Result := 0.5 * Temp * Value - Temp + 2.0 / 3.0;
end
else
if Value < 2.0 then
begin
Value := 2.0 - Value;
Result := Sqr(Value) * Value / 6.0;
end
else
Result := 0.0;
end;
function BitmapLanczos3Filter(Value: Single): Single;
function SinC(Value: Single): Single;
begin
if Value <> 0.0 then
begin
Value := Value * Pi;
Result := System.Sin(Value) / Value;
end
else
Result := 1.0;
end;
begin
if Value < 0.0 then
Value := -Value;
if Value < 3.0 then
Result := SinC(Value) * SinC(Value / 3.0)
else
Result := 0.0;
end;
function BitmapMitchellFilter(Value: Single): Single;
const
B = 1.0 / 3.0;
C = 1.0 / 3.0;
var
Temp: Single;
begin
if Value < 0.0 then
Value := -Value;
Temp := Sqr(Value);
if Value < 1.0 then
begin
Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * Temp)) +
((-18.0 + 12.0 * B + 6.0 * C) * Temp) +
(6.0 - 2.0 * B));
Result := Value / 6.0;
end
else
if Value < 2.0 then
begin
Value := (((-B - 6.0 * C) * (Value * Temp)) +
((6.0 * B + 30.0 * C) * Temp) +
((-12.0 * B - 48.0 * C) * Value) +
(8.0 * B + 24.0 * C));
Result := Value / 6.0;
end
else
Result := 0.0;
end;
const
FilterList: array [TResamplingFilter] of TBitmapFilterFunction =
(
BitmapBoxFilter,
BitmapTriangleFilter,
BitmapHermiteFilter,
BitmapBellFilter,
BitmapSplineFilter,
BitmapLanczos3Filter,
BitmapMitchellFilter
);
procedure FillLineCache(N, Delta: Integer; Line: Pointer);
var
I: Integer;
Run: PBGRA;
begin
Run := Line;
for I := 0 to N - 1 do
begin
CurrentLineR[I] := Run.R;
CurrentLineG[I] := Run.G;
CurrentLineB[I] := Run.B;
Inc(PByte(Run), Delta);
end;
end;
function ApplyContributors(N: Integer; Contributors: TContributors): TBGRA;
var
J: Integer;
RGB: TRGBInt;
Total,
Weight: Integer;
Pixel: Cardinal;
Contr: PContributor;
begin
RGB.R := 0;
RGB.G := 0;
RGB.B := 0;
Total := 0;
Contr := @Contributors[0];
for J := 0 to N - 1 do
begin
Weight := Contr.Weight;
Inc(Total, Weight);
Pixel := Contr.Pixel;
Inc(RGB.R, CurrentLineR[Pixel] * Weight);
Inc(RGB.G, CurrentLineG[Pixel] * Weight);
Inc(RGB.B, CurrentLineB[Pixel] * Weight);
Inc(Contr);
end;
if Total = 0 then
begin
Result.R := IntToByte(RGB.R shr 8);
Result.G := IntToByte(RGB.G shr 8);
Result.B := IntToByte(RGB.B shr 8);
end
else
begin
Result.R := IntToByte(RGB.R div Total);
Result.G := IntToByte(RGB.G div Total);
Result.B := IntToByte(RGB.B div Total);
end;
end;
// This is the actual scaling routine. Target must be allocated already with
// sufficient size. Source must contain valid data, Radius must not be 0 and
// Filter must not be nil.
procedure DoStretch(Filter: TBitmapFilterFunction; Radius: Single; Source, Target: TBitmap);
var
ScaleX, ScaleY: Single; // Zoom scale factors
I, J, K, N: Integer; // Loop variables
Center: Single; // Filter calculation variables
Width: Single;
Weight: Integer; // Filter calculation variables
Left, Right: Integer; // Filter calculation variables
Work: TBitmap;
ContributorList: TContributorList;
SourceLine, DestLine: PPixelArray;
DestPixel: PBGRA;
Delta, DestDelta: Integer;
SourceHeight, SourceWidth: Integer;
TargetHeight, TargetWidth: Integer;
begin
// shortcut variables
SourceHeight := Source.Height;
SourceWidth := Source.Width;
TargetHeight := Target.Height;
TargetWidth := Target.Width;
// create intermediate image to hold horizontal zoom
Work := TBitmap.Create;
try
Work.PixelFormat := pf32bit;
Work.Height := SourceHeight;
Work.Width := TargetWidth;
if SourceWidth = 1 then
ScaleX := TargetWidth / SourceWidth
else
ScaleX := (TargetWidth - 1) / (SourceWidth - 1);
if SourceHeight = 1 then
ScaleY := TargetHeight / SourceHeight
else
ScaleY := (TargetHeight - 1) / (SourceHeight - 1);
// pre-calculate filter contributions for a row
SetLength(ContributorList, TargetWidth);
// horizontal sub-sampling
if ScaleX < 1 then
begin
// scales from bigger to smaller Width
Width := Radius / ScaleX;
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleX;
Left := Math.Floor(Center - Width);
Right := Math.Ceil(Center + Width);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleX) * ScaleX * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceWidth then
N := SourceWidth - J + SourceWidth - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end
else
begin
// horizontal super-sampling
// scales from smaller to bigger Width
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleX;
Left := Math.Floor(Center - Radius);
Right := Math.Ceil(Center + Radius);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceWidth then
N := SourceWidth - J + SourceWidth - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;
// now apply filter to sample horizontally from Src to Work
SetLength(CurrentLineR, SourceWidth);
SetLength(CurrentLineG, SourceWidth);
SetLength(CurrentLineB, SourceWidth);
for K := 0 to SourceHeight - 1 do
begin
SourceLine := Source.ScanLine[K];
FillLineCache(SourceWidth, SizeOf(TBGRA), SourceLine);
DestPixel := Work.ScanLine[K];
for I := 0 to TargetWidth - 1 do
with ContributorList[I] do
begin
DestPixel^ := ApplyContributors(N, ContributorList[I].Contributors);
// move on to next column
Inc(DestPixel);
end;
end;
// free the memory allocated for horizontal filter weights, since we need
// the structure again
for I := 0 to TargetWidth - 1 do
ContributorList[I].Contributors := nil;
ContributorList := nil;
// pre-calculate filter contributions for a column
SetLength(ContributorList, TargetHeight);
// vertical sub-sampling
if ScaleY < 1 then
begin
// scales from bigger to smaller height
Width := Radius / ScaleY;
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleY;
Left := Math.Floor(Center - Width);
Right := Math.Ceil(Center + Width);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleY) * ScaleY * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceHeight then
N := SourceHeight - J + SourceHeight - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end
else
begin
// vertical super-sampling
// scales from smaller to bigger height
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleY;
Left := Math.Floor(Center - Radius);
Right := Math.Ceil(Center + Radius);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceHeight then
N := SourceHeight - J + SourceHeight - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;
// apply filter to sample vertically from Work to Target
SetLength(CurrentLineR, SourceHeight);
SetLength(CurrentLineG, SourceHeight);
SetLength(CurrentLineB, SourceHeight);
SourceLine := Work.ScanLine[0];
Delta := Integer(Work.ScanLine[1]) - Integer(SourceLine);
DestLine := Target.ScanLine[0];
DestDelta := Integer(Target.ScanLine[1]) - Integer(DestLine);
for K := 0 to TargetWidth - 1 do
begin
DestPixel := Pointer(DestLine);
FillLineCache(SourceHeight, Delta, SourceLine);
for I := 0 to TargetHeight - 1 do
with ContributorList[I] do
begin
DestPixel^ := ApplyContributors(N, ContributorList[I].Contributors);
Inc(Integer(DestPixel), DestDelta);
end;
Inc(SourceLine);
Inc(DestLine);
end;
// free the memory allocated for vertical filter weights
for I := 0 to TargetHeight - 1 do
ContributorList[I].Contributors := nil;
// this one is done automatically on exit, but is here for completeness
ContributorList := nil;
finally
Work.Free;
CurrentLineR := nil;
CurrentLineG := nil;
CurrentLineB := nil;
Target.Modified := True;
end;
end;
// Filter functions for TJclBitmap32
type
TPointRec = record
Pos: Integer;
Weight: Integer;
end;
TCluster = array of TPointRec;
TMappingTable = array of TCluster;
TFilterFunc = function(Value: Extended): Extended;
function NearestFilter(Value: Extended): Extended;
begin
if (Value > -0.5) and (Value <= 0.5) then
Result := 1
else
Result := 0;
end;
function LinearFilter(Value: Extended): Extended;
begin
if Value < -1 then
Result := 0
else
if Value < 0 then
Result := 1 + Value
else
if Value < 1 then
Result := 1 - Value
else
Result := 0;
end;
function SplineFilter(Value: Extended): Extended;
var
tt: Extended;
begin
Value := Abs(Value);
if Value < 1 then
begin
tt := Sqr(Value);
Result := 0.5 * tt * Value - tt + 2 / 3;
end
else
if Value < 2 then
begin
Value := 2 - Value;
Result := 1 / 6 * Sqr(Value) * Value;
end
else
Result := 0;
end;
function BuildMappingTable(DstWidth, SrcFrom, SrcWidth: Integer;
StretchFilter: TStretchFilter): TMappingTable;
const
FILTERS: array [TStretchFilter] of TFilterFunc =
(NearestFilter, LinearFilter, SplineFilter);
var
Filter: TFilterFunc;
FilterWidth: Extended;
Scale, OldScale: Extended;
Center: Extended;
Bias: Extended;
Left, Right: Integer;
I, J, K: Integer;
Weight: Integer;
begin
if SrcWidth = 0 then
begin
Result := nil;
Exit;
end;
Filter := FILTERS[StretchFilter];
if StretchFilter in [sfNearest, sfLinear] then
FilterWidth := 1
else
FilterWidth := 1.5;
SetLength(Result, DstWidth);
Scale := (DstWidth - 1) / (SrcWidth - 1);
if Scale < 1 then
begin
OldScale := Scale;
Scale := 1 / Scale;
FilterWidth := FilterWidth * Scale;
for I := 0 to DstWidth - 1 do
begin
Center := I * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
Bias := 0;
for J := Left to Right do
begin
Weight := Round(255 * Filter((Center - J) * OldScale) * OldScale);
if Weight <> 0 then
begin
Bias := Bias + Weight / 255;
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J + SrcFrom, 0, SrcWidth - 1);
Result[I][K].Weight := Weight;
end;
end;
if (Bias > 0) and (Bias <> 1) then
begin
Bias := 1 / Bias;
for K := 0 to High(Result[I]) do
Result[I][K].Weight := Round(Result[I][K].Weight * Bias);
end;
end;
end
else
begin
FilterWidth := 1 / FilterWidth;
Scale := 1 / Scale;
for I := 0 to DstWidth - 1 do
begin
Center := I * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
for J := Left to Right do
begin
Weight := Round(255 * Filter(Center - J));
if Weight <> 0 then
begin
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J + SrcFrom, 0, SrcWidth - 1);
Result[I][K].Weight := Weight;
end;
end;
end;
end;
end;
// Bitmap Functions
// Scales the source graphic to the given size (NewWidth, NewHeight) and stores the Result in Target.
// Filter describes the filter function to be applied and Radius the size of the filter area.
// Is Radius = 0 then the recommended filter area will be used (see DefaultFilterRadius).
procedure Stretch(NewWidth, NewHeight: Cardinal; Filter: TResamplingFilter;
Radius: Single; Source: TGraphic; Target: TBitmap);
var
Temp: TBitmap;
begin
if Source.Empty then
Exit; // do nothing
if Radius = 0 then
Radius := DefaultFilterRadius[Filter];
Temp := TBitmap.Create;
try
// To allow Source = Target, the following assignment needs to be done initially
Temp.Assign(Source);
Temp.PixelFormat := pf32bit;
Target.FreeImage;
Target.PixelFormat := pf32bit;
Target.Width := NewWidth;
Target.Height := NewHeight;
DoStretch(FilterList[Filter], Radius, Temp, Target);
finally
Temp.Free;
end;
end;
procedure Stretch(NewWidth, NewHeight: Cardinal; Filter: TResamplingFilter;
Radius: Single; Bitmap: TBitmap);
begin
Stretch(NewWidth, NewHeight, Filter, Radius, Bitmap, Bitmap);
end;
{$IFDEF MSWINDOWS}
procedure DrawBitmap(DC: HDC; Bitmap: HBITMAP; X, Y, Width, Height: Integer);
var
MemDC: HDC;
OldBitmap: HBITMAP;
begin
MemDC := CreateCompatibleDC(DC);
OldBitmap := SelectObject(MemDC, Bitmap);
BitBlt(DC, X, Y, Width, Height, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDC, OldBitmap);
DeleteObject(MemDC);
end;
{$ENDIF MSWINDOWS}
{$IFDEF MSWINDOWS}
function ExtractIconCount(const FileName: string): Integer;
begin
Result := ExtractIcon(HInstance, PChar(FileName), $FFFFFFFF);
end;
function BitmapToIcon(Bitmap: HBITMAP; cx, cy: Integer): HICON;
var
ImgList: HIMAGELIST;
I: Integer;
begin
ImgList := ImageList_Create(cx, cy, ILC_COLOR, 1, 1);
try
I := ImageList_Add(ImgList, Bitmap, 0);
Result := ImageList_GetIcon(ImgList, I, ILD_NORMAL);
finally
ImageList_Destroy(ImgList);
end;
end;
function IconToBitmap(Icon: HICON): HBITMAP;
var
IconInfo: TIconInfo;
begin
Result := 0;
if GetIconInfo(Icon, IconInfo) then
begin
DeleteObject(IconInfo.hbmMask);
Result := IconInfo.hbmColor;
end;
end;
{$ENDIF MSWINDOWS}
{$IFDEF MSWINDOWS}
function FillGradient(DC: HDC; ARect: TRect; ColorCount: Integer;
StartColor, EndColor: TColor; ADirection: TGradientDirection): Boolean;
var
StartRGB: array [0..2] of Byte;
RGBKoef: array [0..2] of Double;
Brush: HBRUSH;
AreaWidth, AreaHeight, I: Integer;
ColorRect: TRect;
RectOffset: Double;
begin
RectOffset := 0;
Result := False;
if ColorCount < 1 then
Exit;
StartColor := ColorToRGB(StartColor);
EndColor := ColorToRGB(EndColor);
StartRGB[0] := GetRValue(StartColor);
StartRGB[1] := GetGValue(StartColor);
StartRGB[2] := GetBValue(StartColor);
RGBKoef[0] := (GetRValue(EndColor) - StartRGB[0]) / ColorCount;
RGBKoef[1] := (GetGValue(EndColor) - StartRGB[1]) / ColorCount;
RGBKoef[2] := (GetBValue(EndColor) - StartRGB[2]) / ColorCount;
AreaWidth := ARect.Right - ARect.Left;
AreaHeight := ARect.Bottom - ARect.Top;
case ADirection of
gdHorizontal:
RectOffset := AreaWidth / ColorCount;
gdVertical:
RectOffset := AreaHeight / ColorCount;
end;
for I := 0 to ColorCount - 1 do
begin
Brush := CreateSolidBrush(RGB(
StartRGB[0] + Round((I + 1) * RGBKoef[0]),
StartRGB[1] + Round((I + 1) * RGBKoef[1]),
StartRGB[2] + Round((I + 1) * RGBKoef[2])));
case ADirection of
gdHorizontal:
SetRect(ColorRect, Round(RectOffset * I), 0, Round(RectOffset * (I + 1)), AreaHeight);
gdVertical:
SetRect(ColorRect, 0, Round(RectOffset * I), AreaWidth, Round(RectOffset * (I + 1)));
end;
OffsetRect(ColorRect, ARect.Left, ARect.Top);
FillRect(DC, ColorRect, Brush);
DeleteObject(Brush);
end;
Result := True;
end;
{$ENDIF MSWINDOWS}
//=== Matrices ===============================================================
{ TODO -oWIMDC -cReplace : Insert JclMatrix support }
function _DET(a1, a2, b1, b2: Extended): Extended; overload;
begin
Result := a1 * b2 - a2 * b1;
end;
function _DET(a1, a2, a3, b1, b2, b3, c1, c2, c3: Extended): Extended; overload;
begin
Result :=
a1 * (b2 * c3 - b3 * c2) -
b1 * (a2 * c3 - a3 * c2) +
c1 * (a2 * b3 - a3 * b2);
end;
procedure Adjoint(var M: TMatrix3d);
var
a1, a2, a3: Extended;
b1, b2, b3: Extended;
c1, c2, c3: Extended;
begin
a1 := M.A[0, 0];
a2 := M.A[0, 1];
a3 := M.A[0, 2];
b1 := M.A[1, 0];
b2 := M.A[1, 1];
b3 := M.A[1, 2];
c1 := M.A[2, 0];
c2 := M.A[2, 1];
c3 := M.A[2, 2];
M.A[0, 0]:= _DET(b2, b3, c2, c3);
M.A[0, 1]:= -_DET(a2, a3, c2, c3);
M.A[0, 2]:= _DET(a2, a3, b2, b3);
M.A[1, 0]:= -_DET(b1, b3, c1, c3);
M.A[1, 1]:= _DET(a1, a3, c1, c3);
M.A[1, 2]:= -_DET(a1, a3, b1, b3);
M.A[2, 0]:= _DET(b1, b2, c1, c2);
M.A[2, 1]:= -_DET(a1, a2, c1, c2);
M.A[2, 2]:= _DET(a1, a2, b1, b2);
end;
function Determinant(const M: TMatrix3d): Extended;
begin
Result := _DET(
M.A[0, 0], M.A[1, 0], M.A[2, 0],
M.A[0, 1], M.A[1, 1], M.A[2, 1],
M.A[0, 2], M.A[1, 2], M.A[2, 2]);
end;
procedure Scale(var M: TMatrix3d; Factor: Extended);
var
I, J: Integer;
begin
for I := 0 to 2 do
for J := 0 to 2 do
M.A[I, J] := M.A[I, J] * Factor;
end;
procedure InvertMatrix(var M: TMatrix3d);
var
Det: Extended;
begin
Det := Determinant(M);
if Abs(Det) < 1E-5 then
M := IdentityMatrix
else
begin
Adjoint(M);
Scale(M, 1 / Det);
end;
end;
function Mult(const M1, M2: TMatrix3d): TMatrix3d;
var
I, J: Integer;
begin
for I := 0 to 2 do
for J := 0 to 2 do
Result.A[I, J] :=
M1.A[0, J] * M2.A[I, 0] +
M1.A[1, J] * M2.A[I, 1] +
M1.A[2, J] * M2.A[I, 2];
end;
type
TVector3d = array [0..2] of Extended;
TVector3i = array [0..2] of Integer;
function VectorTransform(const M: TMatrix3d; const V: TVector3d): TVector3d;
begin
Result[0] := M.A[0, 0] * V[0] + M.A[1, 0] * V[1] + M.A[2, 0] * V[2];
Result[1] := M.A[0, 1] * V[0] + M.A[1, 1] * V[1] + M.A[2, 1] * V[2];
Result[2] := M.A[0, 2] * V[0] + M.A[1, 2] * V[1] + M.A[2, 2] * V[2];
end;
//=== { TJclLinearTransformation } ===========================================
constructor TJclLinearTransformation.Create;
begin
inherited Create;
Clear;
end;
procedure TJclLinearTransformation.Clear;
begin
FMatrix := IdentityMatrix;
end;
function TJclLinearTransformation.GetTransformedBounds(const Src: TRect): TRect;
var
V1, V2, V3, V4: TVector3d;
begin
V1[0] := Src.Left;
V1[1] := Src.Top;
V1[2] := 1;
V2[0] := Src.Right - 1;
V2[1] := V1[1];
V2[2] := 1;
V3[0] := V1[0];
V3[1] := Src.Bottom - 1;
V3[2] := 1;
V4[0] := V2[0];
V4[1] := V3[1];
V4[2] := 1;
V1 := VectorTransform(Matrix, V1);
V2 := VectorTransform(Matrix, V2);
V3 := VectorTransform(Matrix, V3);
V4 := VectorTransform(Matrix, V4);
Result.Left := Round(Min(Min(V1[0], V2[0]), Min(V3[0], V4[0])) - 0.5);
Result.Right := Round(Max(Max(V1[0], V2[0]), Max(V3[0], V4[0])) + 0.5);
Result.Top := Round(Min(Min(V1[1], V2[1]), Min(V3[1], V4[1])) - 0.5);
Result.Bottom := Round(Max(Max(V1[1], V2[1]), Max(V3[1], V4[1])) + 0.5);
end;
procedure TJclLinearTransformation.PrepareTransform;
var
M: TMatrix3d;
begin
M := Matrix;
InvertMatrix(M);
// calculate a fixed point (4096) factors
A := Round(M.A[0, 0] * 4096);
B := Round(M.A[1, 0] * 4096);
C := Round(M.A[2, 0] * 4096);
D := Round(M.A[0, 1] * 4096);
E := Round(M.A[1, 1] * 4096);
F := Round(M.A[2, 1] * 4096);
end;
procedure TJclLinearTransformation.Rotate(Cx, Cy, Alpha: Extended);
var
S, C: Extended;
M: TMatrix3d;
begin
if (Cx <> 0) and (Cy <> 0) then
Translate(-Cx, -Cy);
SinCos(DegToRad(Alpha), S, C);
M := IdentityMatrix;
M.A[0, 0] := C;
M.A[1, 0] := S;
M.A[0, 1] := -S;
M.A[1, 1] := C;
FMatrix := Mult(M, FMatrix);
if (Cx <> 0) and (Cy <> 0) then
Translate(Cx, Cy);
end;
procedure TJclLinearTransformation.Scale(Sx, Sy: Extended);
var
M: TMatrix3d;
begin
M := IdentityMatrix;
M.A[0, 0] := Sx;
M.A[1, 1] := Sy;
FMatrix := Mult(M, FMatrix);
end;
procedure TJclLinearTransformation.Skew(Fx, Fy: Extended);
var
M: TMatrix3d;
begin
M := IdentityMatrix;
M.A[1, 0] := Fx;
M.A[0, 1] := Fy;
FMatrix := Mult(M, FMatrix);
end;
procedure TJclLinearTransformation.Transform(DstX, DstY: Integer;
out SrcX, SrcY: Integer);
begin
SrcX := Sar(DstX * A + DstY * B + C, 12);
SrcY := Sar(DstX * D + DstY * E + F, 12);
end;
procedure TJclLinearTransformation.Transform256(DstX, DstY: Integer;
out SrcX256, SrcY256: Integer);
begin
SrcX256 := Sar(DstX * A + DstY * B + C, 4);
SrcY256 := Sar(DstX * D + DstY * E + F, 4);
end;
procedure TJclLinearTransformation.Translate(Dx, Dy: Extended);
var
M: TMatrix3d;
begin
M := IdentityMatrix;
M.A[2, 0] := Dx;
M.A[2, 1] := Dy;
FMatrix := Mult(M, FMatrix);
end;
//=== PolyLines and Polygons =================================================
procedure QSortLine(const ALine: TScanLine; L, R: Integer);
var
I, J, P: Integer;
begin
repeat
I := L;
J := R;
P := ALine[(L + R) shr 1];
repeat
while ALine[I] < P do
Inc(I);
while ALine[J] > P do
Dec(J);
if I <= J then
begin
SwapOrd(ALine[I], ALine[J]);
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
QSortLine(ALine, L, J);
L := I;
until I >= R;
end;
procedure SortLine(const ALine: TScanLine);
var
L: Integer;
begin
L := Length(ALine);
Assert(not Odd(L));
if L = 2 then
TestSwap(ALine[0], ALine[1])
else
if L > 2 then
QSortLine(ALine, 0, L - 1);
end;
procedure SortLines(const ScanLines: TScanLines);
var
I: Integer;
begin
for I := 0 to High(ScanLines) do
SortLine(ScanLines[I]);
end;
procedure AddPolygon(const Points: TDynPointArray; BaseY: Integer;
MaxX, MaxY: Integer; var ScanLines: TScanLines; SubSampleX: Boolean);
var
I, X1, Y1, X2, Y2: Integer;
Direction, PrevDirection: Integer; // up = 1 or down = -1
procedure AddEdgePoint(X, Y: Integer);
var
L: Integer;
begin
if (Y < 0) or (Y > MaxY) then
Exit;
X := Constrain(X, 0, MaxX);
L := Length(ScanLines[Y - BaseY]);
SetLength(ScanLines[Y - BaseY], L + 1);
ScanLines[Y - BaseY][L] := X;
end;
procedure DrawEdge(X1, Y1, X2, Y2: Integer);
var
X, Y, I: Integer;
Dx, Dy, Sx, Sy: Integer;
Delta: Integer;
begin
// this function 'renders' a line into the edge (ScanLines) buffer
if Y2 = Y1 then
Exit;
Dx := X2 - X1;
Dy := Y2 - Y1;
if Dy > 0 then
Sy := 1
else
begin
Sy := -1;
Dy := -Dy;
end;
if Dx > 0 then
Sx := 1
else
begin
Sx := -1;
Dx := -Dx;
end;
Delta := (Dx mod Dy) shr 1;
X := X1;
Y := Y1;
for I := 0 to Dy - 1 do
begin
AddEdgePoint(X, Y);
Inc(Y, Sy);
Inc(Delta, Dx);
while Delta > Dy do
begin
Inc(X, Sx);
Dec(Delta, Dy);
end;
end;
end;
begin
X1 := Points[0].X;
Y1 := Points[0].Y;
if SubSampleX then
X1 := X1 shl 8;
// find the last Y different from Y1 and assign it to Y0
PrevDirection := 0;
for I := High(Points) downto 1 do
begin
if Points[I].Y > Y1 then
PrevDirection := -1
else
if Points[I].Y < Y1 then
PrevDirection := 1
else
Continue;
Break;
end;
Assert(PrevDirection <> 0);
for I := 1 to High(Points) do
begin
X2 := Points[I].X;
Y2 := Points[I].Y;
if SubSampleX then
X2 := X2 shl 8;
if Y1 <> Y2 then
begin
DrawEdge(X1, Y1, X2, Y2);
if Y2 > Y1 then
Direction := 1 // up
else
Direction := -1; // down
if Direction <> PrevDirection then
begin
AddEdgePoint(X1, Y1);
PrevDirection := Direction;
end;
end;
X1 := X2;
Y1 := Y2;
end;
X2 := Points[0].X;
Y2 := Points[0].Y;
if SubSampleX then
X2 := X2 shl 8;
if Y1 <> Y2 then
begin
DrawEdge(X1, Y1, X2, Y2);
if Y2 > Y1 then
Direction := 1
else
Direction := -1;
if Direction <> PrevDirection then
AddEdgePoint(X1, Y1);
end;
end;
// Gamma table support for opacities
procedure SetGamma(Gamma: Single);
var
I: Integer;
begin
for I := Low(GAMMA_TABLE) to High(GAMMA_TABLE) do
GAMMA_TABLE[I] := Round(255 * Power(I / 255, Gamma));
end;
// modify Jan 28, 2001 for use under BCB5
// the compiler show error 245 "language feature ist not available"
// we must take a record and under this we can use the static array
procedure SetIdentityMatrix;
begin
IdentityMatrix.A[0, 0] := 1.0;
IdentityMatrix.A[0, 1] := 0.0;
IdentityMatrix.A[0, 2] := 0.0;
IdentityMatrix.A[1, 0] := 0.0;
IdentityMatrix.A[1, 1] := 1.0;
IdentityMatrix.A[1, 2] := 0.0;
IdentityMatrix.A[2, 0] := 0.0;
IdentityMatrix.A[2, 1] := 0.0;
IdentityMatrix.A[2, 2] := 1.0;
end;
initialization
SetIdentityMatrix;
SetGamma(0.7);
// History:
// Revision 1.18 2004/11/14 06:05:05 rrossmair
// - some source formatting
//
// Revision 1.17 2004/11/06 02:19:45 mthoma
// history cleaning.
//
// Revision 1.16 2004/10/17 20:54:14 mthoma
// cleaning
//
// Revision 1.15 2004/07/28 07:40:41 marquardt
// remove comiler warnings
//
// Revision 1.14 2004/07/16 03:50:35 rrossmair
// fixed "not accesssible with BCB" warning for TJclRegion.CreateRect
//
// Revision 1.13 2004/07/15 05:15:41 rrossmair
// TJclRegion: Handle ownership management added, some refactoring
//
// Revision 1.12 2004/07/12 02:54:33 rrossmair
// TJclRegion.Create fixed
//
// Revision 1.11 2004/06/14 13:05:19 marquardt
// style cleaning ENDIF, Tabs
//
// Revision 1.10 2004/05/14 15:20:44 rrossmair
// added Marcin Wieczorek to Contributors list
//
// Revision 1.9 2004/05/05 22:16:40 rrossmair
// header updated according to new policy: initial developers & contributors listed
//
// Revision 1.8 2004/04/18 06:32:07 rrossmair
// replaced symbol "Develop" by jpp-pre-undefined "PROTOTYPE"; protected CVS key words by "PROTOTYPE" symbol
//
// Revision 1.7 2004/04/08 19:44:30 mthoma
// Fixed 0001513: CheckParams at the beginning of ApplyLut is: CheckParams(Src, Dst) but should be CheckParams(Dst, Src)
//
// Revision 1.6 2004/04/06 05:01:54
// adapt compiler conditions, add log entry
end.