Componentes.Terceros.jcl/official/1.96/source/vcl/JclGraphics.pas

5645 lines
147 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 JclGraphics;
{$I jcl.inc}
interface
uses
Windows,
Classes, SysUtils,
Graphics, JclGraphUtils, Controls,
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
TJclDesktopCanvas = class(TCanvas)
private
FDesktop: HDC;
public
constructor Create;
destructor Destroy; override;
end;
TJclRegion = class;
TJclRegionInfo = class(TObject)
private
FData: Pointer;
FDataSize: Integer;
function GetBox: TRect;
protected
function GetCount: Integer;
function GetRect(index: Integer): TRect;
public
constructor Create(Region: TJclRegion);
destructor Destroy; override;
property Box: TRect read GetBox;
property Rectangles[Index: Integer]: TRect read GetRect;
property Count: Integer read GetCount;
end;
TJclRegion = class(TObject)
private
FHandle: HRGN;
FBoxRect: TRect;
FRegionType: Integer;
FOwnsHandle: Boolean;
procedure CheckHandle;
protected
function GetHandle: HRGN;
function GetBox: TRect;
function GetRegionType: TJclRegionKind;
public
constructor Create(RegionHandle: HRGN; OwnsHandle: Boolean = True);
constructor CreateElliptic(const ARect: TRect); overload;
constructor CreateElliptic(const Top, Left, Bottom, Right: Integer); overload;
constructor CreatePoly(const Points: TDynPointArray; Count: Integer; FillMode: TPolyFillMode);
constructor CreatePolyPolygon(const Points: TDynPointArray; const Vertex: TDynIntegerArray;
Count: Integer; FillMode: TPolyFillMode);
constructor CreateRect(const ARect: TRect; DummyForBCB: Boolean = False); overload;
constructor CreateRect(const Top, Left, Bottom, Right: Integer; DummyForBCB: Byte = 0); overload;
constructor CreateRoundRect(const ARect: TRect; CornerWidth, CornerHeight: Integer); overload;
constructor CreateRoundRect(const Top, Left, Bottom, Right, CornerWidth, CornerHeight: Integer); overload;
constructor CreateBitmap(Bitmap: TBitmap; RegionColor: TColor; RegionBitmapMode: TJclRegionBitmapMode);
constructor CreatePath(Canvas: TCanvas);
constructor CreateRegionInfo(RegionInfo: TJclRegionInfo);
constructor CreateMapWindow(InitialRegion: TJclRegion; hWndFrom, hWndTo: THandle); overload;
constructor CreateMapWindow(InitialRegion: TJclRegion; ControlFrom, ControlTo: TWinControl); overload;
destructor Destroy; override;
procedure Clip(Canvas: TCanvas);
procedure Combine(DestRegion, SrcRegion: TJclRegion; CombineOp: TJclRegionCombineOperator); overload;
procedure Combine(SrcRegion: TJclRegion; CombineOp: TJclRegionCombineOperator); overload;
function Copy: TJclRegion;
function Equals(CompareRegion: TJclRegion): Boolean;
procedure Fill(Canvas: TCanvas);
procedure FillGradient(Canvas: TCanvas; ColorCount: Integer; StartColor, EndColor: TColor; ADirection: TGradientDirection);
procedure Frame(Canvas: TCanvas; FrameWidth, FrameHeight: Integer);
procedure Invert(Canvas: TCanvas);
procedure Offset(X, Y: Integer);
procedure Paint(Canvas: TCanvas);
function PointIn(X, Y: Integer): Boolean; overload;
function PointIn(const Point: TPoint): Boolean; overload;
function RectIn(const ARect: TRect): Boolean; overload;
function RectIn(Top, Left, Bottom, Right: Integer): Boolean; overload;
procedure SetWindow(Window: THandle; Redraw: Boolean);
function GetRegionInfo: TJclRegionInfo;
property Box: TRect read GetBox;
property Handle: HRGN read GetHandle;
property RegionType: TJclRegionKind read GetRegionType;
end;
{ TJclThreadPersistent }
{ TJclThreadPersistent is an ancestor for TJclBitmap32 object. In addition to
TPersistent methods, it provides thread-safe locking and change notification }
TJclThreadPersistent = class(TPersistent)
private
FLock: TRTLCriticalSection;
FLockCount: Integer;
FUpdateCount: Integer;
FOnChanging: TNotifyEvent;
FOnChange: TNotifyEvent;
protected
property LockCount: Integer read FLockCount;
property UpdateCount: Integer read FUpdateCount;
public
constructor Create; virtual;
destructor Destroy; override;
procedure Changing; virtual;
procedure Changed; virtual;
procedure BeginUpdate;
procedure EndUpdate;
procedure Lock;
procedure Unlock;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
{ TJclCustomMap }
{ An ancestor for bitmaps and similar 2D distributions which have width and
height properties }
TJclCustomMap = class(TJclThreadPersistent)
private
FHeight: Integer;
FWidth: Integer;
procedure SetHeight(NewHeight: Integer);
procedure SetWidth(NewWidth: Integer);
public
procedure Delete; virtual;
function Empty: Boolean; virtual;
procedure SetSize(Source: TPersistent); overload;
procedure SetSize(NewWidth, NewHeight: Integer); overload; virtual;
property Height: Integer read FHeight write SetHeight;
property Width: Integer read FWidth write SetWidth;
end;
{ TJclBitmap32 }
{ The TJclBitmap32 class is responsible for storage of a bitmap, as well as for drawing in it }
TJclBitmap32 = class(TJclCustomMap)
private
FBitmapInfo: TBitmapInfo;
FBits: PColor32Array;
FDrawMode: TDrawMode;
FFont: TFont;
FHandle: HBITMAP;
FHDC: HDC;
FMasterAlpha: Byte;
FOuterColor: TColor32; // the value returned when accessing outer areas
FPenColor: TColor32;
FStippleCounter: Single;
FStipplePattern: TArrayOfColor32;
FStippleStep: Single;
FStretchFilter: TStretchFilter;
function GetPixel(X, Y: Integer): TColor32;
function GetPixelS(X, Y: Integer): TColor32;
function GetPixelPtr(X, Y: Integer): PColor32;
function GetScanLine(Y: Integer): PColor32Array;
procedure SetDrawMode(Value: TDrawMode);
procedure SetFont(Value: TFont);
procedure SetMasterAlpha(Value: Byte);
procedure SetPixel(X, Y: Integer; Value: TColor32);
procedure SetPixelS(X, Y: Integer; Value: TColor32);
procedure SetStippleStep(Value: Single);
procedure SetStretchFilter(Value: TStretchFilter);
protected
FontHandle: HFont;
RasterX: Integer;
RasterY: Integer;
RasterXF: Single;
RasterYF: Single;
procedure AssignTo(Dst: TPersistent); override;
function ClipLine(var X0, Y0, X1, Y1: Integer): Boolean;
class function ClipLineF(var X0, Y0, X1, Y1: Single; MinX, MaxX, MinY, MaxY: Single): Boolean;
procedure FontChanged(Sender: TObject);
procedure SET_T256(X, Y: Integer; C: TColor32);
procedure SET_TS256(X, Y: Integer; C: TColor32);
procedure ReadData(Stream: TStream); virtual;
procedure WriteData(Stream: TStream); virtual;
procedure DefineProperties(Filer: TFiler); override;
property StippleCounter: Single read FStippleCounter;
public
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure SetSize(NewWidth, NewHeight: Integer); override;
function Empty: Boolean; override;
procedure Clear; overload;
procedure Clear(FillColor: TColor32); overload;
procedure Delete; override;
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SaveToFile(const FileName: string);
procedure ResetAlpha;
procedure Draw(DstX, DstY: Integer; Src: TJclBitmap32); overload;
procedure Draw(DstRect, SrcRect: TRect; Src: TJclBitmap32); overload;
procedure Draw(DstRect, SrcRect: TRect; hSrc: HDC); overload;
procedure DrawTo(Dst: TJclBitmap32); overload;
procedure DrawTo(Dst: TJclBitmap32; DstX, DstY: Integer); overload;
procedure DrawTo(Dst: TJclBitmap32; DstRect: TRect); overload;
procedure DrawTo(Dst: TJclBitmap32; DstRect, SrcRect: TRect); overload;
procedure DrawTo(hDst: HDC; DstX, DstY: Integer); overload;
procedure DrawTo(hDst: HDC; DstRect, SrcRect: TRect); overload;
function GetPixelB(X, Y: Integer): TColor32;
procedure SetPixelT(X, Y: Integer; Value: TColor32); overload;
procedure SetPixelT(var Ptr: PColor32; Value: TColor32); overload;
procedure SetPixelTS(X, Y: Integer; Value: TColor32);
procedure SetPixelF(X, Y: Single; Value: TColor32);
procedure SetPixelFS(X, Y: Single; Value: TColor32);
procedure SetStipple(NewStipple: TArrayOfColor32); overload;
procedure SetStipple(NewStipple: array of TColor32); overload;
procedure ResetStippleCounter;
function GetStippleColor: TColor32;
procedure DrawHorzLine(X1, Y, X2: Integer; Value: TColor32);
procedure DrawHorzLineS(X1, Y, X2: Integer; Value: TColor32);
procedure DrawHorzLineT(X1, Y, X2: Integer; Value: TColor32);
procedure DrawHorzLineTS(X1, Y, X2: Integer; Value: TColor32);
procedure DrawHorzLineTSP(X1, Y, X2: Integer);
procedure DrawVertLine(X, Y1, Y2: Integer; Value: TColor32);
procedure DrawVertLineS(X, Y1, Y2: Integer; Value: TColor32);
procedure DrawVertLineT(X, Y1, Y2: Integer; Value: TColor32);
procedure DrawVertLineTS(X, Y1, Y2: Integer; Value: TColor32);
procedure DrawVertLineTSP(X, Y1, Y2: Integer);
procedure DrawLine(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineT(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineTS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineA(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineAS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean = False);
procedure DrawLineF(X1, Y1, X2, Y2: Single; Value: TColor32; L: Boolean = False);
procedure DrawLineFS(X1, Y1, X2, Y2: Single; Value: TColor32; L: Boolean = False);
procedure DrawLineFP(X1, Y1, X2, Y2: Single; L: Boolean = False);
procedure DrawLineFSP(X1, Y1, X2, Y2: Single; L: Boolean = False);
procedure MoveTo(X, Y: Integer);
procedure LineToS(X, Y: Integer);
procedure LineToTS(X, Y: Integer);
procedure LineToAS(X, Y: Integer);
procedure MoveToF(X, Y: Single);
procedure LineToFS(X, Y: Single);
procedure FillRect(X1, Y1, X2, Y2: Integer; Value: TColor32);
procedure FillRectS(X1, Y1, X2, Y2: Integer; Value: TColor32);
procedure FillRectT(X1, Y1, X2, Y2: Integer; Value: TColor32);
procedure FillRectTS(X1, Y1, X2, Y2: Integer; Value: TColor32);
procedure FrameRectS(X1, Y1, X2, Y2: Integer; Value: TColor32);
procedure FrameRectTS(X1, Y1, X2, Y2: Integer; Value: TColor32); overload;
procedure FrameRectTSP(X1, Y1, X2, Y2: Integer); overload;
procedure RaiseRectTS(X1, Y1, X2, Y2: Integer; Contrast: Integer);
procedure UpdateFont;
procedure TextOut(X, Y: Integer; const Text: string); overload;
procedure TextOut(X, Y: Integer; const ClipRect: TRect; const Text: string); overload;
procedure TextOut(ClipRect: TRect; const Flags: Cardinal; const Text: string); overload;
function TextExtent(const Text: string): TSize;
function TextHeight(const Text: string): Integer;
function TextWidth(const Text: string): Integer;
procedure RenderText(X, Y: Integer; const Text: string; AALevel: Integer; Color: TColor32);
property BitmapHandle: HBITMAP read FHandle;
property BitmapInfo: TBitmapInfo read FBitmapInfo;
property Bits: PColor32Array read FBits;
property Font: TFont read FFont write SetFont;
property Handle: HDC read FHDC;
property PenColor: TColor32 read FPenColor write FPenColor;
property Pixel[X, Y: Integer]: TColor32 read GetPixel write SetPixel; default;
property PixelS[X, Y: Integer]: TColor32 read GetPixelS write SetPixelS;
property PixelPtr[X, Y: Integer]: PColor32 read GetPixelPtr;
property ScanLine[Y: Integer]: PColor32Array read GetScanLine;
property StippleStep: Single read FStippleStep write SetStippleStep;
published
property DrawMode: TDrawMode read FDrawMode write SetDrawMode default dmOpaque;
property MasterAlpha: Byte read FMasterAlpha write SetMasterAlpha default $FF;
property OuterColor: TColor32 read FOuterColor write FOuterColor default 0;
property StretchFilter: TStretchFilter read FStretchFilter write SetStretchFilter default sfNearest;
property OnChanging;
property OnChange;
end;
TJclByteMap = class(TJclCustomMap)
private
FBytes: TDynByteArray;
FHeight: Integer;
FWidth: Integer;
function GetValue(X, Y: Integer): Byte;
function GetValPtr(X, Y: Integer): PByte;
procedure SetValue(X, Y: Integer; Value: Byte);
protected
procedure AssignTo(Dst: TPersistent); override;
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function Empty: Boolean; override;
procedure Clear(FillValue: Byte);
procedure ReadFrom(Source: TJclBitmap32; Conversion: TConversionKind);
procedure SetSize(NewWidth, NewHeight: Integer); override;
procedure WriteTo(Dest: TJclBitmap32; Conversion: TConversionKind); overload;
procedure WriteTo(Dest: TJclBitmap32; const Palette: TPalette32); overload;
property Bytes: TDynByteArray read FBytes;
property ValPtr[X, Y: Integer]: PByte read GetValPtr;
property Value[X, Y: Integer]: Byte read GetValue write SetValue; default;
end;
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;
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;
procedure BitmapToJPeg(const FileName: string);
procedure JPegToBitmap(const FileName: string);
procedure SaveIconToFile(Icon: HICON; const FileName: string);
procedure WriteIcon(Stream: TStream; ColorBitmap, MaskBitmap: HBITMAP;
WriteLength: Boolean = False); overload;
procedure WriteIcon(Stream: TStream; Icon: HICON; WriteLength: Boolean = False); overload;
procedure GetIconFromBitmap(Icon: TIcon; Bitmap: TBitmap);
function GetAntialiasedBitmap(const Bitmap: TBitmap): TBitmap;
procedure BlockTransfer(Dst: TJclBitmap32; DstX: Integer; DstY: Integer; Src: TJclBitmap32;
SrcRect: TRect; CombineOp: TDrawMode);
procedure StretchTransfer(Dst: TJclBitmap32; DstRect: TRect; Src: TJclBitmap32; SrcRect: TRect;
StretchFilter: TStretchFilter; CombineOp: TDrawMode);
procedure Transform(Dst, Src: TJclBitmap32; SrcRect: TRect; Transformation: TJclTransformation);
procedure SetBorderTransparent(ABitmap: TJclBitmap32; ARect: TRect);
function FillGradient(DC: HDC; ARect: TRect; ColorCount: Integer;
StartColor, EndColor: TColor; ADirection: TGradientDirection): Boolean; overload;
function CreateRegionFromBitmap(Bitmap: TBitmap; RegionColor: TColor;
RegionBitmapMode: TJclRegionBitmapMode): HRGN;
procedure ScreenShot(bm: TBitmap; Left, Top, Width, Height: Integer; Window: THandle = HWND_DESKTOP); overload;
procedure ScreenShot(bm: TBitmap; IncludeTaskBar: Boolean = True); overload;
function MapWindowRect(hWndFrom, hWndTo: THandle; ARect: TRect):TRect;
// PolyLines and Polygons
procedure PolyLineTS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
procedure PolyLineAS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
procedure PolyLineFS(Bitmap: TJclBitmap32; const Points: TDynPointArrayF; Color: TColor32);
procedure PolygonTS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
procedure PolygonAS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
procedure PolygonFS(Bitmap: TJclBitmap32; const Points: TDynPointArrayF; Color: TColor32);
procedure PolyPolygonTS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArray;
Color: TColor32);
procedure PolyPolygonAS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArray;
Color: TColor32);
procedure PolyPolygonFS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArrayF;
Color: TColor32);
// Filters
procedure AlphaToGrayscale(Dst, Src: TJclBitmap32);
procedure IntensityToAlpha(Dst, Src: TJclBitmap32);
procedure Invert(Dst, Src: TJclBitmap32);
procedure InvertRGB(Dst, Src: TJclBitmap32);
procedure ColorToGrayscale(Dst, Src: TJclBitmap32);
procedure ApplyLUT(Dst, Src: TJclBitmap32; const LUT: TLUT8);
procedure SetGamma(Gamma: Single = 0.7);
implementation
uses
Math,
CommCtrl, ShellApi,
ClipBrd, JPeg, TypInfo,
JclResources,
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;
procedure CheckBitmaps(Dst, Src: TJclBitmap32);
begin
if (Dst = nil) or Dst.Empty then
raise EJclGraphicsError.CreateRes(@RsDestinationBitmapEmpty);
if (Src = nil) or Src.Empty then
raise EJclGraphicsError.CreateRes(@RsSourceBitmapEmpty);
end;
function CheckSrcRect(Src: TJclBitmap32; const SrcRect: TRect): Boolean;
begin
Result := False;
if IsRectEmpty(SrcRect) then
Exit;
if (SrcRect.Left < 0) or (SrcRect.Right > Src.Width) or
(SrcRect.Top < 0) or (SrcRect.Bottom > Src.Height) then
raise EJclGraphicsError.CreateRes(@RsSourceBitmapInvalid);
Result := True;
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;
if not Target.Empty then
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;
procedure StretchNearest(Dst: TJclBitmap32; DstRect: TRect;
Src: TJclBitmap32; SrcRect: TRect; CombineOp: TDrawMode);
var
SrcW, SrcH, DstW, DstH: Integer;
MapX, MapY: array of Integer;
DstX, DstY: Integer;
R: TRect;
I, J, Y: Integer;
P: PColor32;
MstrAlpha: TColor32;
begin
// check source and destination
CheckBitmaps(Dst, Src);
if not CheckSrcRect(Src, SrcRect) then
Exit;
if IsRectEmpty(DstRect) then
Exit;
IntersectRect(R, DstRect, Rect(0, 0, Dst.Width, Dst.Height));
if IsRectEmpty(R) then
Exit;
if (CombineOp = dmBlend) and (Src.MasterAlpha = 0) then
Exit;
SrcW := SrcRect.Right - SrcRect.Left;
SrcH := SrcRect.Bottom - SrcRect.Top;
DstW := DstRect.Right - DstRect.Left;
DstH := DstRect.Bottom - DstRect.Top;
DstX := DstRect.Left;
DstY := DstRect.Top;
// check if we actually have to stretch anything
if (SrcW = DstW) and (SrcH = DstH) then
begin
BlockTransfer(Dst, DstX, DstY, Src, SrcRect, CombineOp);
Exit;
end;
// build X coord mapping table
SetLength(MapX, DstW);
SetLength(MapY, DstH);
try
for I := 0 to DstW - 1 do
MapX[I] := I * (SrcW) div (DstW) + SrcRect.Left;
// build Y coord mapping table
for J := 0 to DstH - 1 do
MapY[J] := J * (SrcH) div (DstH) + SrcRect.Top;
// transfer pixels
case CombineOp of
dmOpaque:
for J := R.Top to R.Bottom - 1 do
begin
Y := MapY[J - DstY];
P := Dst.PixelPtr[R.Left, J];
for I := R.Left to R.Right - 1 do
begin
P^ := Src[MapX[I - DstX], Y];
Inc(P);
end;
end;
dmBlend:
begin
MstrAlpha := Src.MasterAlpha;
if MstrAlpha = 255 then
for J := R.Top to R.Bottom - 1 do
begin
Y := MapY[J - DstY];
P := Dst.PixelPtr[R.Left, J];
for I := R.Left to R.Right - 1 do
begin
BlendMem(Src[MapX[I - DstX], Y], P^);
Inc(P);
end;
end
else // Master Alpha is in [1..254] range
for J := R.Top to R.Bottom - 1 do
begin
Y := MapY[J - DstY];
P := Dst.PixelPtr[R.Left, J];
for I := R.Left to R.Right - 1 do
begin
BlendMemEx(Src[MapX[I - DstX], Y], P^, MstrAlpha);
Inc(P);
end;
end;
end;
end;
finally
EMMS;
MapX := nil;
MapY := nil;
end;
end;
procedure BlockTransfer(Dst: TJclBitmap32; DstX: Integer; DstY: Integer; Src: TJclBitmap32;
SrcRect: TRect; CombineOp: TDrawMode);
var
SrcX, SrcY: Integer;
S, D: TRect;
J, N: Integer;
Ps, Pd: PColor32;
MstrAlpha: TColor32;
begin
CheckBitmaps(Src, Dst);
if CombineOp = dmOpaque then
begin
BitBlt(Dst.Handle, DstX, DstY, SrcRect.Right - SrcRect.Left,
SrcRect.Bottom - SrcRect.Top, Src.Handle, SrcRect.Left, SrcRect.Top,
SRCCOPY);
Exit;
end;
if Src.MasterAlpha = 0 then
Exit;
// clip the rectangles with bitmap boundaries
SrcX := SrcRect.Left;
SrcY := SrcRect.Top;
IntersectRect(S, SrcRect, Rect(0, 0, Src.Width, Src.Height));
OffsetRect(S, DstX - SrcX, DstY - SrcY);
IntersectRect(D, S, Rect(0, 0, Dst.Width, Dst.Height));
if IsRectEmpty(D) then
Exit;
MstrAlpha := Src.MasterAlpha;
N := D.Right - D.Left;
try
if MstrAlpha = 255 then
for J := D.Top to D.Bottom - 1 do
begin
Ps := Src.PixelPtr[D.Left + SrcX - DstX, J + SrcY - DstY];
Pd := Dst.PixelPtr[D.Left, J];
BlendLine(Ps, Pd, N);
end
else
for J := D.Top to D.Bottom - 1 do
begin
Ps := Src.PixelPtr[D.Left + SrcX - DstX, J + SrcY - DstY];
Pd := Dst.PixelPtr[D.Left, J];
BlendLineEx(Ps, Pd, N, MstrAlpha);
end;
finally
EMMS;
end;
end;
procedure StretchTransfer(Dst: TJclBitmap32; DstRect: TRect; Src: TJclBitmap32; SrcRect: TRect;
StretchFilter: TStretchFilter; CombineOp: TDrawMode);
var
SrcW, SrcH, DstW, DstH: Integer;
MapX, MapY: TMappingTable;
DstX, DstY: Integer;
R: TRect;
I, J, X, Y: Integer;
P: PColor32;
ClusterX, ClusterY: TCluster;
C, Wt, Cr, Cg, Cb, Ca: Integer;
MstrAlpha: TColor32;
begin
// make compiler happy
MapX := nil;
MapY := nil;
ClusterX := nil;
ClusterY := nil;
if StretchFilter = sfNearest then
begin
StretchNearest(Dst, DstRect, Src, SrcRect, CombineOp);
Exit;
end;
// check source and destination
CheckBitmaps(Dst, Src);
if not CheckSrcRect(Src, SrcRect) then
Exit;
if IsRectEmpty(DstRect) then
Exit;
IntersectRect(R, DstRect, Rect(0, 0, Dst.Width, Dst.Height));
if IsRectEmpty(R) then
Exit;
if (CombineOp = dmBlend) and (Src.MasterAlpha = 0) then
Exit;
SrcW := SrcRect.Right - SrcRect.Left;
SrcH := SrcRect.Bottom - SrcRect.Top;
DstW := DstRect.Right - DstRect.Left;
DstH := DstRect.Bottom - DstRect.Top;
DstX := DstRect.Left;
DstY := DstRect.Top;
MstrAlpha := Src.MasterAlpha;
// check if we actually have to stretch anything
if (SrcW = DstW) and (SrcH = DstH) then
begin
BlockTransfer(Dst, DstX, DstY, Src, SrcRect, CombineOp);
Exit;
end;
// mapping tables
MapX := BuildMappingTable(DstW, SrcRect.Left, SrcW, StretchFilter);
MapY := BuildMappingTable(DstH, SrcRect.Top, SrcH, StretchFilter);
try
ClusterX := nil;
ClusterY := nil;
if (MapX = nil) or (MapY = nil) then
Exit;
// transfer pixels
for J := R.Top to R.Bottom - 1 do
begin
ClusterY := MapY[J - DstY];
P := Dst.PixelPtr[R.Left, J];
for I := R.Left to R.Right - 1 do
begin
ClusterX := MapX[I - DstX];
// reset color accumulators
Ca := 0;
Cr := 0;
Cg := 0;
Cb := 0;
// now iterate through each cluster
for Y := 0 to High(ClusterY) do
for X := 0 to High(ClusterX) do
begin
C := Src[ClusterX[X].Pos, ClusterY[Y].Pos];
Wt := ClusterX[X].Weight * ClusterY[Y].Weight;
Inc(Ca, C shr 24 * Wt);
Inc(Cr, (C and $00FF0000) shr 16 * Wt);
Inc(Cg, (C and $0000FF00) shr 8 * Wt);
Inc(Cb, (C and $000000FF) * Wt);
end;
Ca := Ca and $00FF0000;
Cr := Cr and $00FF0000;
Cg := Cg and $00FF0000;
Cb := Cb and $00FF0000;
C := (Ca shl 8) or Cr or (Cg shr 8) or (Cb shr 16);
// combine it with the background
case CombineOp of
dmOpaque:
P^ := C;
dmBlend:
BlendMemEx(C, P^, MstrAlpha);
end;
Inc(P);
end;
end;
finally
EMMS;
MapX := nil;
MapY := nil;
end;
end;
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;
{ TODO : remove VCL-dependency by replacing pf24bit by pf32bit }
function GetAntialiasedBitmap(const Bitmap: TBitmap): TBitmap;
var
Antialias: TBitmap;
X, Y: Integer;
Line1, Line2, Line: PJclByteArray;
begin
Assert(Bitmap <> nil);
if Bitmap.PixelFormat <> pf24bit then
Bitmap.PixelFormat := pf24bit;
Antialias := TBitmap.Create;
with Bitmap do
begin
Antialias.PixelFormat := pf24bit;
Antialias.Width := Width div 2;
Antialias.Height := Height div 2;
for Y := 0 to Antialias.Height - 1 do
begin
Line1 := ScanLine[Y * 2];
Line2 := ScanLine[Y * 2 + 1];
Line := Antialias.ScanLine[Y];
for X := 0 to Antialias.Width - 1 do
begin
Line[X * 3] := (Integer(Line1[X * 6]) + Integer(Line2[X * 6]) +
Integer(Line1[X * 6 + 3]) + Integer(Line2[X * 6 + 3])) div 4;
Line[X * 3 + 1] := (Integer(Line1[X * 6 + 1]) + Integer(Line2[X * 6 + 1]) +
Integer(Line1[X * 6 + 3 + 1]) + Integer(Line2[X * 6 + 3 + 1])) div 4;
Line[X * 3 + 2] := (Integer(Line1[X * 6 + 2]) + Integer(Line2[X * 6 + 2]) +
Integer(Line1[X * 6 + 3 + 2]) + Integer(Line2[X * 6 + 3 + 2])) div 4;
end;
end;
end;
Result := Antialias;
end;
procedure JPegToBitmap(const FileName: string);
var
Bitmap: TBitmap;
JPeg: TJPegImage;
begin
Bitmap := nil;
JPeg := nil;
try
JPeg := TJPegImage.Create;
JPeg.LoadFromFile(FileName);
Bitmap := TBitmap.Create;
Bitmap.Assign(JPeg);
Bitmap.SaveToFile(ChangeFileExt(FileName, LoadResString(@RsBitmapExtension)));
finally
FreeAndNil(Bitmap);
FreeAndNil(JPeg);
end;
end;
procedure BitmapToJPeg(const FileName: string);
var
Bitmap: TBitmap;
JPeg: TJPegImage;
begin
Bitmap := nil;
JPeg := nil;
try
Bitmap := TBitmap.Create;
Bitmap.LoadFromFile(FileName);
JPeg := TJPegImage.Create;
JPeg.Assign(Bitmap);
JPeg.SaveToFile(ChangeFileExt(FileName, LoadResString(@RsJpegExtension)));
finally
FreeAndNil(Bitmap);
FreeAndNil(JPeg);
end;
end;
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;
procedure GetIconFromBitmap(Icon: TIcon; Bitmap: TBitmap);
var
IconInfo: TIconInfo;
begin
with TBitmap.Create do
try
Assign(Bitmap);
if not Transparent then
TransparentColor := clNone;
IconInfo.fIcon := True;
IconInfo.hbmMask := MaskHandle;
IconInfo.hbmColor := Handle;
Icon.Handle := CreateIconIndirect(IconInfo);
finally
Free;
end;
end;
const
rc3_Icon = 1;
type
PCursorOrIcon = ^TCursorOrIcon;
TCursorOrIcon = packed record
Reserved: Word;
wType: Word;
Count: Word;
end;
PIconRec = ^TIconRec;
TIconRec = packed record
Width: Byte;
Height: Byte;
Colors: Word;
Reserved1: Word;
Reserved2: Word;
DIBSize: Longint;
DIBOffset: Longint;
end;
procedure WriteIcon(Stream: TStream; ColorBitmap, MaskBitmap: HBITMAP; WriteLength: Boolean = False);
var
MonoInfoSize, ColorInfoSize: DWORD;
MonoBitsSize, ColorBitsSize: DWORD;
MonoInfo, MonoBits, ColorInfo, ColorBits: Pointer;
CI: TCursorOrIcon;
List: TIconRec;
Length: Longint;
begin
FillChar(CI, SizeOf(CI), 0);
FillChar(List, SizeOf(List), 0);
GetDIBSizes(MaskBitmap, MonoInfoSize, MonoBitsSize);
GetDIBSizes(ColorBitmap, ColorInfoSize, ColorBitsSize);
MonoInfo := nil;
MonoBits := nil;
ColorInfo := nil;
ColorBits := nil;
try
MonoInfo := AllocMem(MonoInfoSize);
MonoBits := AllocMem(MonoBitsSize);
ColorInfo := AllocMem(ColorInfoSize);
ColorBits := AllocMem(ColorBitsSize);
GetDIB(MaskBitmap, 0, MonoInfo^, MonoBits^);
GetDIB(ColorBitmap, 0, ColorInfo^, ColorBits^);
if WriteLength then
begin
Length := SizeOf(CI) + SizeOf(List) + ColorInfoSize +
ColorBitsSize + MonoBitsSize;
Stream.Write(Length, SizeOf(Length));
end;
with CI do
begin
CI.wType := RC3_ICON;
CI.Count := 1;
end;
Stream.Write(CI, SizeOf(CI));
with List, PBitmapInfoHeader(ColorInfo)^ do
begin
Width := biWidth;
Height := biHeight;
Colors := biPlanes * biBitCount;
DIBSize := ColorInfoSize + ColorBitsSize + MonoBitsSize;
DIBOffset := SizeOf(CI) + SizeOf(List);
end;
Stream.Write(List, SizeOf(List));
with PBitmapInfoHeader(ColorInfo)^ do
Inc(biHeight, biHeight); { color height includes mono bits }
Stream.Write(ColorInfo^, ColorInfoSize);
Stream.Write(ColorBits^, ColorBitsSize);
Stream.Write(MonoBits^, MonoBitsSize);
finally
FreeMem(ColorInfo, ColorInfoSize);
FreeMem(ColorBits, ColorBitsSize);
FreeMem(MonoInfo, MonoInfoSize);
FreeMem(MonoBits, MonoBitsSize);
end;
end;
// WriteIcon depends on unit Graphics by use of GetDIBSizes and GetDIB
procedure WriteIcon(Stream: TStream; Icon: HICON; WriteLength: Boolean = False);
var
IconInfo: TIconInfo;
begin
if GetIconInfo(Icon, IconInfo) then
try
WriteIcon(Stream, IconInfo.hbmColor, IconInfo.hbmMask, WriteLength);
finally
DeleteObject(IconInfo.hbmColor);
DeleteObject(IconInfo.hbmMask);
end
else
RaiseLastOSError;
end;
procedure SaveIconToFile(Icon: HICON; const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
WriteIcon(Stream, Icon, False);
finally
Stream.Free;
end;
end;
procedure Transform(Dst, Src: TJclBitmap32; SrcRect: TRect;
Transformation: TJclTransformation);
var
SrcBlend: Boolean;
C, SrcAlpha: TColor32;
R, DstRect: TRect;
Pixels: PColor32Array;
I, J, X, Y: Integer;
function GET_S256(X, Y: Integer; out C: TColor32): Boolean;
var
flrx, flry, celx, cely: Longword;
C1, C2, C3, C4: TColor32;
P: PColor32;
begin
flrx := X and $FF;
flry := Y and $FF;
X := Sar(X,8);
Y := Sar(Y,8);
celx := flrx xor 255;
cely := flry xor 255;
if (X >= SrcRect.Left) and (X < SrcRect.Right - 1) and
(Y >= SrcRect.Top) and (Y < SrcRect.Bottom - 1) then
begin
// everything is ok take the four values and interpolate them
P := Src.PixelPtr[X, Y];
C1 := P^;
Inc(P);
C2 := P^;
Inc(P, Src.Width);
C4 := P^;
Dec(P);
C3 := P^;
C := CombineReg(CombineReg(C1, C2, celx), CombineReg(C3, C4, celx), cely);
Result := True;
end
else
begin
// (X,Y) coordinate is out of the SrcRect, do not interpolate
C := 0; // just write something to disable compiler warnings
Result := False;
end;
end;
begin
SrcBlend := (Src.DrawMode = dmBlend);
SrcAlpha := Src.MasterAlpha; // store it into a local variable
// clip SrcRect
R := SrcRect;
IntersectRect(SrcRect, R, Rect(0, 0, Src.Width, Src.Height));
if IsRectEmpty(SrcRect) then
Exit;
// clip DstRect
R := Transformation.GetTransformedBounds(SrcRect);
IntersectRect(DstRect, R, Rect(0, 0, Dst.Width, Dst.Height));
if IsRectEmpty(DstRect) then
Exit;
try
if Src.StretchFilter <> sfNearest then
for J := DstRect.Top to DstRect.Bottom - 1 do
begin
Pixels := Dst.ScanLine[J];
for I := DstRect.Left to DstRect.Right - 1 do
begin
Transformation.Transform256(I, J, X, Y);
if GET_S256(X, Y, C) then
if SrcBlend then
BlendMemEx(C, Pixels[I], SrcAlpha)
else
Pixels[I] := C;
end;
end
else // nearest filter
for J := DstRect.Top to DstRect.Bottom - 1 do
begin
Pixels := Dst.ScanLine[J];
for I := DstRect.Left to DstRect.Right - 1 do
begin
Transformation.Transform(I, J, X, Y);
if (X >= SrcRect.Left) and (X < SrcRect.Right) and
(Y >= SrcRect.Top) and (Y < SrcRect.Bottom) then
begin
if SrcBlend then
BlendMemEx(Src.Pixel[X, Y], Pixels[I], SrcAlpha)
else
Pixels[I] := Src.Pixel[X, Y];
end;
end;
end;
finally
EMMS;
end;
Dst.Changed;
end;
procedure SetBorderTransparent(ABitmap: TJclBitmap32; ARect: TRect);
var
I: Integer;
begin
if TestClip(ARect.Left, ARect.Right, ABitmap.Width) and
TestClip(ARect.Top, ARect.Bottom, ABitmap.Height) then
begin
ABitmap.Changing;
for I := ARect.Left to ARect.Right do
ABitmap[I, ARect.Top] := ABitmap[I, ARect.Top] and $00FFFFFF;
for I := ARect.Left to ARect.Right do
ABitmap[I, ARect.Bottom] := ABitmap[I, ARect.Bottom] and $00FFFFFF;
if ARect.Bottom > ARect.Top + 1 then
for I := ARect.Top + 1 to ARect.Bottom - 1 do
begin
ABitmap[ARect.Left, I] := ABitmap[ARect.Left, I] and $00FFFFFF;
ABitmap[ARect.Right, I] := ABitmap[ARect.Right, I] and $00FFFFFF;
end;
ABitmap.Changed;
end;
end;
function CreateRegionFromBitmap(Bitmap: TBitmap; RegionColor: TColor;
RegionBitmapMode: TJclRegionBitmapMode): HRGN;
var
FBitmap: TBitmap;
X, Y: Integer;
StartX: Integer;
Region: HRGN;
begin
Result := 0;
if Bitmap = nil then
EJclGraphicsError.CreateRes(@RsNoBitmapForRegion);
if (Bitmap.Width = 0) or (Bitmap.Height = 0) then
Exit;
FBitmap := TBitmap.Create;
try
FBitmap.Assign(Bitmap);
for Y := 0 to FBitmap.Height - 1 do
begin
X := 0;
while X < FBitmap.Width do
begin
if RegionBitmapMode = rmExclude then
begin
while FBitmap.Canvas.Pixels[X,Y] = RegionColor do
begin
Inc(X);
if X = FBitmap.Width then
Break;
end;
end
else
begin
while FBitmap.Canvas.Pixels[X,Y] <> RegionColor do
begin
Inc(X);
if X = FBitmap.Width then
Break;
end;
end;
if X = FBitmap.Width then
Break;
StartX := X;
if RegionBitmapMode = rmExclude then
begin
while FBitmap.Canvas.Pixels[X,Y] <> RegionColor do
begin
if X = FBitmap.Width then
Break;
Inc(X);
end;
end
else
begin
while FBitmap.Canvas.Pixels[X,Y] = RegionColor do
begin
if X = FBitmap.Width then
Break;
Inc(X);
end;
end;
if Result = 0 then
Result := CreateRectRgn(StartX, Y, X, Y + 1)
else
begin
Region := CreateRectRgn(StartX, Y, X, Y + 1);
if Region <> 0 then
begin
CombineRgn(Result, Result, Region, RGN_OR);
DeleteObject(Region);
end;
end;
end;
end;
finally
FBitmap.Free;
end;
end;
procedure ScreenShot(bm: TBitmap; Left, Top, Width, Height: Integer; Window: THandle); overload;
var
WinDC: HDC;
Pal: TMaxLogPalette;
begin
bm.Width := Width;
bm.Height := Height;
// Get the HDC of the window...
WinDC := GetDC(Window);
if WinDC = 0 then
raise EJclGraphicsError.CreateRes(@RsNoDeviceContextForWindow);
// Palette-device?
if (GetDeviceCaps(WinDC, RASTERCAPS) and RC_PALETTE) = RC_PALETTE then
begin
FillChar(Pal, SizeOf(TMaxLogPalette), #0); // fill the structure with zeros
Pal.palVersion := $300; // fill in the palette version
// grab the system palette entries...
Pal.palNumEntries := GetSystemPaletteEntries(WinDC, 0, 256, Pal.palPalEntry);
if Pal.PalNumEntries <> 0 then
bm.Palette := CreatePalette(PLogPalette(@Pal)^);
end;
// copy from the screen to our bitmap...
BitBlt(bm.Canvas.Handle, 0, 0, Width, Height, WinDC, Left, Top, SRCCOPY);
ReleaseDC(Window, WinDC); // finally, relase the DC of the window
end;
procedure ScreenShot(bm: TBitmap; IncludeTaskBar: Boolean = True); overload;
var
R: TRect;
begin
if IncludeTaskBar then
begin
R.Left := 0;
R.Top := 0;
R.Right := GetSystemMetrics(SM_CXSCREEN);
R.Bottom := GetSystemMetrics(SM_CYSCREEN);
end
else
SystemParametersInfo(SPI_GETWORKAREA, 0, @R, 0);
ScreenShot(bm, R.Left, R.Top, R.Right, R.Bottom, HWND_DESKTOP);
end;
function MapWindowRect(hWndFrom, hWndTo: THandle; ARect:TRect):TRect;
begin
MapWindowPoints(hWndFrom, hWndTo, ARect, 2);
Result := ARect;
end;
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;
//=== { TJclDesktopCanvas } ==================================================
constructor TJclDesktopCanvas.Create;
begin
inherited Create;
FDesktop := GetDC(HWND_DESKTOP);
Handle := FDesktop;
end;
destructor TJclDesktopCanvas.Destroy;
begin
Handle := 0;
ReleaseDC(HWND_DESKTOP, FDesktop);
inherited Destroy;
end;
//=== { TJclRegionInfo } =====================================================
constructor TJclRegionInfo.Create(Region: TJclRegion);
begin
inherited Create;
if Region = nil then
raise EJclGraphicsError.CreateRes(@RsInvalidRegion);
FData := nil;
FDataSize := GetRegionData(Region.Handle, 0, nil);
GetMem(FData, FDataSize);
GetRegionData(Region.Handle, FDataSize, FData);
end;
destructor TJclRegionInfo.Destroy;
begin
if FData <> nil then
FreeMem(FData);
inherited Destroy;
end;
function TJclRegionInfo.GetBox: TRect;
begin
Result := RectAssign(TRgnData(FData^).rdh.rcBound.Left, TRgnData(FData^).rdh.rcBound.Top,
TRgnData(FData^).rdh.rcBound.Right, TRgnData(FData^).rdh.rcBound.Bottom);
end;
function TJclRegionInfo.GetCount: Integer;
begin
Result := TRgnData(FData^).rdh.nCount;
end;
function TJclRegionInfo.GetRect(Index: Integer): TRect;
var RectP: PRect;
begin
if (Index < 0) or (DWORD(Index) >= TRgnData(FData^).rdh.nCount) then
raise EJclGraphicsError.CreateRes(@RsRegionDataOutOfBound);
RectP := PRect(PChar(@TRgnData(FData^).Buffer) + (SizeOf(TRect)*Index));
Result := RectAssign(RectP^.Left, RectP.Top, RectP^.Right, RectP^.Bottom);
end;
//=== { TJclRegion } =========================================================
constructor TJclRegion.Create(RegionHandle: HRGN; OwnsHandle: Boolean = True);
begin
inherited Create;
FHandle := RegionHandle;
FOwnsHandle := OwnsHandle;
CheckHandle;
GetBox;
end;
constructor TJclRegion.CreateBitmap(Bitmap: TBitmap; RegionColor: TColor;
RegionBitmapMode: TJclRegionBitmapMode);
begin
Create(CreateRegionFromBitmap(Bitmap, RegionColor, RegionBitmapMode), True);
end;
constructor TJclRegion.CreateElliptic(const ARect: TRect);
begin
Create(CreateEllipticRgnIndirect(ARect), True);
end;
constructor TJclRegion.CreateElliptic(const Top, Left, Bottom, Right: Integer);
begin
Create(CreateEllipticRgn(Top, Left, Bottom, Right), True);
end;
constructor TJclRegion.CreatePoly(const Points: TDynPointArray; Count: Integer;
FillMode: TPolyFillMode);
begin
case FillMode of
fmAlternate:
Create(CreatePolygonRgn(Points, Count, ALTERNATE), True);
fmWinding:
Create(CreatePolygonRgn(Points, Count, WINDING), True);
end;
end;
constructor TJclRegion.CreatePolyPolygon(const Points: TDynPointArray;
const Vertex: TDynIntegerArray; Count: Integer; FillMode: TPolyFillMode);
begin
case FillMode of
fmAlternate:
Create(CreatePolyPolygonRgn(Points, Vertex, Count, ALTERNATE), True);
fmWinding:
Create(CreatePolyPolygonRgn(Points, Vertex, Count, WINDING), True);
end;
end;
constructor TJclRegion.CreateRect(const ARect: TRect; DummyForBCB: Boolean = False);
begin
Create(CreateRectRgnIndirect(ARect), True);
end;
constructor TJclRegion.CreateRect(const Top, Left, Bottom, Right: Integer; DummyForBCB: Byte = 0);
begin
Create(CreateRectRgn(Top, Left, Bottom, Right), True);
end;
constructor TJclRegion.CreateRoundRect(const ARect: TRect; CornerWidth,
CornerHeight: Integer);
begin
Create(CreateRoundRectRgn(ARect.Top, ARect.Left, ARect.Bottom, ARect.Right,
CornerWidth, CornerHeight), True);
end;
constructor TJclRegion.CreateRoundRect(const Top, Left, Bottom, Right, CornerWidth,
CornerHeight: Integer);
begin
Create(CreateRoundRectRgn(Top, Left, Bottom, Right, CornerWidth, CornerHeight), True);
end;
constructor TJclRegion.CreatePath(Canvas: TCanvas);
begin
Create(PathToRegion(Canvas.Handle), True);
end;
constructor TJclRegion.CreateRegionInfo(RegionInfo: TJclRegionInfo);
begin
if RegionInfo = nil then
raise EJclGraphicsError.CreateRes(@RsInvalidRegionInfo);
Create(ExtCreateRegion(nil,RegionInfo.FDataSize,TRgnData(RegionInfo.FData^)), True);
end;
constructor TJclRegion.CreateMapWindow(InitialRegion: TJclRegion; hWndFrom, hWndTo: THandle);
var
RectRegion: HRGN;
CurrentRegionInfo : TJclRegionInfo;
SimpleRect: TRect;
Index:integer;
begin
Create(CreateRectRgn(0, 0, 0, 0), True);
if (hWndFrom <> 0) or (hWndTo <> 0 ) then
begin
CurrentRegionInfo := InitialRegion.GetRegionInfo;
try
for Index := 0 to CurrentRegionInfo.Count-1 do
begin
SimpleRect := CurrentRegionInfo.Rectangles[Index];
SimpleRect := MapWindowRect(hWndFrom,hWndTo,SimpleRect);
RectRegion := CreateRectRgnIndirect(SimpleRect);
if RectRegion <> 0 then
begin
CombineRgn(Handle, Handle, RectRegion, RGN_OR);
DeleteObject(RectRegion);
end;
end;
finally
CurrentRegionInfo.Free;
GetBox;
end;
end;
end;
constructor TJclRegion.CreateMapWindow(InitialRegion: TJclRegion;
ControlFrom, ControlTo: TWinControl);
begin
CreateMapWindow(InitialRegion,ControlFrom.Handle,ControlTo.Handle);
end;
destructor TJclRegion.Destroy;
begin
if FOwnsHandle and (FHandle <> 0) then
DeleteObject(FHandle);
inherited Destroy;
end;
procedure TJclRegion.CheckHandle;
begin
if FHandle = 0 then
begin
if FOwnsHandle then
raise EJclWin32Error.CreateRes(@RsRegionCouldNotCreated)
else
raise EJclGraphicsError.CreateRes(@RsInvalidHandleForRegion);
end;
end;
procedure TJclRegion.Combine(DestRegion, SrcRegion: TJclRegion;
CombineOp: TJclRegionCombineOperator);
begin
case CombineOp of
coAnd:
FRegionType := CombineRgn(DestRegion.Handle, SrcRegion.Handle, FHandle, RGN_AND);
coOr:
FRegionType := CombineRgn(DestRegion.Handle, SrcRegion.Handle, FHandle, RGN_OR);
coDiff:
FRegionType := CombineRgn(DestRegion.Handle, SrcRegion.Handle, FHandle, RGN_DIFF);
coXor:
FRegionType := CombineRgn(DestRegion.Handle, SrcRegion.Handle, FHandle, RGN_XOR);
end;
end;
procedure TJclRegion.Combine(SrcRegion: TJclRegion; CombineOp: TJclRegionCombineOperator);
begin
case CombineOp of
coAnd:
FRegionType := CombineRgn(FHandle, SrcRegion.Handle, FHandle, RGN_AND);
coOr:
FRegionType := CombineRgn(FHandle, SrcRegion.Handle, FHandle, RGN_OR);
coDiff:
FRegionType := CombineRgn(FHandle, SrcRegion.Handle, FHandle, RGN_DIFF);
coXor:
FRegionType := CombineRgn(FHandle, SrcRegion.Handle, FHandle, RGN_XOR);
end;
end;
procedure TJclRegion.Clip(Canvas: TCanvas);
begin
FRegionType := SelectClipRgn(Canvas.Handle, FHandle);
end;
function TJclRegion.Equals(CompareRegion: TJclRegion): Boolean;
begin
Result := EqualRgn(CompareRegion.Handle, FHandle);
end;
function TJclRegion.GetHandle: HRGN;
begin
Result := FHandle;
end;
procedure TJclRegion.Fill(Canvas: TCanvas);
begin
FillRgn(Canvas.Handle, FHandle, Canvas.Brush.Handle);
end;
procedure TJclRegion.FillGradient(Canvas: TCanvas; ColorCount: Integer;
StartColor, EndColor: TColor; ADirection: TGradientDirection);
begin
SelectClipRgn(Canvas.Handle,FHandle);
JclGraphics.FillGradient(Canvas.Handle, Box, ColorCount, StartColor, EndColor, ADirection);
end;
procedure TJclRegion.Frame(Canvas: TCanvas; FrameWidth, FrameHeight: Integer);
begin
FrameRgn(Canvas.Handle, FHandle, Canvas.Brush.Handle, FrameWidth, FrameHeight);
end;
function TJclRegion.GetBox: TRect;
begin
FRegionType := GetRgnBox(FHandle, FBoxRect);
Result := FBoxRect;
end;
function TJclRegion.GetRegionType: TJclRegionKind;
begin
case FRegionType of
NULLREGION:
Result := rkNull;
SIMPLEREGION:
Result := rkSimple;
COMPLEXREGION:
Result := rkComplex;
else
Result := rkError;
end;
end;
procedure TJclRegion.Invert(Canvas: TCanvas);
begin
InvertRgn(Canvas.Handle, FHandle);
end;
procedure TJclRegion.Offset(X, Y: Integer);
begin
FRegionType := OffsetRgn(FHandle, X, Y);
end;
procedure TJclRegion.Paint(Canvas: TCanvas);
begin
PaintRgn(Canvas.Handle, FHandle);
end;
function TJclRegion.PointIn(X, Y: Integer): Boolean;
begin
Result := PtInRegion(FHandle, X, Y);
end;
function TJclRegion.PointIn(const Point: TPoint): Boolean;
begin
Result := PtInRegion(FHandle, Point.X, Point.Y);
end;
function TJclRegion.RectIn(const ARect: TRect): Boolean;
begin
Result := RectInRegion(FHandle, ARect);
end;
function TJclRegion.RectIn(Top, Left, Bottom, Right: Integer): Boolean;
begin
Result := RectInRegion(FHandle, RectAssign(Left, Top, Right, Bottom));
end;
{ Documentation Info (from MSDN): After a successful call to SetWindowRgn, the system owns
the region specified by the region handle hRgn. The system does
not make a copy of the region. Thus, you should not make any
further function calls with this region handle. In particular,
do not delete this region handle. The system deletes the region
handle when it no longer needed. }
procedure TJclRegion.SetWindow(Window: THandle; Redraw: Boolean);
begin
if SetWindowRgn(Window, FHandle, Redraw) <> 0 then
FOwnsHandle := False; // Make sure that we do not release the Handle. If we didn't own it before
// please take care that the owner doesn't release it.
end;
function TJclRegion.Copy: TJclRegion;
begin
Result := TJclRegion.CreateRect(0, 0, 0, 0, 0); // (rom) call correct overloaded constructor for BCB
CombineRgn(Result.Handle, FHandle, 0, RGN_COPY);
Result.GetBox;
end;
function TJclRegion.GetRegionInfo: TJclRegionInfo;
begin
Result := TJclRegionInfo.Create(Self);
end;
//=== { TJclThreadPersistent } ===============================================
constructor TJclThreadPersistent.Create;
begin
inherited Create;
InitializeCriticalSection(FLock);
end;
destructor TJclThreadPersistent.Destroy;
begin
DeleteCriticalSection(FLock);
inherited Destroy;
end;
procedure TJclThreadPersistent.BeginUpdate;
begin
Inc(FUpdateCount);
end;
procedure TJclThreadPersistent.Changing;
begin
if (FUpdateCount = 0) and Assigned(FOnChanging) then
FOnChanging(Self);
end;
procedure TJclThreadPersistent.Changed;
begin
if (FUpdateCount = 0) and Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TJclThreadPersistent.EndUpdate;
begin
Assert(FUpdateCount > 0, LoadResString(@RsAssertUnpairedEndUpdate));
Dec(FUpdateCount);
end;
procedure TJclThreadPersistent.Lock;
begin
InterlockedIncrement(FLockCount);
EnterCriticalSection(FLock);
end;
procedure TJclThreadPersistent.Unlock;
begin
LeaveCriticalSection(FLock);
InterlockedDecrement(FLockCount);
end;
//=== { TJclCustomMap } ======================================================
procedure TJclCustomMap.Delete;
begin
SetSize(0, 0);
end;
function TJclCustomMap.Empty: Boolean;
begin
Result := (Width = 0) or (Height = 0);
end;
procedure TJclCustomMap.SetHeight(NewHeight: Integer);
begin
SetSize(Width, NewHeight);
end;
procedure TJclCustomMap.SetSize(NewWidth, NewHeight: Integer);
begin
FWidth := NewWidth;
FHeight := NewHeight;
end;
procedure TJclCustomMap.SetSize(Source: TPersistent);
var
WidthInfo, HeightInfo: PPropInfo;
begin
if Source is TJclCustomMap then
SetSize(TJclCustomMap(Source).Width, TJclCustomMap(Source).Height)
else
if Source is TGraphic then
SetSize(TGraphic(Source).Width, TGraphic(Source).Height)
else
if Source = nil then
SetSize(0, 0)
else
begin
WidthInfo := GetPropInfo(Source, 'Width', [tkInteger]);
HeightInfo := GetPropInfo(Source, 'Height', [tkInteger]);
if Assigned(WidthInfo) and Assigned(HeightInfo) then
SetSize(GetOrdProp(Source, WidthInfo), GetOrdProp(Source, HeightInfo))
else
raise EJclGraphicsError.CreateResFmt(@RsMapSizeFmt,[Source.ClassName]);
end;
end;
procedure TJclCustomMap.SetWidth(NewWidth: Integer);
begin
SetSize(NewWidth, Height);
end;
//=== { TJclBitmap32 } =======================================================
constructor TJclBitmap32.Create;
begin
inherited Create;
FillChar(FBitmapInfo, SizeOf(TBitmapInfo), #0);
with FBitmapInfo.bmiHeader do
begin
biSize := SizeOf(TBitmapInfoHeader);
biPlanes := 1;
biBitCount := 32;
biCompression := BI_RGB;
end;
FOuterColor := $00000000; // by default as full transparency black
FFont := TFont.Create;
FFont.OnChange := FontChanged;
FFont.OwnerCriticalSection := @FLock;
FMasterAlpha := $FF;
FPenColor := clWhite32;
FStippleStep := 1;
end;
destructor TJclBitmap32.Destroy;
begin
Lock;
try
FFont.Free;
SetSize(0, 0);
finally
Unlock;
end;
inherited Destroy;
end;
procedure TJclBitmap32.SetSize(NewWidth, NewHeight: Integer);
begin
if NewWidth <= 0 then
NewWidth := 0;
if NewHeight <= 0 then
NewHeight := 0;
if (NewWidth = Width) and (NewHeight = Height) then
Exit;
Changing;
try
if FHDC <> 0 then
DeleteDC(FHDC);
if FHandle <> 0 then
DeleteObject(FHandle);
FBits := nil;
FWidth := 0;
FHeight := 0;
if (NewWidth > 0) and (NewHeight > 0) then
begin
with FBitmapInfo.bmiHeader do
begin
biWidth := NewWidth;
biHeight := -NewHeight;
end;
FHandle := CreateDIBSection(0, FBitmapInfo, DIB_RGB_COLORS, Pointer(FBits), 0, 0);
if FBits = nil then
raise EJclGraphicsError.CreateRes(@RsDibHandleAllocation);
FHDC := CreateCompatibleDC(0);
if FHDC = 0 then
begin
DeleteObject(FHandle);
FHandle := 0;
FBits := nil;
raise EJclGraphicsError.CreateRes(@RsCreateCompatibleDc);
end;
if SelectObject(FHDC, FHandle) = 0 then
begin
DeleteDC(FHDC);
DeleteObject(FHandle);
FHDC := 0;
FHandle := 0;
FBits := nil;
raise EJclGraphicsError.CreateRes(@RsSelectObjectInDc);
end;
FWidth := NewWidth;
FHeight := NewHeight;
end;
finally
Changed;
end;
end;
function TJclBitmap32.Empty: Boolean;
begin
Result := (FHandle = 0);
end;
procedure TJclBitmap32.Clear;
begin
Clear(clBlack32);
end;
procedure TJclBitmap32.Clear(FillColor: TColor32);
begin
if Empty then
Exit;
Changing;
FillLongword(Bits[0], Width * Height, FillColor);
Changed;
end;
procedure TJclBitmap32.Delete;
begin
Changing;
SetSize(0, 0);
Changed;
end;
procedure TJclBitmap32.Assign(Source: TPersistent);
var
Canvas: TCanvas;
Picture: TPicture;
procedure AssignFromBitmap(SrcBmp: TBitmap);
begin
SetSize(SrcBmp.Width, SrcBmp.Height);
if Empty then
Exit;
BitBlt(Handle, 0, 0, Width, Height, SrcBmp.Canvas.Handle, 0, 0, SRCCOPY);
ResetAlpha;
end;
begin
Changing;
BeginUpdate;
try
if Source = nil then
begin
SetSize(0, 0);
Exit;
end
else
if Source is TJclBitmap32 then
begin
SetSize(TJclBitmap32(Source).Width, TJclBitmap32(Source).Height);
Move(TJclBitmap32(Source).Bits[0], Bits[0], Width * Height * 4);
Exit;
end
else
if Source is TBitmap then
begin
AssignFromBitmap(TBitmap(Source));
Exit;
end
else
if Source is TPicture then
begin
with TPicture(Source) do
begin
if TPicture(Source).Graphic is TBitmap then
AssignFromBitmap(TBitmap(TPicture(Source).Graphic))
else
begin
// icons, metafiles etc...
SetSize(TPicture(Source).Graphic.Width, TPicture(Source).Graphic.Height);
if Empty then
Exit;
Canvas := TCanvas.Create;
try
Canvas.Handle := Self.Handle;
TJclGraphicAccess(Graphic).Draw(Canvas, Rect(0, 0, Width, Height));
ResetAlpha;
finally
Canvas.Free;
end;
end;
end;
Exit;
end
else
if Source is TClipboard then
begin
Picture := TPicture.Create;
try
Picture.Assign(TClipboard(Source));
SetSize(Picture.Width, Picture.Height);
if Empty then
Exit;
Canvas := TCanvas.Create;
try
Canvas.Handle := Self.Handle;
TJclGraphicAccess(Picture.Graphic).Draw(Canvas, Rect(0, 0, Width, Height));
ResetAlpha;
finally
Canvas.Free;
end;
finally
Picture.Free;
end;
Exit;
end
else
inherited Assign(Source); // default handler
finally;
EndUpdate;
Changed;
end;
end;
procedure TJclBitmap32.AssignTo(Dst: TPersistent);
var
Bmp: TBitmap;
begin
if Dst is TPicture then
begin
Bmp := TPicture(Dst).Bitmap;
Bmp.HandleType := bmDIB;
Bmp.PixelFormat := pf32bit;
Bmp.Width := Width;
Bmp.Height := Height;
DrawTo(Bmp.Canvas.Handle, 0, 0);
end
else
if Dst is TBitmap then
begin
Bmp := TBitmap(Dst);
Bmp.HandleType := bmDIB;
Bmp.PixelFormat := pf32bit;
Bmp.Width := Width;
Bmp.Height := Height;
DrawTo(Bmp.Canvas.Handle, 0, 0);
end
else
if Dst is TClipboard then
begin
Bmp := TBitmap.Create;
try
Bmp.HandleType := bmDIB;
Bmp.PixelFormat := pf32bit;
Bmp.Width := Width;
Bmp.Height := Height;
DrawTo(Bmp.Canvas.Handle, 0, 0);
TClipboard(Dst).Assign(Bmp);
finally
Bmp.Free;
end;
end
else
inherited AssignTo(Dst);
end;
procedure TJclBitmap32.SetPixel(X, Y: Integer; Value: TColor32);
begin
Bits[X + Y * Width] := Value;
end;
procedure TJclBitmap32.SetPixelS(X, Y: Integer; Value: TColor32);
begin
if (X >= 0) and (X < Width) and (Y >= 0) and (Y < Height) then
Bits[X + Y * Width] := Value;
end;
function TJclBitmap32.GetScanLine(Y: Integer): PColor32Array;
begin
Result := @Bits[Y * FWidth];
end;
function TJclBitmap32.GetPixel(X, Y: Integer): TColor32;
begin
Result := Bits[X + Y * Width];
end;
function TJclBitmap32.GetPixelS(X, Y: Integer): TColor32;
begin
if (X >= 0) and (X < Width) and (Y >= 0) and (Y < Height) then
Result := Bits[X + Y * Width]
else
Result := OuterColor;
end;
function TJclBitmap32.GetPixelPtr(X, Y: Integer): PColor32;
begin
Result := @Bits[X + Y * Width];
end;
procedure TJclBitmap32.Draw(DstX, DstY: Integer; Src: TJclBitmap32);
begin
Changing;
if Src <> nil then
Src.DrawTo(Self, DstX, DstY);
Changed;
end;
procedure TJclBitmap32.Draw(DstRect, SrcRect: TRect; Src: TJclBitmap32);
begin
Changing;
if Src <> nil then
Src.DrawTo(Self, DstRect, SrcRect);
Changed;
end;
procedure TJclBitmap32.Draw(DstRect, SrcRect: TRect; hSrc: HDC);
begin
if Empty then
Exit;
Changing;
StretchBlt(Handle, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
DstRect.Bottom - DstRect.Top, hSrc, SrcRect.Left, SrcRect.Top,
SrcRect.Right - SrcRect.Left, SrcRect.Bottom - SrcRect.Top, SRCCOPY);
Changed;
end;
procedure TJclBitmap32.DrawTo(Dst: TJclBitmap32);
begin
if Empty or Dst.Empty then
Exit;
Dst.Changing;
BlockTransfer(Dst, 0, 0, Self, Rect(0, 0, Width, Height), DrawMode);
Dst.Changed;
end;
procedure TJclBitmap32.DrawTo(Dst: TJclBitmap32; DstX, DstY: Integer);
begin
if Empty or Dst.Empty then
Exit;
Dst.Changing;
BlockTransfer(Dst, DstX, DstY, Self, Rect(0, 0, Width, Height), DrawMode);
Dst.Changed;
end;
procedure TJclBitmap32.DrawTo(Dst: TJclBitmap32; DstRect: TRect);
begin
if Empty or Dst.Empty then
Exit;
Dst.Changing;
StretchTransfer(Dst, DstRect, Self, Rect(0, 0, Width, Height), StretchFilter, DrawMode);
Dst.Changed;
end;
procedure TJclBitmap32.DrawTo(Dst: TJclBitmap32; DstRect, SrcRect: TRect);
begin
if Empty or Dst.Empty then
Exit;
Dst.Changing;
StretchTransfer(Dst, DstRect, Self, SrcRect, StretchFilter, DrawMode);
Dst.Changed;
end;
procedure TJclBitmap32.DrawTo(hDst: HDC; DstX, DstY: Integer);
begin
if Empty then
Exit;
BitBlt(hDst, DstX, DstY, Width, Height, Handle, 0, 0, SRCCOPY);
end;
procedure TJclBitmap32.DrawTo(hDst: HDC; DstRect, SrcRect: TRect);
begin
if Empty then
Exit;
StretchDIBits(hDst,
DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left, DstRect.Bottom - DstRect.Top,
SrcRect.Left, SrcRect.Top, SrcRect.Right - SrcRect.Left, SrcRect.Bottom - SrcRect.Top,
Bits, FBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
end;
procedure TJclBitmap32.ResetAlpha;
var
I: Integer;
P: PByte;
begin
Changing;
P := Pointer(FBits);
Inc(P, 3);
for I := 0 to Width * Height - 1 do
begin
P^ := $FF;
Inc(P, 4)
end;
Changed;
end;
function TJclBitmap32.GetPixelB(X, Y: Integer): TColor32;
begin
// this function should never be used on empty bitmaps !!!
if X < 0 then
X := 0
else
if X >= Width then
X := Width - 1;
if Y < 0 then
Y := 0
else
if Y >= Height then
Y := Height - 1;
Result := Bits[X + Y * Width];
end;
procedure TJclBitmap32.SetPixelT(X, Y: Integer; Value: TColor32);
begin
BlendMem(Value, Bits[X + Y * Width]);
EMMS;
end;
procedure TJclBitmap32.SetPixelT(var Ptr: PColor32; Value: TColor32);
begin
BlendMem(Value, Ptr^);
EMMS;
Inc(Ptr);
end;
procedure TJclBitmap32.SetPixelTS(X, Y: Integer; Value: TColor32);
begin
if (X >= 0) and (X < Width) and (Y >= 0) and (Y < Width) then
begin
BlendMem(Value, Bits[X + Y * Width]);
EMMS;
end;
end;
procedure TJclBitmap32.SET_T256(X, Y: Integer; C: TColor32);
var
flrx, flry, celx, cely: Longword;
P: PColor32;
A: TColor32;
begin
A := C shr 24; // opacity
flrx := X and $FF;
flry := Y and $FF;
X := Sar(X,8);
Y := Sar(Y,8);
celx := A * GAMMA_TABLE[flrx xor 255];
cely := GAMMA_TABLE[flry xor 255];
flrx := A * GAMMA_TABLE[flrx];
flry := GAMMA_TABLE[flry];
P := @FBits[X + Y * FWidth];
CombineMem(C, P^, celx * cely shr 16);
Inc(P);
CombineMem(C, P^, flrx * cely shr 16);
Inc(P, FWidth);
CombineMem(C, P^, flrx * flry shr 16);
Dec(P);
CombineMem(C, P^, celx * flry shr 16);
end;
procedure TJclBitmap32.SET_TS256(X, Y: Integer; C: TColor32);
var
flrx, flry, celx, cely: Longword;
P: PColor32;
A: TColor32;
begin
if (X < -256) or (Y < -256) then
Exit;
flrx := X and $FF;
flry := Y and $FF;
X := Sar(X,8);
Y := Sar(Y,8);
if (X >= FWidth) or (Y >= FHeight) then
Exit;
A := C shr 24; // opacity
celx := A * GAMMA_TABLE[flrx xor 255];
cely := GAMMA_TABLE[flry xor 255];
flrx := A * GAMMA_TABLE[flrx];
flry := GAMMA_TABLE[flry];
P := @FBits[X + Y * FWidth];
if (X >= 0) and (Y >= 0) and (X < FWidth - 1) and (Height < FHeight - 1) then
begin
CombineMem(C, P^, celx * cely shr 16);
Inc(P);
CombineMem(C, P^, flrx * cely shr 16);
Inc(P, FWidth);
CombineMem(C, P^, flrx * flry shr 16);
Dec(P);
CombineMem(C, P^, celx * flry shr 16);
end
else
begin
if (X >= 0) and (Y >= 0) then
CombineMem(C, P^, celx * cely shr 16);
Inc(P);
if (X < FWidth - 1) and (Y >= 0) then
CombineMem(C, P^, flrx * cely shr 16);
Inc(P, FWidth);
if (X < FWidth - 1) and (Y < FHeight - 1) then
CombineMem(C, P^, flrx * flry shr 16);
Dec(P);
if (X >= 0) and (Y < FHeight - 1) then
CombineMem(C, P^, celx * flry shr 16);
end;
end;
procedure TJclBitmap32.SetPixelF(X, Y: Single; Value: TColor32);
begin
SET_T256(Round(X * 256), Round(Y * 256), Value);
EMMS;
end;
procedure TJclBitmap32.SetPixelFS(X, Y: Single; Value: TColor32);
begin
SET_TS256(Round(X * 256), Round(Y * 256), Value);
EMMS;
end;
procedure TJclBitmap32.SetStipple(NewStipple: TArrayOfColor32);
begin
FStippleCounter := 0;
FStipplePattern := Copy(NewStipple, 0, Length(NewStipple));
end;
procedure TJclBitmap32.SetStipple(NewStipple: array of TColor32);
var
L: Integer;
begin
FStippleCounter := 0;
L := High(NewStipple) - Low(NewStipple) + 1;
SetLength(FStipplePattern, L);
Move(NewStipple[Low(NewStipple)], FStipplePattern[0], L * SizeOf(TColor32));
end;
function TJclBitmap32.GetStippleColor: TColor32;
var
L: Integer;
NextIndex, PrevIndex: Integer;
PrevWeight: Integer;
begin
L := Length(FStipplePattern);
if L = 0 then
begin
// no pattern defined, just return something and exit
Result := clBlack32;
Exit;
end;
while FStippleCounter >= L do
FStippleCounter := FStippleCounter - L;
while FStippleCounter < 0 do
FStippleCounter := FStippleCounter + L;
PrevIndex := Round(FStippleCounter - 0.5);
PrevWeight := 255 - Round(255 * (FStippleCounter - PrevIndex));
if PrevIndex < 0 then
FStippleCounter := L - 1;
NextIndex := PrevIndex + 1;
if NextIndex >= L then
NextIndex := 0;
if PrevWeight = 255 then
Result := FStipplePattern[PrevIndex]
else
begin
Result := CombineReg(
FStipplePattern[PrevIndex],
FStipplePattern[NextIndex],
PrevWeight);
EMMS;
end;
FStippleCounter := FStippleCounter + FStippleStep;
end;
procedure TJclBitmap32.SetStippleStep(Value: Single);
begin
FStippleStep := Value;
end;
procedure TJclBitmap32.ResetStippleCounter;
begin
FStippleCounter := 0;
end;
procedure TJclBitmap32.DrawHorzLine(X1, Y, X2: Integer; Value: TColor32);
begin
FillLongword(Bits[X1 + Y * Width], X2 - X1 + 1, Value);
end;
procedure TJclBitmap32.DrawHorzLineS(X1, Y, X2: Integer; Value: TColor32);
begin
if (Y >= 0) and (Y < Height) and TestClip(X1, X2, Width) then
DrawHorzLine(X1, Y, X2, Value);
end;
procedure TJclBitmap32.DrawHorzLineT(X1, Y, X2: Integer; Value: TColor32);
var
I: Integer;
P: PColor32;
begin
if X2 < X1 then
Exit;
P := PixelPtr[X1, Y];
for I := X1 to X2 do
begin
BlendMem(Value, P^);
Inc(P);
end;
EMMS;
end;
procedure TJclBitmap32.DrawHorzLineTS(X1, Y, X2: Integer; Value: TColor32);
begin
if (Y >= 0) and (Y < Height) and TestClip(X1, X2, Width) then
DrawHorzLineT(X1, Y, X2, Value);
end;
procedure TJclBitmap32.DrawHorzLineTSP(X1, Y, X2: Integer);
var
I: Integer;
begin
if Empty then
Exit;
if (Y >= 0) and (Y < Height) then
begin
if ((X1 < 0) and (X2 < 0)) or ((X1 >= Width) and (X2 >= Width)) then
Exit;
if X1 < 0 then
X1 := 0
else
if X1 >= Width then
X1 := Width - 1;
if X2 < 0 then
X2 := 0
else
if X2 >= Width then
X2 := Width - 1;
if X2 >= X1 then
for I := X1 to X2 do
SetPixelT(I, Y, GetStippleColor)
else
for I := X2 downto X1 do
SetPixelT(I, Y, GetStippleColor);
end;
end;
procedure TJclBitmap32.DrawVertLine(X, Y1, Y2: Integer; Value: TColor32);
var
I: Integer;
P: PColor32;
begin
if Y2 < Y1 then
Exit;
P := PixelPtr[X, Y1];
for I := 0 to Y2 - Y1 do
begin
P^ := Value;
Inc(P, Width);
end;
end;
procedure TJclBitmap32.DrawVertLineS(X, Y1, Y2: Integer; Value: TColor32);
begin
if (X >= 0) and (X < Width) and TestClip(Y1, Y2, Height) then
DrawVertLine(X, Y1, Y2, Value);
end;
procedure TJclBitmap32.DrawVertLineT(X, Y1, Y2: Integer; Value: TColor32);
var
I: Integer;
P: PColor32;
begin
P := PixelPtr[X, Y1];
for I := Y1 to Y2 do
begin
BlendMem(Value, P^);
Inc(P, Width);
end;
EMMS;
end;
procedure TJclBitmap32.DrawVertLineTS(X, Y1, Y2: Integer; Value: TColor32);
begin
if (X >= 0) and (X < Width) and TestClip(Y1, Y2, Height) then
DrawVertLineT(X, Y1, Y2, Value);
end;
procedure TJclBitmap32.DrawVertLineTSP(X, Y1, Y2: Integer);
var
I: Integer;
begin
if Empty then
Exit;
if (X >= 0) and (X < Width) then
begin
if ((Y1 < 0) and (Y2 < 0)) or ((Y1 >= Height) and (Y2 >= Height)) then
Exit;
if Y1 < 0 then
Y1 := 0
else
if Y1 >= Height then
Y1 := Height - 1;
if Y2 < 0 then
Y2 := 0
else
if Y2 >= Height then
Y2 := Height - 1;
if Y2 >= Y1 then
for I := Y1 to Y2 do
SetPixelT(X, I, GetStippleColor)
else
for I := Y2 downto Y1 do
SetPixelT(X, I, GetStippleColor);
end;
end;
procedure TJclBitmap32.DrawLine(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
var
Dy, Dx, Sy, Sx, I, Delta: Integer;
P: PColor32;
begin
Changing;
try
Dx := X2 - X1;
Dy := Y2 - Y1;
if Dx > 0 then
Sx := 1
else
if Dx < 0 then
begin
Dx := -Dx;
Sx := -1;
end
else // Dx = 0
begin
if Dy > 0 then
DrawVertLine(X1, Y1, Y2 - 1, Value)
else
if Dy < 0 then
DrawVertLine(X1, Y2, Y1 - 1, Value);
if L then
Pixel[X2, Y2] := Value;
Exit;
end;
if Dy > 0 then
Sy := 1
else
if Dy < 0 then
begin
Dy := -Dy;
Sy := -1;
end
else // Dy = 0
begin
if Dx > 0 then
DrawHorzLine(X1, Y1, X2 - 1, Value)
else
DrawHorzLine(X2, Y1, X1 - 1, Value);
if L then
Pixel[X2, Y2] := Value;
Exit;
end;
P := PixelPtr[X1, Y1];
Sy := Sy * Width;
if Dx > Dy then
begin
Delta := Dx shr 1;
for I := 0 to Dx - 1 do
begin
P^ := Value;
Inc(P, Sx);
Delta := Delta + Dy;
if Delta > Dx then
begin
Inc(P, Sy);
Delta := Delta - Dx;
end;
end;
end
else // Dx < Dy
begin
Delta := Dy shr 1;
for I := 0 to Dy - 1 do
begin
P^ := Value;
Inc(P, Sy);
Delta := Delta + Dx;
if Delta > Dy then
begin
Inc(P, Sx);
Delta := Delta - Dy;
end;
end;
end;
if L then
P^ := Value;
finally
Changed;
end;
end;
function TJclBitmap32.ClipLine(var X0, Y0, X1, Y1: Integer): Boolean;
type
TEdge = (Left, Right, Top, Bottom);
TOutCode = set of TEdge;
var
Accept, AllDone: Boolean;
OutCode0, OutCode1, OutCodeOut: TOutCode;
X, Y: Integer;
procedure CompOutCode(X, Y: Integer; var Code: TOutCode);
begin
Code := [];
if X < 0 then
Code := Code + [Left];
if X >= Width then
Code := Code + [Right];
if Y < 0 then
Code := Code + [Top];
if Y >= Height then
Code := Code + [Bottom];
end;
begin
Accept := False;
AllDone := False;
CompOutCode(X0, Y0, OutCode0);
CompOutCode(X1, Y1, OutCode1);
repeat
if (OutCode0 = []) and (OutCode1 = []) then // trivial accept and exit
begin
Accept := True;
AllDone := True;
end
else
if (OutCode0 * OutCode1) <> [] then
AllDone := True // trivial reject
else // calculate intersections
begin
if OutCode0 <> [] then
OutCodeOut := OutCode0
else
OutCodeOut := OutCode1;
X := 0;
Y := 0;
if Left in OutCodeOut then
Y := Y0 + (Y1 - Y0) * (-X0) div (X1 - X0)
else
if Right in OutCodeOut then
begin
Y := Y0 + (Y1 - Y0) * (Width - 1 - X0) div (X1 - X0);
X := Width - 1;
end
else
if Top in OutCodeOut then
X := X0 + (X1 - X0) * (-Y0) div (Y1 - Y0)
else
if Bottom in OutCodeOut then
begin
X := X0 + (X1 - X0) * (Height - 1 - Y0) div (Y1 - Y0);
Y := Height - 1;
end;
if OutCodeOut = OutCode0 then
begin
X0 := X;
Y0 := Y;
CompOutCode(X0, Y0, OutCode0);
end
else
begin
X1 := X;
Y1 := Y;
CompOutCode(X1, Y1, OutCode1);
end;
end;
until AllDone;
Result := Accept;
end;
class function TJclBitmap32.ClipLineF(var X0, Y0, X1, Y1: Single;
MinX, MaxX, MinY, MaxY: Single): Boolean;
type
TEdge = (Left, Right, Top, Bottom);
TOutCode = set of TEdge;
var
Accept, AllDone: Boolean;
OutCode0, OutCode1, OutCodeOut: TOutCode;
X, Y: Single;
procedure CompOutCode(X, Y: Single; var Code: TOutCode);
begin
Code := [];
if X < MinX then
Code := Code + [Left];
if X > MaxX then
Code := Code + [Right];
if Y < MinY then
Code := Code + [Top];
if Y > MaxY then
Code := Code + [Bottom];
end;
begin
Accept := False;
AllDone := False;
CompOutCode(X0, Y0, OutCode0);
CompOutCode(X1, Y1, OutCode1);
repeat
if (OutCode0 = []) and (OutCode1 = []) then // trivial accept and exit
begin
Accept := True;
AllDone := True;
end
else
if (OutCode0 * OutCode1) <> [] then
AllDone := True // trivial reject
else // calculate intersections
begin
if OutCode0 <> [] then
OutCodeOut := OutCode0
else
OutCodeOut := OutCode1;
X := 0;
Y := 0;
if Left in OutCodeOut then
begin
Y := Y0 + (Y1 - Y0) * (MinX - X0) / (X1 - X0);
X := MinX;
end
else
if Right in OutCodeOut then
begin
Y := Y0 + (Y1 - Y0) * (MaxX - X0) / (X1 - X0);
X := MaxX - 1;
end
else
if Top in OutCodeOut then
begin
X := X0 + (X1 - X0) * (MinY - Y0) / (Y1 - Y0);
Y := MinY;
end
else
if Bottom in OutCodeOut then
begin
X := X0 + (X1 - X0) * (MaxY - Y0) / (Y1 - Y0);
Y := MaxY;
end;
if OutCodeOut = OutCode0 then
begin
X0 := X;
Y0 := Y;
CompOutCode(X0, Y0, OutCode0);
end
else
begin
X1 := X;
Y1 := Y;
CompOutCode(X1, Y1, OutCode1);
end;
end;
until AllDone;
Result := Accept;
end;
procedure TJclBitmap32.DrawLineS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
begin
if ClipLine(X1, Y1, X2, Y2) then
DrawLine(X1, Y1, X2, Y2, Value, L);
end;
procedure TJclBitmap32.DrawLineT(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
var
Dy, Dx, Sy, Sx, I, Delta: Integer;
P: PColor32;
begin
Changing;
try
Dx := X2 - X1;
Dy := Y2 - Y1;
if Dx > 0 then
Sx := 1
else
if Dx < 0 then
begin
Dx := -Dx;
Sx := -1;
end
else // Dx = 0
begin
if Dy > 0 then
DrawVertLineT(X1, Y1, Y2 - 1, Value)
else
if Dy < 0 then
DrawVertLineT(X1, Y2, Y1 - 1, Value);
if L then
SetPixelT(X2, Y2, Value);
Exit;
end;
if Dy > 0 then
Sy := 1
else
if Dy < 0 then
begin
Dy := -Dy;
Sy := -1;
end
else // Dy = 0
begin
if Dx > 0 then
DrawHorzLineT(X1, Y1, X2 - 1, Value)
else
DrawHorzLineT(X2, Y1, X1 - 1, Value);
if L then
SetPixelT(X2, Y2, Value);
Exit;
end;
P := PixelPtr[X1, Y1];
Sy := Sy * Width;
try
if Dx > Dy then
begin
Delta := Dx shr 1;
for I := 0 to Dx - 1 do
begin
BlendMem(Value, P^);
Inc(P, Sx);
Delta := Delta + Dy;
if Delta > Dx then
begin
Inc(P, Sy);
Delta := Delta - Dx;
end;
end;
end
else // Dx < Dy
begin
Delta := Dy shr 1;
for I := 0 to Dy - 1 do
begin
BlendMem(Value, P^);
Inc(P, Sy);
Delta := Delta + Dx;
if Delta > Dy then
begin
Inc(P, Sx);
Delta := Delta - Dy;
end;
end;
end;
if L then
BlendMem(Value, P^);
finally
EMMS;
end;
finally
Changed;
end;
end;
procedure TJclBitmap32.DrawLineTS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
begin
if ClipLine(X1, Y1, X2, Y2) then
DrawLineT(X1, Y1, X2, Y2, Value, L);
end;
procedure TJclBitmap32.DrawLineF(X1, Y1, X2, Y2: Single; Value: TColor32; L: Boolean);
var
N, I: Integer;
px, py, ex, ey, nx, ny, hyp: Integer;
A: TColor32;
begin
Changing;
try
px := Round(x1 * 65536);
py := Round(y1 * 65536);
ex := Round(x2 * 65536);
ey := Round(y2 * 65536);
nx := ex - px;
ny := ey - py;
hyp := Round(Hypot(nx, ny));
if L then
Inc(hyp, 65536);
if hyp < 256 then
Exit;
N := hyp shr 16;
if N > 0 then
begin
nx := Round(nx / hyp * 65536);
ny := Round(ny / hyp * 65536);
for I := 0 to N - 1 do
begin
SET_T256(px shr 8, py shr 8, Value);
px := px + nx;
py := py + ny;
end;
end;
A := Value shr 24;
hyp := hyp - N shl 16;
A := A * Longword(hyp) shl 8 and $FF000000;
SET_T256((px + ex - nx) shr 9, (py + ey - ny) shr 9, Value and _RGB + A);
finally
EMMS;
Changed;
end;
end;
procedure TJclBitmap32.DrawLineFS(X1, Y1, X2, Y2: Single; Value: TColor32; L: Boolean);
var
N, I: Integer;
px, py, ex, ey, nx, ny, hyp: Integer;
A: TColor32;
begin
if ClipLineF(X1, Y1, X2, Y2, 0, FWidth, 0, FHeight) then
if (X1 < FWidth - 1) and (X2 < FWidth - 1) and
(Y1 < FHeight - 1) and (Y2 < FHeight - 1) then
DrawLineF(X1, Y1, X2, Y2, Value, False)
else // check every pixel
begin
Changing;
try
px := Round(x1 * 65536);
py := Round(y1 * 65536);
ex := Round(x2 * 65536);
ey := Round(y2 * 65536);
nx := ex - px;
ny := ey - py;
hyp := Round(Hypot(nx, ny));
if L then
Inc(Hyp, 65536);
if hyp < 256 then
Exit;
N := hyp shr 16;
if N > 0 then
begin
nx := Round(nx / hyp * 65536);
ny := Round(ny / hyp * 65536);
for I := 0 to N - 1 do
begin
SET_TS256(px div 256, py div 256, Value);
px := px + nx;
py := py + ny;
end;
end;
A := Value shr 24;
hyp := hyp - N shl 16;
A := A * Longword(hyp) shl 8 and $FF000000;
SET_TS256(Sar(px + ex - nx,9), Sar(py + ey - ny,9), Value and _RGB + A);
finally
EMMS;
Changed;
end;
end;
end;
procedure TJclBitmap32.DrawLineFP(X1, Y1, X2, Y2: Single; L: Boolean);
var
N, I: Integer;
px, py, ex, ey, nx, ny, hyp: Integer;
A, C: TColor32;
begin
Changing;
try
px := Round(x1 * 65536);
py := Round(y1 * 65536);
ex := Round(x2 * 65536);
ey := Round(y2 * 65536);
nx := ex - px;
ny := ey - py;
hyp := Round(Hypot(nx, ny));
if L then
Inc(hyp, 65536);
if hyp < 256 then
Exit;
N := hyp shr 16;
if N > 0 then
begin
nx := Round(nx / hyp * 65536);
ny := Round(ny / hyp * 65536);
for I := 0 to N - 1 do
begin
C := GetStippleColor;
SET_T256(px shr 8, py shr 8, C);
EMMS;
px := px + nx;
py := py + ny;
end;
end;
C := GetStippleColor;
A := C shr 24;
hyp := hyp - N shl 16;
A := A * Longword(hyp) shl 8 and $FF000000;
SET_T256((px + ex - nx) shr 9, (py + ey - ny) shr 9, C and _RGB + A);
EMMS;
finally
Changed;
end;
end;
procedure TJclBitmap32.DrawLineFSP(X1, Y1, X2, Y2: Single; L: Boolean);
var
N, I: Integer;
px, py, ex, ey, nx, ny, hyp: Integer;
A, C: TColor32;
begin
if ClipLineF(X1, Y1, X2, Y2, 0, FWidth, 0, FHeight) then
if (X1 < FWidth - 1) and (X2 < FWidth - 1) and
(Y1 < FHeight - 1) and (Y2 < FHeight - 1) then
DrawLineFP(X1, Y1, X2, Y2, False)
else // check every pixel
begin
Changing;
try
px := Round(x1 * 65536);
py := Round(y1 * 65536);
ex := Round(x2 * 65536);
ey := Round(y2 * 65536);
nx := ex - px;
ny := ey - py;
hyp := Round(Hypot(nx, ny));
if L then
Inc(hyp, 65536);
if hyp < 256 then
Exit;
N := hyp shr 16;
if N > 0 then
begin
nx := Round(nx / hyp * 65536);
ny := Round(ny / hyp * 65536);
for I := 0 to N - 1 do
begin
C := GetStippleColor;
SET_TS256(px div 256, py div 256, C);
EMMS;
px := px + nx;
py := py + ny;
end;
end;
C := GetStippleColor;
A := C shr 24;
hyp := hyp - N shl 16;
A := A * Longword(hyp) shl 8 and $FF000000;
SET_TS256(Sar(px + ex - nx,9), Sar(py + ey - ny,9), C and _RGB + A);
EMMS;
finally
Changed;
end;
end;
end;
procedure TJclBitmap32.DrawLineA(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
var
Dx, Dy, Sx, Sy, D: Integer;
EC, EA: Word;
CI: Byte;
P: PColor32;
begin
if (X1 = X2) or (Y1 = Y2) then
begin
DrawLineT(X1, Y1, X2, Y2, Value, L);
Exit;
end;
Dx := X2 - X1;
Dy := Y2 - Y1;
if Dx > 0 then
Sx := 1
else
begin
Sx := -1;
Dx := -Dx;
end;
if Dy > 0 then
Sy := 1
else
begin
Sy := -1;
Dy := -Dy;
end;
Changing;
try
EC := 0;
BlendMem(Value, Bits[X1 + Y1 * Width]);
if Dy > Dx then
begin
EA := Dx shl 16 div Dy;
if not L then
Dec(Dy);
while Dy > 0 do
begin
Dec(Dy);
D := EC;
Inc(EC, EA);
if EC <= D then
Inc(X1, Sx);
Inc(Y1, Sy);
CI := EC shr 8;
P := @Bits[X1 + Y1 * Width];
BlendMemEx(Value, P^, GAMMA_TABLE[CI xor 255]);
Inc(P, Sx);
BlendMemEx(Value, P^, GAMMA_TABLE[CI]);
end;
end
else // DY <= DX
begin
EA := Dy shl 16 div Dx;
if not L then
Dec(Dx);
while Dx > 0 do
begin
Dec(Dx);
D := EC;
Inc(EC, EA);
if EC <= D then
Inc(Y1, Sy);
Inc(X1, Sx);
CI := EC shr 8;
P := @Bits[X1 + Y1 * Width];
BlendMemEx(Value, P^, GAMMA_TABLE[CI xor 255]);
if Sy = 1 then
Inc(P, Width)
else
Dec(P, Width);
BlendMemEx(Value, P^, GAMMA_TABLE[CI]);
end;
end;
finally
EMMS;
Changed;
end;
end;
procedure TJclBitmap32.DrawLineAS(X1, Y1, X2, Y2: Integer; Value: TColor32; L: Boolean);
begin
if ClipLine(X1, Y1, X2, Y2) then
DrawLineA(X1, Y1, X2, Y2, Value, L);
end;
procedure TJclBitmap32.MoveTo(X, Y: Integer);
begin
RasterX := X;
RasterY := Y;
end;
procedure TJclBitmap32.LineToS(X, Y: Integer);
begin
DrawLineS(RasterX, RasterY, X, Y, PenColor, False);
RasterX := X;
RasterY := Y;
end;
procedure TJclBitmap32.LineToTS(X, Y: Integer);
begin
DrawLineTS(RasterX, RasterY, X, Y, PenColor, False);
RasterX := X;
RasterY := Y;
end;
procedure TJclBitmap32.LineToAS(X, Y: Integer);
begin
DrawLineAS(RasterX, RasterY, X, Y, PenColor, False);
RasterX := X;
RasterY := Y;
end;
procedure TJclBitmap32.MoveToF(X, Y: Single);
begin
RasterXF := X;
RasterYF := Y;
end;
procedure TJclBitmap32.LineToFS(X, Y: Single);
begin
DrawLineFS(RasterXF, RasterYF, X, Y, PenColor, False);
RasterXF := X;
RasterYF := Y;
end;
procedure TJclBitmap32.FillRect(X1, Y1, X2, Y2: Integer; Value: TColor32);
var
J: Integer;
P: PColor32Array;
begin
Changing;
for J := Y1 to Y2 do
begin
P := Pointer(GetScanLine(J));
FillLongword(P[X1], X2 - X1 + 1, Value);
end;
Changed;
end;
procedure TJclBitmap32.FillRectS(X1, Y1, X2, Y2: Integer; Value: TColor32);
begin
if TestClip(X1, X2, Width) and TestClip(Y1, Y2, Height) then
FillRect(X1, Y1, X2, Y2, Value);
end;
procedure TJclBitmap32.FillRectT(X1, Y1, X2, Y2: Integer; Value: TColor32);
var
I, J: Integer;
P: PColor32;
A: Integer;
begin
A := Value shr 24;
if A = $FF then
FillRect(X1, Y1, X2, Y2, Value)
else
begin
Changing;
try
for J := Y1 to Y2 do
begin
P := GetPixelPtr(X1, J);
for I := X1 to X2 do
begin
CombineMem(Value, P^, A);
Inc(P);
end;
end;
finally
EMMS;
Changed;
end;
end;
end;
procedure TJclBitmap32.FillRectTS(X1, Y1, X2, Y2: Integer; Value: TColor32);
begin
if TestClip(X1, X2, Width) and TestClip(Y1, Y2, Height) then
FillRectT(X1, Y1, X2, Y2, Value);
end;
procedure TJclBitmap32.FrameRectS(X1, Y1, X2, Y2: Integer; Value: TColor32);
begin
Changing;
TestSwap(X1, X2);
TestSwap(Y1, Y2);
DrawHorzLineS(X1, Y1, X2, Value);
if Y2 > Y1 then
DrawHorzLineS(X1, Y2, X2, Value);
if Y2 > Y1 + 1 then
begin
DrawVertLineS(X1, Y1 + 1, Y2 - 1, Value);
if X2 > X1 then
DrawVertLineS(X2, Y1 + 1, Y2 - 1, Value);
end;
Changed;
end;
procedure TJclBitmap32.FrameRectTS(X1, Y1, X2, Y2: Integer; Value: TColor32);
begin
Changing;
TestSwap(X1, X2);
TestSwap(Y1, Y2);
DrawHorzLineTS(X1, Y1, X2, Value);
if Y2 > Y1 then
DrawHorzLineTS(X1, Y2, X2, Value);
if Y2 > Y1 + 1 then
begin
DrawVertLineTS(X1, Y1 + 1, Y2 - 1, Value);
if X2 > X1 then
DrawVertLineTS(X2, Y1 + 1, Y2 - 1, Value);
end;
Changed;
end;
procedure TJclBitmap32.FrameRectTSP(X1, Y1, X2, Y2: Integer);
begin
Changing;
TestSwap(X1, X2);
TestSwap(Y1, Y2);
DrawHorzLineTSP(X1, Y1, X2);
if Y2 > Y1 + 1 then
begin
DrawVertLineTSP(X2, Y1 + 1, Y2 - 1);
if X2 > X1 then
DrawVertLineTSP(X1, Y1 + 1, Y2 - 1);
end;
if Y2 > Y1 then
DrawHorzLineTSP(X1, Y2, X2);
Changed;
end;
procedure TJclBitmap32.RaiseRectTS(X1, Y1, X2, Y2: Integer; Contrast: Integer);
var
C1, C2: TColor32;
begin
Changing;
try
if Contrast > 0 then
begin
C1 := clWhite32;
C2 := clBlack32;
end
else
if Contrast < 0 then
begin
C1 := clBlack32;
C2 := clWhite32;
Contrast := -Contrast;
end
else
Exit;
Contrast := Clamp(Contrast * 255 div 100);
C1 := SetAlpha(C1, Contrast);
C2 := SetAlpha(C2, Contrast);
TestSwap(X1, X2);
TestSwap(Y1, Y2);
DrawHorzLineTS(X1, Y1, X2 - 1, C1);
DrawHorzLineTS(X1 + 1, Y2, X2, C2);
DrawVertLineTS(X1, Y1, Y2 - 1, C1);
DrawVertLineTS(X2, Y1 + 1, Y2, C2);
finally
Changed;
end;
end;
procedure TJclBitmap32.LoadFromStream(Stream: TStream);
var
B: TBitmap;
begin
Changing;
B := TBitmap.Create;
try
B.LoadFromStream(Stream);
Assign(B);
finally
B.Free;
Changed;
end;
end;
procedure TJclBitmap32.SaveToStream(Stream: TStream);
var
B: TBitmap;
begin
B := TBitmap.Create;
try
AssignTo(B);
B.SaveToStream(Stream);
finally
B.Free;
end;
end;
procedure TJclBitmap32.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
Result := not (Filer.Ancestor is TGraphic)
else
Result := not Empty;
end;
begin
Filer.DefineBinaryProperty('Data', ReadData, WriteData, DoWrite);
end;
procedure TJclBitmap32.ReadData(Stream: TStream);
var
w, h: Integer;
begin
Changing;
try
Stream.ReadBuffer(w, 4);
Stream.ReadBuffer(h, 4);
SetSize(w, h);
Stream.ReadBuffer(FBits[0], FWidth * FHeight * 4);
finally
Changed;
end;
end;
procedure TJclBitmap32.WriteData(Stream: TStream);
begin
Stream.WriteBuffer(FWidth, 4);
Stream.WriteBuffer(FHeight, 4);
Stream.WriteBuffer(FBits[0], FWidth * FHeight * 4);
end;
procedure TJclBitmap32.LoadFromFile(const FileName: string);
var
P: TPicture;
begin
P := TPicture.Create;
try
P.LoadFromFile(FileName);
Assign(P);
finally
P.Free;
end;
end;
procedure TJclBitmap32.SaveToFile(const FileName: string);
var
B: TBitmap;
begin
B := TBitmap.Create;
try
AssignTo(B);
B.SaveToFile(FileName);
finally
B.Free;
end;
end;
procedure TJclBitmap32.SetFont(Value: TFont);
begin
FFont.Assign(Value);
FontChanged(Self);
end;
procedure TJclBitmap32.FontChanged(Sender: TObject);
begin
if FontHandle > 0 then
begin
SelectObject(Handle, GetStockObject(SYSTEM_FONT));
FontHandle := 0;
end;
end;
procedure TJclBitmap32.UpdateFont;
begin
if FontHandle = 0 then
begin
SelectObject(Handle, Font.Handle);
SetTextColor(Handle, ColorToRGB(Font.Color));
SetBkMode(Handle, Windows.TRANSPARENT);
end;
end;
procedure TJclBitmap32.SetDrawMode(Value: TDrawMode);
begin
if FDrawMode <> Value then
begin
Changing;
FDrawMode := Value;
Changed;
end;
end;
procedure TJclBitmap32.SetMasterAlpha(Value: Byte);
begin
if FMasterAlpha <> Value then
begin
Changing;
FMasterAlpha := Value;
Changed;
end;
end;
procedure TJclBitmap32.SetStretchFilter(Value: TStretchFilter);
begin
if FStretchFilter <> Value then
begin
Changing;
FStretchFilter := Value;
Changed;
end;
end;
function TJclBitmap32.TextExtent(const Text: string): TSize;
begin
UpdateFont;
Result.cX := 0;
Result.cY := 0;
Windows.GetTextExtentPoint32(Handle, PChar(Text), Length(Text), Result);
end;
procedure TJclBitmap32.TextOut(X, Y: Integer; const Text: string);
begin
Changing;
UpdateFont;
ExtTextOut(Handle, X, Y, 0, nil, PChar(Text), Length(Text), nil);
Changed;
end;
procedure TJclBitmap32.TextOut(X, Y: Integer; const ClipRect: TRect;
const Text: string);
begin
Changing;
UpdateFont;
ExtTextOut(Handle, X, Y, ETO_CLIPPED, @ClipRect, PChar(Text), Length(Text), nil);
Changed;
end;
procedure TJclBitmap32.TextOut(ClipRect: TRect; const Flags: Cardinal;
const Text: string);
begin
Changing;
UpdateFont;
DrawText(Handle, PChar(Text), Length(Text), ClipRect, Flags);
Changed;
end;
function TJclBitmap32.TextHeight(const Text: string): Integer;
begin
Result := TextExtent(Text).cY;
end;
function TJclBitmap32.TextWidth(const Text: string): Integer;
begin
Result := TextExtent(Text).cX;
end;
procedure TJclBitmap32.RenderText(X, Y: Integer; const Text: string; AALevel: Integer; Color: TColor32);
var
B, B2: TJclBitmap32;
Sz: TSize;
C: TColor32;
I: Integer;
P: PColor32;
begin
AALevel := Constrain(AALevel, 0, 4);
B := TJclBitmap32.Create;
try
if AALevel = 0 then
begin
Sz := TextExtent(Text + ' ');
B.SetSize(Sz.cX, Sz.cY);
B.Font := Font;
B.Clear(0);
B.Font.Color := clWhite;
B.TextOut(0, 0, Text);
end
else
begin
B2 := TJclBitmap32.Create;
try
B2.SetSize(1, 1); // just need some DC here
B2.Font := Font;
B2.Font.Size := Font.Size shl AALevel;
Sz := B2.TextExtent(Text + ' ');
Sz.cx := (Sz.cx shr AALevel + 1) shl AALevel;
B2.SetSize(Sz.cx, Sz.cy);
B2.Clear(0);
B2.Font.Color := clWhite;
B2.TextOut(0, 0, Text);
B2.StretchFilter := sfLinear;
B.SetSize(Sz.cx shr AALevel, Sz.cy shr AALevel);
B.Draw(Rect(0, 0, B.Width, B.Height), Rect(0, 0, B2.Width, B2.Height), B2);
finally
B2.Free;
end;
end;
// convert intensity and color to alpha
B.MasterAlpha := Color shr 24;
Color := Color and $00FFFFFF;
P := @B.Bits[0];
for I := 0 to B.Width * B.Height - 1 do
begin
C := P^;
if C <> 0 then
begin
C := P^ shl 24; // transfer blue channel to alpha
C := C + Color;
P^ := C;
end;
Inc(P);
end;
B.DrawMode := dmBlend;
B.DrawTo(Self, X, Y);
finally
B.Free;
end;
end;
//=== { TJclByteMap } ========================================================
destructor TJclByteMap.Destroy;
begin
FBytes := nil;
inherited Destroy;
end;
procedure TJclByteMap.Assign(Source: TPersistent);
begin
Changing;
BeginUpdate;
try
if Source is TJclByteMap then
begin
FWidth := TJclByteMap(Source).Width;
FHeight := TJclByteMap(Source).Height;
FBytes := Copy(TJclByteMap(Source).Bytes, 0, FWidth * FHeight);
end
else
if Source is TJclBitmap32 then
ReadFrom(TJclBitmap32(Source), ckWeightedRGB)
else
inherited Assign(Source);
finally
EndUpdate;
Changed;
end;
end;
procedure TJclByteMap.AssignTo(Dst: TPersistent);
begin
if Dst is TJclBitmap32 then
WriteTo(TJclBitmap32(Dst), ckUniformRGB)
else
inherited AssignTo(Dst);
end;
procedure TJclByteMap.Clear(FillValue: Byte);
begin
Changing;
FillChar(Bytes[0], Width * Height, FillValue);
Changed;
end;
function TJclByteMap.Empty: Boolean;
begin
Result := Bytes = nil;
end;
function TJclByteMap.GetValPtr(X, Y: Integer): PByte;
begin
Result := @Bytes[X + Y * Width];
end;
function TJclByteMap.GetValue(X, Y: Integer): Byte;
begin
Result := Bytes[X + Y * Width];
end;
procedure TJclByteMap.ReadFrom(Source: TJclBitmap32; Conversion: TConversionKind);
var
W, H, I, N: Integer;
SrcC: PColor32;
SrcB, DstB: PByte;
Value: TColor32;
begin
Changing;
BeginUpdate;
try
SetSize(Source.Width, Source.Height);
if Empty then
Exit;
W := Source.Width;
H := Source.Height;
N := W * H - 1;
SrcC := Source.PixelPtr[0, 0];
SrcB := Pointer(SrcC);
DstB := @Bytes[0];
case Conversion of
ckRed:
begin
Inc(SrcB, 2);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB);
Inc(SrcB, 4);
end;
end;
ckGreen:
begin
Inc(SrcB, 1);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB);
Inc(SrcB, 4);
end;
end;
ckBlue:
begin
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB);
Inc(SrcB, 4);
end;
end;
ckAlpha:
begin
Inc(SrcB, 3);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB);
Inc(SrcB, 4);
end;
end;
ckUniformRGB:
begin
for I := 0 to N do
begin
Value := SrcC^;
Value := (Value and $00FF0000) shr 16 + (Value and $0000FF00) shr 8 +
(Value and $000000FF);
Value := Value div 3;
DstB^ := Value;
Inc(DstB);
Inc(SrcC);
end;
end;
ckWeightedRGB:
begin
for I := 0 to N do
begin
DstB^ := Intensity(SrcC^);
Inc(DstB);
Inc(SrcC);
end;
end;
end;
finally
EndUpdate;
Changed;
end;
end;
procedure TJclByteMap.SetValue(X, Y: Integer; Value: Byte);
begin
Bytes[X + Y * Width] := Value;
end;
procedure TJclByteMap.SetSize(NewWidth, NewHeight: Integer);
begin
Changing;
inherited SetSize(NewWidth, NewHeight);
SetLength(FBytes, Width * Height);
Changed;
end;
procedure TJclByteMap.WriteTo(Dest: TJclBitmap32; Conversion: TConversionKind);
var
W, H, I, N: Integer;
DstC: PColor32;
DstB, SrcB: PByte;
begin
Dest.Changing;
Dest.BeginUpdate;
try
Dest.SetSize(Width, Height);
if Empty then
Exit;
W := Width;
H := Height;
N := W * H - 1;
DstC := Dest.PixelPtr[0, 0];
DstB := Pointer(DstC);
SrcB := @Bytes[0];
case Conversion of
ckRed:
begin
Inc(DstB, 2);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB, 4);
Inc(SrcB);
end;
end;
ckGreen:
begin
Inc(DstB, 1);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB, 4);
Inc(SrcB);
end;
end;
ckBlue:
begin
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB, 4);
Inc(SrcB);
end;
end;
ckAlpha:
begin
Inc(DstB, 3);
for I := 0 to N do
begin
DstB^ := SrcB^;
Inc(DstB, 4);
Inc(SrcB);
end;
end;
ckUniformRGB, ckWeightedRGB:
begin
for I := 0 to N do
begin
DstC^ := Gray32(SrcB^, $FF);
Inc(DstC);
Inc(SrcB);
end;
end;
end;
finally
Dest.EndUpdate;
Dest.Changed;
end;
end;
procedure TJclByteMap.WriteTo(Dest: TJclBitmap32; const Palette: TPalette32);
var
W, H, I, N: Integer;
DstC: PColor32;
SrcB: PByte;
begin
Dest.Changing;
Dest.BeginUpdate;
try
Dest.SetSize(Width, Height);
if Empty then
Exit;
W := Width;
H := Height;
N := W * H - 1;
DstC := Dest.PixelPtr[0, 0];
SrcB := @Bytes[0];
for I := 0 to N do
begin
DstC^ := Palette[SrcB^];
Inc(DstC);
Inc(SrcB);
end;
finally
Dest.EndUpdate;
Dest.Changed;
end;
end;
//=== 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 PolylineTS(Bitmap: TJclBitmap32; const Points: TDynPointArray;
Color: TColor32);
var
I, L: Integer;
DoAlpha: Boolean;
begin
DoAlpha := Color and $FF000000 <> $FF000000;
L := Length(Points);
if L < 2 then
Exit;
Bitmap.Changing;
Bitmap.BeginUpdate;
with Points[L - 1] do
Bitmap.MoveTo(X, Y);
Bitmap.PenColor := Color;
if DoAlpha then
for I := 0 to L - 1 do
with Points[I] do
Bitmap.LineToTS(X, Y)
else
for I := 0 to L - 1 do
with Points[I] do
Bitmap.LineToS(X, Y);
Bitmap.EndUpdate;
Bitmap.Changed;
end;
procedure PolyLineAS(Bitmap: TJclBitmap32; const Points: TDynPointArray;
Color: TColor32);
var
I, L: Integer;
begin
L := Length(Points);
if L < 2 then
Exit;
Bitmap.Changing;
Bitmap.BeginUpdate;
with Points[L - 1] do
Bitmap.MoveTo(X, Y);
Bitmap.PenColor := Color;
for I := 0 to L - 1 do
with Points[I] do
Bitmap.LineToAS(X, Y);
Bitmap.EndUpdate;
Bitmap.Changed;
end;
procedure PolylineFS(Bitmap: TJclBitmap32; const Points: TDynPointArrayF;
Color: TColor32);
var
I, L: Integer;
begin
L := Length(Points);
if L < 2 then
Exit;
Bitmap.Changing;
Bitmap.BeginUpdate;
with Points[L - 1] do
Bitmap.MoveToF(X, Y);
Bitmap.PenColor := Color;
for I := 0 to L - 1 do
with Points[I] do
Bitmap.LineToFS(X, Y);
Bitmap.EndUpdate;
Bitmap.Changed;
end;
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;
procedure FillLines(Bitmap: TJclBitmap32; BaseY: Integer;
const ScanLines: TScanLines; Color: TColor32);
var
I, J, L: Integer;
Left, Right: Integer;
DoAlpha: Boolean;
begin
DoAlpha := Color and $FF000000 <> $FF000000;
for J := 0 to High(ScanLines) do
begin
L := Length(ScanLines[J]); // assuming length is even
I := 0;
while I < L do
begin
Left := ScanLines[J][I];
Inc(I);
Right := ScanLines[J][I];
if Right > Left then
begin
if (Left and $FF) < $80 then
Left := Left shr 8
else
Left := Left shr 8 + 1;
if (Right and $FF) < $80 then
Right := Right shr 8
else
Right := Right shr 8 + 1;
if DoAlpha then
Bitmap.DrawHorzLineT(Left, BaseY + J, Right, Color)
else
Bitmap.DrawHorzLine(Left, BaseY + J, Right, Color);
end;
Inc(I);
end;
end;
end;
procedure FillLines2(Bitmap: TJclBitmap32; BaseY: Integer;
const ScanLines: TScanLines; Color: TColor32);
var
I, J, L, N: Integer;
MinY, MaxY, Y, Top, Bottom: Integer;
MinX, MaxX, X, Dx: Integer;
Left, Right: Integer;
Buffer: array of Integer;
P: PColor32;
DoAlpha: Boolean;
begin
DoAlpha := Color and $FF000000 <> $FF000000;
// find the range of Y screen coordinates
MinY := BaseY shr 4;
MaxY := (BaseY + Length(ScanLines) + 15) shr 4;
Y := MinY;
while Y < MaxY do
begin
Top := Y shl 4 - BaseY;
Bottom := Top + 15;
if Top < 0 then
Top := 0;
if Bottom > High(ScanLines) then
Bottom := High(ScanLines);
// find left and right edges of the screen scanline
MinX := 1000000;
MaxX := -1000000;
for J := Top to Bottom do
begin
L := High(ScanLines[J]);
Left := ScanLines[J][0] shr 4;
Right := (ScanLines[J][L] + 15) shr 4;
if Left < MinX then
MinX := Left;
if Right > MaxX then
MaxX := Right;
end;
// allocate the buffer for a screen scanline
SetLength(Buffer, MaxX - MinX + 2);
FillLongword(Buffer[0], Length(Buffer), 0);
// and fill it
for J := Top to Bottom do
begin
I := 0;
L := Length(ScanLines[J]);
while I < L do
begin
// Left edge
X := ScanLines[J][I];
Dx := X and $0F;
X := X shr 4 - MinX;
Inc(Buffer[X], Dx xor $0F);
Inc(Buffer[X + 1], Dx);
Inc(I);
// Right edge
X := ScanLines[J][I];
Dx := X and $0F;
X := X shr 4 - MinX;
Dec(Buffer[X], Dx xor $0F);
Dec(Buffer[X + 1], Dx);
Inc(I);
end;
end;
// integrate the buffer
N := 0;
for I := 0 to High(Buffer) do
begin
Inc(N, Buffer[I]);
Buffer[I] := N * 273 shr 8; // some bias
end;
// draw it to the screen
P := Bitmap.PixelPtr[MinX, Y];
try
if DoAlpha then
for I := 0 to High(Buffer) do
begin
BlendMemEx(Color, P^, Buffer[I]);
Inc(P);
end
else
for I := 0 to High(Buffer) do
begin
N := Buffer[I];
if N = 255 then
P^ := Color
else
BlendMemEx(Color, P^, Buffer[I]);
Inc(P);
end;
finally
EMMS;
end;
Inc(Y);
end;
end;
procedure GetMinMax(const Points: TDynPointArray; out MinY, MaxY: Integer);
var
I, Y: Integer;
begin
MinY := 100000;
MaxY := -100000;
for I := 0 to High(Points) do
begin
Y := Points[I].Y;
if Y < MinY then
MinY := Y;
if Y > MaxY then
MaxY := Y;
end;
end;
procedure PolygonTS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
var
L, MinY, MaxY: Integer;
ScanLines: TScanLines;
begin
L := Length(Points);
if L < 3 then
Exit;
GetMinMax(Points, MinY, MaxY);
MinY := Constrain(MinY, 0, Bitmap.Height);
MaxY := Constrain(MaxY, 0, Bitmap.Height);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
AddPolygon(Points, MinY, Bitmap.Width shl 8 - 1, Bitmap.Height - 1,
ScanLines, True);
SortLines(ScanLines);
Bitmap.Changing;
Bitmap.BeginUpdate;
try
FillLines(Bitmap, MinY, ScanLines, Color);
finally
Bitmap.EndUpdate;
Bitmap.Changed;
end;
end;
procedure PolygonAS(Bitmap: TJclBitmap32; const Points: TDynPointArray; Color: TColor32);
var
L, I, MinY, MaxY: Integer;
ScanLines: TScanLines;
PP: TDynPointArray;
begin
L := Length(Points);
if L < 3 then
Exit;
SetLength(PP, L);
for I := 0 to L - 1 do
begin
PP[I].X := Points[I].X shl 4 + 7;
PP[I].Y := Points[I].Y shl 4 + 7;
end;
GetMinMax(PP, MinY, MaxY);
MinY := Constrain(MinY, 0, Bitmap.Height shl 4 - 1);
MaxY := Constrain(MaxY, 0, Bitmap.Height shl 4 - 1);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
AddPolygon(PP, MinY, Bitmap.Width shl 4 - 1, Bitmap.Height shl 4 - 1,
ScanLines, False);
SortLines(ScanLines);
Bitmap.Changing;
Bitmap.BeginUpdate;
try
FillLines2(Bitmap, MinY, ScanLines, Color);
finally
Bitmap.EndUpdate;
Bitmap.Changed;
end;
end;
procedure PolygonFS(Bitmap: TJclBitmap32; const Points: TDynPointArrayF; Color: TColor32);
var
L, I, MinY, MaxY: Integer;
ScanLines: TScanLines;
PP: TDynPointArray;
begin
L := Length(Points);
if L < 3 then
Exit;
SetLength(PP, L);
for I := 0 to L - 1 do
begin
PP[I].X := Round(Points[I].X * 16) + 7;
PP[I].Y := Round(Points[I].Y * 16) + 7;
end;
GetMinMax(PP, MinY, MaxY);
MinY := Constrain(MinY, 0, Bitmap.Height shl 4 - 1);
MaxY := Constrain(MaxY, 0, Bitmap.Height shl 4 - 1);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
AddPolygon(PP, MinY, Bitmap.Width shl 4 - 1, Bitmap.Height shl 4 - 1,
ScanLines, False);
SortLines(ScanLines);
Bitmap.Changing;
Bitmap.BeginUpdate;
try
FillLines2(Bitmap, MinY, ScanLines, Color);
finally
Bitmap.EndUpdate;
Bitmap.Changed;
end;
end;
procedure PolyPolygonTS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArray;
Color: TColor32);
var
N, L, min, max, MinY, MaxY: Integer;
ScanLines: TScanLines;
begin
MinY := 100000;
MaxY := -100000;
for N := 0 to High(Points) do
begin
L := Length(Points[N]);
if L < 3 then
Exit;
GetMinMax(Points[N], min, max);
if min < MinY then
MinY := min;
if max > MaxY then
MaxY := max;
end;
MinY := Constrain(MinY, 0, Bitmap.Height - 1);
MaxY := Constrain(MaxY, 0, Bitmap.Height - 1);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
for N := 0 to High(Points) do
AddPolygon(Points[N], MinY, Bitmap.Width shl 8 - 1 , Bitmap.Height - 1,
ScanLines, True);
SortLines(ScanLines);
Bitmap.Changing;
FillLines(Bitmap, MinY, ScanLines, Color);
Bitmap.Changed;
end;
procedure PolyPolygonAS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArray;
Color: TColor32);
var
N, L, I, min, max, MinY, MaxY: Integer;
ScanLines: TScanLines;
PPP: TDynDynPointArrayArray;
begin
MinY := 100000;
MaxY := -100000;
SetLength(PPP, Length(Points));
for N := 0 to High(Points) do
begin
L := Length(Points);
SetLength(PPP[N], Length(Points[N]));
for I := 0 to L - 1 do
begin
PPP[N][I].X := Points[N][I].X shl 4 + 7;
PPP[N][I].Y := Points[N][I].Y shl 4 + 7;
end;
if L < 3 then
Continue;
GetMinMax(PPP[N], min, max);
if min < MinY then
MinY := min;
if max > MaxY then
MaxY := max;
end;
MinY := Constrain(MinY, 0, Bitmap.Height shl 4 - 1);
MaxY := Constrain(MaxY, 0, Bitmap.Height shl 4 - 1);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
for N := 0 to High(PPP) do
begin
AddPolygon(PPP[N], MinY, Bitmap.Width shl 4 - 1, Bitmap.Height shl 4 - 1,
ScanLines, False);
end;
SortLines(ScanLines);
Bitmap.Changing;
FillLines2(Bitmap, MinY, ScanLines, Color);
Bitmap.Changed;
end;
procedure PolyPolygonFS(Bitmap: TJclBitmap32; const Points: TDynDynPointArrayArrayF;
Color: TColor32);
var
N, L, I, min, max, MinY, MaxY: Integer;
ScanLines: TScanLines;
PPP: TDynDynPointArrayArray;
begin
MinY := 100000;
MaxY := -100000;
SetLength(PPP, Length(Points));
for N := 0 to High(Points) do
begin
L := Length(Points);
SetLength(PPP[N], Length(Points[N]));
for I := 0 to L - 1 do
begin
PPP[N][I].X := Round(Points[N][I].X * 16) + 7;
PPP[N][I].Y := Round(Points[N][I].Y * 16) + 7;
end;
if L < 3 then
Continue;
GetMinMax(PPP[N], min, max);
if min < MinY then
MinY := min;
if max > MaxY then
MaxY := max;
end;
MinY := Constrain(MinY, 0, Bitmap.Height shl 4 - 1);
MaxY := Constrain(MaxY, 0, Bitmap.Height shl 4 - 1);
if MinY >= MaxY then
Exit;
SetLength(ScanLines, MaxY - MinY + 1);
for N := 0 to High(PPP) do
AddPolygon(PPP[N], MinY, Bitmap.Width shl 4 - 1, Bitmap.Height shl 4 - 1,
ScanLines, False);
SortLines(ScanLines);
Bitmap.Changing;
FillLines2(Bitmap, MinY, ScanLines, Color);
Bitmap.Changed;
end;
//=== Filters ================================================================
procedure CheckParams(Dst, Src: TJclBitmap32);
begin
if Src = nil then
raise EJclGraphicsError.CreateRes(@RsSourceBitmapEmpty);
if Dst = nil then
raise EJclGraphicsError.CreateRes(@RsDestinationBitmapEmpty);
Dst.SetSize(Src.Width, Src.Height); // Should this go? See #0001513. It is currently of no use.
end;
procedure AlphaToGrayscale(Dst, Src: TJclBitmap32);
var
I: Integer;
D, S: PColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
D^ := Gray32(AlphaComponent(S^), $FF);
Inc(S);
Inc(D);
end;
Dst.Changed;
end;
procedure IntensityToAlpha(Dst, Src: TJclBitmap32);
var
I: Integer;
D, S: PColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
D^ := SetAlpha(D^, Intensity(S^));
Inc(S);
Inc(D);
end;
Dst.Changed;
end;
procedure Invert(Dst, Src: TJclBitmap32);
var
I: Integer;
D, S: PColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
D^ := S^ xor $FFFFFFFF;
Inc(S);
Inc(D);
end;
Dst.Changed;
end;
procedure InvertRGB(Dst, Src: TJclBitmap32);
var
I: Integer;
D, S: PColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
D^ := S^ xor $00FFFFFF;
Inc(S);
Inc(D);
end;
Dst.Changed;
end;
procedure ColorToGrayscale(Dst, Src: TJclBitmap32);
var
I: Integer;
D, S: PColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
D^ := Gray32(Intensity(S^), $FF);
Inc(S);
Inc(D);
end;
Dst.Changed;
end;
procedure ApplyLUT(Dst, Src: TJclBitmap32; const LUT: TLUT8);
var
I: Integer;
D, S: PColor32;
r, g, b: TColor32;
C: TColor32;
begin
CheckParams(Dst, Src);
Dst.Changing;
Dst.SetSize(Src.Width, Src.Height);
D := @Dst.Bits[0];
S := @Src.Bits[0];
for I := 0 to Src.Width * Src.Height - 1 do
begin
C := S^;
r := C and $00FF0000;
g := C and $0000FF00;
r := r shr 16;
b := C and $000000FF;
g := g shr 8;
r := LUT[r];
g := LUT[g];
b := LUT[b];
D^ := $FF000000 or r shl 16 or g shl 8 or b;
Inc(S);
Inc(D);
end;
Dst.Changed;
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.