lazarus-ccr/components/rgbgraphics/rgbroutines.pas
tomb0 59640eecea Improved clipping rect handling.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@553 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2008-09-11 08:14:25 +00:00

1120 lines
28 KiB
ObjectPascal

{
/***************************************************************************
RGBRoutines.pas
***************************************************************************/
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Author: Tom Gregorovic (_tom_@centrum.cz)
Abstract:
This unit contains routines for manipulating rgb bitmaps (stretching,
drawing on canvas...) and for drawing primitives (lines,
ellipses...).
}
unit RGBRoutines;
{$ifdef fpc}
{$mode objfpc}{$H+}
{$endif}
interface
uses
SysUtils, Math, Forms, LCLIntf,
LCLType, LCLProc, FPImage, IntfGraphics,
Classes,
{$IFDEF LCLwin32}
RGBWinRoutines,
{$ENDIF}
{$IFDEF LCLqt}
RGBQtRoutines,
{$ENDIF}
{$IFDEF LCLgtk}
{$DEFINE StretchRGB32}
RGBGTKRoutines,
{$ENDIF}
{$IFDEF LCLgtk2}
{$DEFINE StretchRGB32}
RGBGTKRoutines,
{$ENDIF}
{$IFDEF LCLcarbon}
{$DEFINE StretchRGB32}
RGBCarbonRoutines,
{$ENDIF}
RGBTypes, RGBUtils;
procedure DrawRGB32Bitmap(Dst: TRGB32BitmapCore; X, Y: Integer; Src: TRGB32BitmapCore); overload;
procedure DrawRGB8Bitmap(Dst: TRGB8BitmapCore; X, Y: Integer; Src: TRGB8BitmapCore); overload;
procedure StretchRGB32BitmapTrunc(Dst, Src: TRGB32BitmapCore);
procedure StretchRGB8BitmapTrunc(Dst, Src: TRGB8BitmapCore);
procedure DrawRGB32Bitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY, SrcWidth, SrcHeight: Integer;
Bitmap: TRGB32BitmapCore); overload;
procedure StretchDrawRGB32Bitmap(Dest: HDC; DstX, DstY, DstWidth, DstHeight: Integer;
SrcX, SrcY, SrcWidth, SrcHeight: Integer; Bitmap: TRGB32BitmapCore); overload;
procedure StretchDrawRGBMaskShapePortion(Dest: HDC; DstX, DstY, DstWidth, DstHeight: Integer;
Bitmap: TRGB8BitmapCore; DX, DY, DW, DH: Integer; BgPen: HPEN; FgPen: HPEN);
procedure DrawRGB8Bitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY, SrcWidth, SrcHeight: Integer;
Bitmap: TRGB8BitmapCore); overload;
type
TDrawPixelProcedure = procedure (X, Y: Integer) of Object;
TGetPixelFunction = function (X, Y: Integer): TRGB32Pixel of Object;
TSamePixelFunction = function (X, Y: Integer; Value: TRGB32Pixel): Boolean of Object;
procedure LineBresenham(X1, Y1, X2, Y2: Integer; DrawPixel: TDrawPixelProcedure);
procedure FillPixelRect(X1, Y1, X2, Y2: Integer; DrawPixel: TDrawPixelProcedure);
procedure NormalRectangle(X1, Y1, X2, Y2: Integer;
DrawOutlinePixel, DrawFillPixel: TDrawPixelProcedure);
procedure EllipticRectangle(X1, Y1, X2, Y2: Integer; LX, LY: Integer;
DrawOutlinePixel, DrawFillPixel: TDrawPixelProcedure);
procedure FloodFillScanLine(X, Y, W, H: Integer; GetPixel: TGetPixelFunction;
SamePixel: TSamePixelFunction; DrawPixel: TDrawPixelProcedure);
implementation
function GetDCClipRect(Dest: HDC): TRect;
begin
if GetClipBox(Dest, @Result) = ERROR then
begin
Result.TopLeft := Point(0, 0);
if not GetDeviceSize(Dest, Result.BottomRight) then
Result.BottomRight := Point(8000, 8000);
end;
end;
procedure DrawRGB32Bitmap(Dst: TRGB32BitmapCore; X, Y: Integer; Src: TRGB32BitmapCore);
var
SrcX, SrcWidth, SrcY, SrcHeight: Integer;
I: Integer;
PS, PD: PRGB32Pixel;
begin
if (Dst = nil) or (Src = nil) then Exit;
if (Dst.Width <= 0) or (Dst.Height <= 0) or (Src.Width <= 0) or (Src.Height <= 0) then Exit;
if (X >= Dst.Width) or (Y >= Dst.Height) then Exit;
if (X + Src.Width <= 0) or (Y + Src.Height <= 0) then Exit;
SrcX := 0;
SrcY := 0;
SrcWidth := Src.Width;
SrcHeight := Src.Height;
if X < 0 then
begin
SrcX := -X;
Inc(SrcWidth, X);
X := 0;
end;
if Y < 0 then
begin
SrcY := -Y;
Inc(SrcHeight, Y);
Y := 0;
end;
if X + SrcWidth > Dst.Width then
Dec(SrcWidth, X + SrcWidth - Dst.Width);
if Y + SrcHeight > Dst.Height then
Dec(SrcHeight, Y + SrcHeight - Dst.Height);
for I := 0 to Pred(SrcHeight) do
begin
PS := Src.Get32PixelPtrUnsafe(SrcX, SrcY + I);
PD := Dst.Get32PixelPtrUnsafe(X, Y + I);
Move(PS^, PD^, SrcWidth shl 2);
end;
end;
procedure DrawRGB8Bitmap(Dst: TRGB8BitmapCore; X, Y: Integer; Src: TRGB8BitmapCore);
var
SrcX, SrcWidth, SrcY, SrcHeight: Integer;
I: Integer;
PS, PD: PRGB8Pixel;
begin
if (Dst = nil) or (Src = nil) then Exit;
if (Dst.Width <= 0) or (Dst.Height <= 0) or (Src.Width <= 0) or (Src.Height <= 0) then Exit;
if (X >= Dst.Width) or (Y >= Dst.Height) then Exit;
if (X + Src.Width <= 0) or (Y + Src.Height <= 0) then Exit;
SrcX := 0;
SrcY := 0;
SrcWidth := Src.Width;
SrcHeight := Src.Height;
if X < 0 then
begin
SrcX := -X;
Inc(SrcWidth, X);
X := 0;
end;
if Y < 0 then
begin
SrcY := -Y;
Inc(SrcHeight, Y);
Y := 0;
end;
if X + SrcWidth > Dst.Width then
Dec(SrcWidth, X + SrcWidth - Dst.Width);
if Y + SrcHeight > Dst.Height then
Dec(SrcHeight, Y + SrcHeight - Dst.Height);
for I := 0 to Pred(SrcHeight) do
begin
PS := Src.Get8PixelPtrUnsafe(SrcX, SrcY + I);
PD := Dst.Get8PixelPtrUnsafe(X, Y + I);
Move(PS^, PD^, SrcWidth);
end;
end;
procedure StretchRGB32BitmapTrunc(Dst, Src: TRGB32BitmapCore);
var
Cols: TIntArray;
Rows: TIntArray;
X, Y, PX, TX, OX, PY, TY, OY: Integer;
I: Integer;
PD, PS, PDLine, PSLine: PRGB32Pixel;
DstDataWidth, DstDataHeight: Integer;
SrcDataWidth, SrcDataHeight: Integer;
SrcRowPixelStride, DstRowPixelStride: Integer;
begin
DstDataWidth := Dst.Width;
DstDataHeight := Dst.Height;
SrcDataWidth := Src.Width;
SrcDataHeight := Src.Height;
SrcRowPixelStride := Src.RowPixelStride;
DstRowPixelStride := Dst.RowPixelStride;
Cols := DivideTrunc(SrcDataWidth, DstDataWidth);
Rows := DivideTrunc(SrcDataHeight, DstDataHeight);
if DstDataWidth <= SrcDataWidth then
begin
PX := 0;
OX := 0;
for X := 0 to High(Cols) do
begin
TX := Cols[X];
Cols[X] := (PX + TX shr 1) - OX;
OX := (PX + TX shr 1);
Inc(PX, TX);
end;
end;
if DstDataHeight <= SrcDataHeight then
begin
PY := 0;
OY := 0;
for Y := 0 to High(Rows) do
begin
TY := Rows[Y];
Rows[Y] := (PY + Rows[Y] shr 1) - OY;
OY := (PY + TY shr 1);
Inc(PY, TY);
end;
end;
PD := PRGB32Pixel(Dst.Pixels);
PS := PRGB32Pixel(Src.Pixels);
for Y := 0 to High(Rows) do
begin
if DstDataHeight <= SrcDataHeight then
begin
Inc(PS, Rows[Y] * SrcRowPixelStride);
end;
PDLine := PD;
PSLine := PS;
if DstDataWidth > SrcDataWidth then
begin
for X := 0 to High(Cols) do
begin
if Cols[X] = 1 then
begin
PDLine^ := PSLine^;
Inc(PDLine);
end
else
begin
FillDWord(PDLine^, Cols[X], PSLine^);
Inc(PDLine, Cols[X]);
end;
Inc(PSLine);
end;
end
else
begin
for X := 0 to High(Cols) do
begin
Inc(PSLine, Cols[X]);
PDLine^ := PSLine^;
Inc(PDLine);
end;
end;
if DstDataHeight > SrcDataHeight then
begin
PDLine := PD;
Inc(PD, DstRowPixelStride);
for I := 2 to Rows[Y] do
begin
Move(PDLine^, PD^, DstDataWidth shl 2);
Inc(PD, DstRowPixelStride);
end;
Inc(PS, SrcRowPixelStride);
end
else
begin
Inc(PD, DstRowPixelStride);
end;
end;
end;
procedure StretchRGB8BitmapTrunc(Dst, Src: TRGB8BitmapCore);
var
Cols: TIntArray;
Rows: TIntArray;
X, Y, PX, TX, OX, PY, TY, OY: Integer;
I: Integer;
PD, PS, PDLine, PSLine: PRGB8Pixel;
DstDataWidth, DstDataHeight: Integer;
SrcDataWidth, SrcDataHeight: Integer;
SrcRowPixelStride, DstRowPixelStride: Integer;
begin
DstDataWidth := Dst.Width;
DstDataHeight := Dst.Height;
SrcDataWidth := Src.Width;
SrcDataHeight := Src.Height;
SrcRowPixelStride := Src.RowPixelStride;
DstRowPixelStride := Dst.RowPixelStride;
Cols := DivideTrunc(SrcDataWidth, DstDataWidth);
Rows := DivideTrunc(SrcDataHeight, DstDataHeight);
if DstDataWidth <= SrcDataWidth then
begin
PX := 0;
OX := 0;
for X := 0 to High(Cols) do
begin
TX := Cols[X];
Cols[X] := (PX + TX shr 1) - OX;
OX := (PX + TX shr 1);
Inc(PX, TX);
end;
end;
if DstDataHeight <= SrcDataHeight then
begin
PY := 0;
OY := 0;
for Y := 0 to High(Rows) do
begin
TY := Rows[Y];
Rows[Y] := (PY + Rows[Y] shr 1) - OY;
OY := (PY + TY shr 1);
Inc(PY, TY);
end;
end;
PD := PRGB8Pixel(Dst.Pixels);
PS := PRGB8Pixel(Src.Pixels);
for Y := 0 to High(Rows) do
begin
if DstDataHeight <= SrcDataHeight then
begin
Inc(PS, Rows[Y] * SrcRowPixelStride);
end;
PDLine := PD;
PSLine := PS;
if DstDataWidth > SrcDataWidth then
begin
for X := 0 to High(Cols) do
begin
if Cols[X] = 1 then
begin
PDLine^ := PSLine^;
Inc(PDLine);
end
else
begin
FillByte(PDLine^, Cols[X], PSLine^);
Inc(PDLine, Cols[X]);
end;
Inc(PSLine);
end;
end
else
begin
for X := 0 to High(Cols) do
begin
Inc(PSLine, Cols[X]);
PDLine^ := PSLine^;
Inc(PDLine);
end;
end;
if DstDataHeight > SrcDataHeight then
begin
PDLine := PD;
Inc(PD, DstRowPixelStride);
for I := 2 to Rows[Y] do
begin
Move(PDLine^, PD^, DstDataWidth);
Inc(PD, DstRowPixelStride);
end;
Inc(PS, SrcRowPixelStride);
end
else
begin
Inc(PD, DstRowPixelStride);
end;
end;
end;
procedure StretchRGB32BitmapTrunc(Dst: TRGB32BitmapCore;
DstX, DstY, DstWidth, DstHeight: Integer;
SrcX, SrcY, SrcWidth, SrcHeight: Integer; Src: TRGB32BitmapCore);
var
Cols: TIntArray;
Rows: TIntArray;
X, Y: Integer;
SX, SY, DX, DY: Integer;
I, J, C: Integer;
PD, PS, PDLine, PSLine: PRGB32Pixel;
DstDataWidth, DstDataHeight: Integer;
SrcDataWidth, SrcDataHeight: Integer;
SrcRowPixelStride, DstRowPixelStride: Integer;
begin
DstDataWidth := Dst.Width;
DstDataHeight := Dst.Height;
SrcDataWidth := Src.Width;
SrcDataHeight := Src.Height;
SrcRowPixelStride := Src.RowPixelStride;
DstRowPixelStride := Dst.RowPixelStride;
Cols := DivideTrunc(SrcWidth, DstWidth);
Rows := DivideTrunc(SrcHeight, DstHeight);
if DstWidth <= SrcWidth then
Cols := GetDifference(GetMidPoints(Cols));
if DstHeight <= SrcHeight then
Rows := GetDifference(GetMidPoints(Rows));
PD := Dst.Get32PixelPtrUnsafe(DstX, DstY);
PS := Src.Get32PixelPtrUnsafe(SrcX, SrcY);
DY := DstY;
SY := SrcY;
for Y := 0 to High(Rows) do
begin
if DstHeight <= SrcHeight then
begin
Inc(PS, Rows[Y] * SrcRowPixelStride);
Inc(SY, Rows[Y]);
end;
if DstHeight > SrcHeight then C := Rows[Y]
else C := 1;
for I := 1 to C do
begin
DX := DstX;
SX := SrcX;
PDLine := PD;
PSLine := PS;
if (SY >= 0) and (SY < SrcDataHeight) and (DY >= 0) and (DY < DstDataHeight) then
begin
if (DstWidth > SrcWidth) then
begin
for X := 0 to High(Cols) do
begin
if Cols[X] = 1 then
begin
if (SX >= 0) and (SX < SrcDataWidth) and (DX >= 0) and (DX < DstDataWidth) then
PDLine^ := PSLine^;
Inc(PDLine);
Inc(DX);
end
else
begin
if (SX >= 0) and (SX < SrcDataWidth) then
begin
if (DX + Cols[X] <= 0) or (DX >= DstDataWidth) then
begin
Inc(PDLine, Cols[X]);
Inc(DX, Cols[X]);
end
else
for J := 1 to Cols[X] do
begin
if (DX >= 0) and (DX < DstDataWidth) then
PDLine^ := PSLine^;
Inc(PDLine);
Inc(DX);
end;
end
else
begin
Inc(PDLine, Cols[X]);
Inc(DX, Cols[X]);
end;
end;
Inc(PSLine);
Inc(SX);
end;
end
else
begin
for X := 0 to High(Cols) do
begin
Inc(PSLine, Cols[X]);
Inc(SX, Cols[X]);
if (SX >= 0) and (SX < SrcDataWidth) and (DX >= 0) and (DX < DstDataWidth) then
PDLine^ := PSLine^;
Inc(PDLine);
Inc(DX);
end;
end;
end;
if DstHeight > SrcHeight then
begin
Inc(PD, DstRowPixelStride);
Inc(DY);
end;
end;
if DstHeight > SrcHeight then
begin
Inc(PS, SrcRowPixelStride);
Inc(SY);
end
else
begin
Inc(PD, DstRowPixelStride);
Inc(DY);
end;
end;
end;
// ! SrcX < 0, SrcY < 0, SrcX + SrcWidth > Bitmap.Width, SrcY + SrcHeight > Bitmap.Height
// ! results in mash
procedure DrawRGB32Bitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY, SrcWidth, SrcHeight: Integer;
Bitmap: TRGB32BitmapCore);
var
Clip: TRect;
begin
if (Bitmap = nil) or (Bitmap.Pixels = nil) then Exit;
if (Bitmap.Width <= 0) or (Bitmap.Height <= 0) then Exit;
if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
Clip := GetDCClipRect(Dest);
if (DstX >= Clip.Right) or (DstY >= Clip.Bottom) or
(DstX + SrcWidth < Clip.Left) or (DstY + SrcHeight < Clip.Top) then Exit;
// clipping:
ClipDimension(Clip.Left, Clip.Right, DstX, SrcX, SrcWidth);
ClipDimension(Clip.Top, Clip.Bottom, DstY, SrcY, SrcHeight);
WidgetSetDrawRGB32Bitmap(Dest, DstX, DstY, SrcX, SrcY, SrcWidth, SrcHeight, Bitmap);
end;
// ! SrcX < 0, SrcY < 0, SrcX + SrcWidth > Bitmap.Width, SrcY + SrcHeight > Bitmap.Height
// ! results in mash
procedure StretchDrawRGB32Bitmap(Dest: HDC; DstX, DstY, DstWidth, DstHeight: Integer;
SrcX, SrcY, SrcWidth, SrcHeight: Integer; Bitmap: TRGB32BitmapCore);
var
Clip: TRect;
{$IFDEF StretchRGB32}
Temp: TRGB32BitmapCore;
X, Y, W, H: Integer;
{$ENDIF}
begin
if (Bitmap = nil) or (Bitmap.Pixels = nil) then Exit;
if (Bitmap.Width <= 0) or (Bitmap.Height <= 0) then Exit;
if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
if (DstWidth <= 0) or (DstHeight <= 0) then Exit;
Clip := GetDCClipRect(Dest);
if (DstX >= Clip.Right) or (DstY >= Clip.Bottom) or
(DstX + DstWidth < Clip.Left) or (DstY + DstHeight < Clip.Top) then Exit;
if (DstWidth = SrcWidth) and (DstHeight = SrcHeight) then
begin
DrawRGB32Bitmap(Dest, DstX, DstY, SrcX, SrcY, SrcWidth, SrcHeight, Bitmap);
Exit;
end;
{$IFDEF StretchRGB32}
X := Max(Clip.Left, DstX);
Y := Max(Clip.Top, DstY);
W := Min(Clip.Right, DstX + DstWidth) - X;
H := Min(Clip.Bottom, DstY + DstHeight) - Y;
Temp := TRGB32BitmapCore.Create(W, H);
try
StretchRGB32BitmapTrunc(Temp,
DstX - X, DstY - Y, DstWidth, DstHeight,
SrcX, SrcY, SrcWidth, SrcHeight, Bitmap);
DrawRGB32Bitmap(Dest, X, Y, 0, 0, W, H, Temp);
finally
Temp.Free;
end;
{$ELSE}
WidgetSetStretchDrawRGB32Bitmap(Dest, DstX, DstY, DstWidth, DstHeight,
SrcX, SrcY, SrcWidth, SrcHeight, Bitmap);
{$ENDIF}
end;
procedure StretchDrawRGBMaskShapePortion(Dest: HDC; DstX, DstY, DstWidth, DstHeight: Integer;
Bitmap: TRGB8BitmapCore; DX, DY, DW, DH: Integer; BgPen: HPEN; FgPen: HPEN);
var
ZoomX, ZoomY: Single;
Clip: TRect;
procedure DrawMask(SX, SY, EX, EY: Integer);
var
X, Y: Integer;
P: PRGB8Pixel;
V1, V2: TRGB8Pixel;
A, B, C: Integer;
Temp: HGDIOBJ;
begin
if EX >= Bitmap.Width then EX := Pred(Bitmap.Width);
if EY >= Bitmap.Height then EY := Pred(Bitmap.Height);
Temp := SelectObject(Dest, BgPen);
try
for Y := SY to EY do
begin
if Pred(SX) >= 0 then V2 := Bitmap.Get8PixelPtr(Pred(SX), Y)^
else V2 := 0;
P := Bitmap.Get8PixelPtr(SX, Y);
for X := Pred(SX) to EX do
begin
V1 := V2;
if X < Pred(Bitmap.Width) then V2 := P^
else V2 := 0;
if (V1 = $FF) and (V2 <> $FF) then
begin
A := DstX + Round(Succ(X) * ZoomX);
B := DstY + Round(Y * ZoomY);
C := DstY + Round(Succ(Y) * ZoomY);
if ((X + Y) and $1) > 0 then
begin
SelectObject(Dest, BgPen);
MoveToEx(Dest, A, B, nil);
LineTo(Dest, A, C);
end
else
begin
SelectObject(Dest, FgPen);
MoveToEx(Dest, A, B, nil);
LineTo(Dest, A, C);
end;
end
else
if (V1 <> $FF) and (V2 = $FF) then
begin
A := DstX + Round(Succ(X) * ZoomX);
B := DstY + Round(Y * ZoomY);
C := DstY + Round(Succ(Y) * ZoomY);
if ((X + Y) and $1) > 0 then
begin
SelectObject(Dest, BgPen);
MoveToEx(Dest, A - 1, B, nil);
LineTo(Dest, A - 1, C);
end
else
begin
SelectObject(Dest, FgPen);
MoveToEx(Dest, A - 1, B, nil);
LineTo(Dest, A - 1, C);
end;
end;
Inc(P);
end;
end;
for X := SX to EX do
begin
if Pred(SY) >= 0 then V2 := Bitmap.Get8PixelPtr(X, Pred(SY))^
else V2 := 0;
P := Bitmap.Get8PixelPtr(X, SY);
for Y := Pred(SY) to EY do
begin
V1 := V2;
if Y < Pred(Bitmap.Height) then V2 := P^
else V2 := 0;
if (V1 = $FF) and (V2 <> $FF) then
begin
A := DstX + Round(X * ZoomX);
B := DstX + Round(Succ(X) * ZoomX);
C := DstY + Round(Succ(Y) * ZoomY);
if ((X + Y) and $1) > 0 then
begin
SelectObject(Dest, BgPen);
MoveToEx(Dest, A, C, nil);
LineTo(Dest, B, C);
end
else
begin
SelectObject(Dest, FgPen);
MoveToEx(Dest, A, C, nil);
LineTo(Dest, B, C);
end;
end
else
if (V1 <> $FF) and (V2 = $FF) then
begin
A := DstX + Round(X * ZoomX);
B := DstX + Round(Succ(X) * ZoomX);
C := DstY + Round(Succ(Y) * ZoomY);
if ((X + Y) and $1) > 0 then
begin
SelectObject(Dest, BgPen);
MoveToEx(Dest, A, C - 1, nil);
LineTo(Dest, B, C - 1);
end
else
begin
SelectObject(Dest, FgPen);
MoveToEx(Dest, A, C - 1, nil);
LineTo(Dest, B, C - 1);
end;
end;
Inc(P, Bitmap.RowPixelStride);
end;
end;
finally
SelectObject(Dest, Temp);
end;
end;
begin
if (Bitmap = nil) or (Bitmap.Pixels = nil) then Exit;
if (Bitmap.Width <= 0) or (Bitmap.Height <= 0) then Exit;
if (DstWidth <= 0) or (DstHeight <= 0) then Exit;
Clip := GetDCClipRect(Dest);
ZoomX := DstWidth / Bitmap.Width;
ZoomY := DstHeight / Bitmap.Height;
if (Floor(DstX + DX * ZoomX) >= Clip.Right) or
(Floor(DstY + DY * ZoomY) >= Clip.Bottom) or
(Ceil(DstX + (DX + DW) * ZoomX) < Clip.Left) or
(Ceil(DstY + (DY + DH) * ZoomY) < Clip.Top) then Exit;
DrawMask(DX, DY, Pred(DX + DW), Pred(DY + DH));
end;
// ! SrcX < 0, SrcY < 0, SrcX + SrcWidth > Bitmap.Width, SrcY + SrcHeight > Bitmap.Height
// ! results in mash
procedure DrawRGB8Bitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY, SrcWidth, SrcHeight: Integer;
Bitmap: TRGB8BitmapCore);
var
Clip: TRect;
begin
if (Bitmap = nil) or (Bitmap.Pixels = nil) then Exit;
if (Bitmap.Width <= 0) or (Bitmap.Height <= 0) then Exit;
if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
Clip := GetDCClipRect(Dest);
if (DstX >= Clip.Right) or (DstY >= Clip.Bottom) or
(DstX + SrcWidth < Clip.Left) or (DstY + SrcHeight < Clip.Top) then Exit;
// clipping:
ClipDimension(Clip.Left, Clip.Right, DstX, SrcX, SrcWidth);
ClipDimension(Clip.Top, Clip.Bottom, DstY, SrcY, SrcHeight);
WidgetSetDrawRGB8Bitmap(Dest, DstX, DstY, SrcX, SrcY, SrcWidth, SrcHeight, Bitmap);
end;
(*
LineBresenham - standard Bresenham's line plotting algorithm
Note: Result depends on order of points.
*)
procedure LineBresenham(X1, Y1, X2, Y2: Integer; DrawPixel: TDrawPixelProcedure);
var
Y, X: Integer;
DX, DY, SX, SY, E: Integer;
begin
DrawPixel(X1, Y1);
if (Y1 = Y2) and (X1 = X2) then Exit;
DX := X2 - X1;
DY := Y2 - Y1;
if DX < 0 then
begin
SX := -1;
DX := -DX;
end
else SX := 1;
if DY < 0 then
begin
SY := -1;
DY := -DY;
end
else SY := 1;
DX := DX shl 1;
DY := DY shl 1;
X := X1;
Y := Y1;
if DX > DY then
begin
E := DY - DX shr 1;
while X <> X2 do
begin
if E >= 0 then
begin
Inc(Y, SY);
Dec(E, DX);
end;
Inc(X, SX);
Inc(E, DY);
DrawPixel(X, Y);
end;
end
else
begin
E := DX - DY shr 1;
while Y <> Y2 do
begin
if E >= 0 then
begin
Inc(X, SX);
Dec(E, DY);
end;
Inc(Y, SY);
Inc(E, DX);
DrawPixel(X, Y);
end;
end;
end;
procedure FillPixelRect(X1, Y1, X2, Y2: Integer; DrawPixel: TDrawPixelProcedure);
var
X, Y: Integer;
begin
SortRect(X1, Y1, X2, Y2);
for Y := Y1 to Y2 do
for X := X1 to X2 do DrawPixel(X, Y);
end;
procedure FillPixelRow(X1, X2, Y: Integer; DrawPixel: TDrawPixelProcedure); inline;
var
X: Integer;
begin
MinMax(X1, X2);
for X := X1 to X2 do DrawPixel(X, Y);
end;
procedure NormalRectangle(X1, Y1, X2, Y2: Integer;
DrawOutlinePixel, DrawFillPixel: TDrawPixelProcedure);
var
X, Y: Integer;
begin
SortRect(X1, Y1, X2, Y2);
for X := X1 to X2 do DrawOutlinePixel(X, Y1);
if Y1 < Y2 then
for X := X1 to X2 do DrawOutlinePixel(X, Y2);
for Y := Succ(Y1) to Pred(Y2) do
begin
DrawOutlinePixel(X1, Y);
if X1 < X2 then DrawOutlinePixel(X2, Y);
for X := Succ(X1) to Pred(X2) do
DrawFillPixel(X, Y);
end;
end;
(*
EllipticRectangle - accurate elliptic rectangle plotting algorithm
LX, LY - length of straight section of elliptic rectangle
If both LX and LY are set to 0, the result is ellipse.
*)
procedure EllipticRectangle(X1, Y1, X2, Y2: Integer; LX, LY: Integer;
DrawOutlinePixel, DrawFillPixel: TDrawPixelProcedure);
var
CX, CY, CX1, CY1, A, B, NX, NY: Single;
X, Y, EX, EY: Integer;
LX1, LY1: Integer;
LX2, LY2: Integer;
DivSqrA, DivSqrB: Single;
I, J, S: Integer;
EdgeList: Array of TPoint;
procedure AddEdge(X, Y: Integer);
begin
if (EdgeList[Y].X = -1) or (X < EdgeList[Y].X) then
EdgeList[Y].X := X;
if (EdgeList[Y].Y = -1) or (X > EdgeList[Y].Y) then
EdgeList[Y].Y := X;
end;
begin
if (X1 = X2) and (Y1 = Y2) then
begin
DrawOutlinePixel(X1, Y1);
Exit;
end;
SortRect(X1, Y1, X2, Y2);
if (X2 - X1 = 1) or (Y2 - Y1 = 1) then
begin
FillPixelRect(X1, Y1, X2, Y2, DrawOutlinePixel);
Exit;
end;
if (LX > X2 - X1) or (LY > Y2 - Y1) then
begin
NormalRectangle(X1, Y1, X2, Y2, DrawOutlinePixel, DrawFillPixel);
Exit;
end;
SetLength(EdgeList, Ceil((Y2 - Y1 + 1) / 2));
for I := 0 to Pred(High(EdgeList)) do EdgeList[I] := Point(-1, -1);
EdgeList[High(EdgeList)] := Point(0, 0);
A := (X2 - X1 + 1 - LX) / 2;
B := (Y2 - Y1 + 1 - LY) / 2;
CX := (X2 + X1 + 1) / 2;
CY := (Y2 + Y1 + 1) / 2;
CX1 := X2 + 1 - A - Floor(CX);
CY1 := Y2 + 1 - B - Floor(CY);
EX := Floor(Sqr(A) / Sqrt(Sqr(A) + Sqr(B)) + Frac(A));
EY := Floor(Sqr(B) / Sqrt(Sqr(A) + Sqr(B)) + Frac(B));
DivSqrA := 1 / Sqr(A);
DivSqrB := 1 / Sqr(B);
NY := B;
AddEdge(Floor(CX1), Round(CY1 + B) - 1);
for X := 1 to Pred(EX) do
begin
NY := B * Sqrt(1 - Sqr(X + 0.5 - Frac(A)) * DivSqrA);
AddEdge(Floor(CX1) + X, Round(CY1 + NY) - 1);
end;
LX1 := Floor(CX1) + Pred(EX);
LY1 := Round(CY1 + NY) - 1;
NX := A;
AddEdge(Round(CX1 + A) - 1, Floor(CY1));
for Y := 1 to Pred(EY) do
begin
NX := A * Sqrt(1 - Sqr(Y + 0.5 - Frac(B)) * DivSqrB);
AddEdge(Round(CX1 + NX) - 1, Floor(CY1) + Y);
end;
LX2 := Round(CX1 + NX) - 1;
LY2 := Floor(CY1) + Pred(EY);
if Abs(LX1 - LX2) > 1 then
begin
if Abs(LY1 - LY2) > 1 then AddEdge(LX1 + 1, LY1 - 1)
else AddEdge(LX1 + 1, LY1);
end
else
if Abs(LY1 - LY2) > 1 then AddEdge(LX2, LY1 - 1);
for I := 0 to High(EdgeList) do
begin
if EdgeList[I].X = -1 then EdgeList[I] := Point(Round(CX1 + A) - 1, Round(CX1 + A) - 1)
else Break;
end;
for J := 0 to High(EdgeList) do
begin
if (J = 0) and (Frac(CY) > 0) then
begin
for I := EdgeList[J].X to EdgeList[J].Y do
begin
DrawOutlinePixel(Floor(CX) + I, Floor(CY) + J);
DrawOutlinePixel(Ceil(CX) - Succ(I), Floor(CY) + J);
end;
for I := Ceil(CX) - EdgeList[J].X to Floor(CX) + Pred(EdgeList[J].X) do
begin
DrawFillPixel(I, Floor(CY) + J);
end;
end
else
if (J = High(EdgeList)) then
begin
if Frac(CX) > 0 then S := -EdgeList[J].Y
else S := -Succ(EdgeList[J].Y);
for I := S to EdgeList[J].Y do
begin
DrawOutlinePixel(Floor(CX) + I, Floor(CY) + J);
DrawOutlinePixel(Floor(CX) + I, Ceil(CY) - Succ(J));
end;
end
else
begin
for I := EdgeList[J].X to EdgeList[J].Y do
begin
DrawOutlinePixel(Floor(CX) + I, Floor(CY) + J);
DrawOutlinePixel(Floor(CX) + I, Ceil(CY) - Succ(J));
if Floor(CX) + I <> Ceil(CX) - Succ(I) then
begin
DrawOutlinePixel(Ceil(CX) - Succ(I), Floor(CY) + J);
DrawOutlinePixel(Ceil(CX) - Succ(I), Ceil(CY) - Succ(J));
end;
end;
for I := Ceil(CX) - EdgeList[J].X to Floor(CX) + Pred(EdgeList[J].X) do
begin
DrawFillPixel(I, Floor(CY) + J);
DrawFillPixel(I, Ceil(CY) - Succ(J));
end;
end;
end;
end;
(*
FloodFillScanLine - 4-directional scan line stack based flood fill algorithm
with control of visited pixels.
*)
procedure FloodFillScanLine(X, Y, W, H: Integer; GetPixel: TGetPixelFunction;
SamePixel: TSamePixelFunction; DrawPixel: TDrawPixelProcedure);
var
S: TRGB32Pixel;
SX, EX, I: Integer;
Added: Boolean;
Visited: Array of Byte;
Stack: Array of Integer;
StackCount: Integer;
function CheckPixel(AX, AY: Integer): Boolean; inline;
begin
if Visited[AX + AY * W] = 1 then Result := False
else
begin
Result := SamePixel(AX, AY, S);
end;
end;
procedure Push(AX, AY: Integer); inline;
begin
if StackCount >= High(Stack) then SetLength(Stack, Length(Stack) shl 1);
Stack[StackCount] := AX or (AY shl 16);
Inc(StackCount);
end;
procedure Pop(var AX, AY: Integer); inline;
begin
Dec(StackCount);
AX := Stack[StackCount] and $FFFF;
AY := (Stack[StackCount] shr 16) and $FFFF;
end;
begin
if (X >= 0) and (X < W) and (Y >= 0) and (Y < H) then
begin
S := GetPixel(X, Y);
SetLength(Stack, 1);
StackCount := 0;
SetLength(Visited, W * H);
FillChar(Visited[0], Length(Visited), #0);
Push(X, Y);
repeat
Pop(X, Y);
if not CheckPixel(X, Y) then Continue;
SX := X;
while (SX > 0) and CheckPixel(Pred(SX), Y) do Dec(SX);
EX := X;
while (EX < Pred(W)) and CheckPixel(Succ(EX), Y) do Inc(EX);
FillChar(Visited[SX + Y * W], Succ(EX - SX), #1);
FillPixelRow(SX, EX, Y, DrawPixel);
Added := False;
if Y > 0 then
for I := SX to EX do
if CheckPixel(I, Pred(Y)) then
begin
if Added then Continue;
Push(I, Pred(Y));
Added := True;
end
else Added := False;
Added := False;
if Y < Pred(H) then
for I := SX to EX do
if CheckPixel(I, Succ(Y)) then
begin
if Added then Continue;
Push(I, Succ(Y));
Added := True;
end
else Added := False;
until StackCount <= 0;
end;
end;
end.