lazarus-ccr/components/rgbgraphics/rgbgraphics.pas
2008-06-28 14:24:08 +00:00

1026 lines
26 KiB
ObjectPascal

{
/***************************************************************************
RGBGraphics.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:
TRGB32Bitmap is a memory image which allows fast pixel manipulations.
TRGB32Canvas is a TRGB32Bitmap canvas for drawing primitives and
drawing bitmap image into TCanvas.
}
unit RGBGraphics;
{$ifdef fpc}
{$mode objfpc}{$H+}
{$endif}
interface
uses
Classes, SysUtils, LCLIntf, FPWriteBMP,
LCLType, LCLProc, FPImage, LResources, IntfGraphics,
GraphType, Graphics, Forms, Math, Clipbrd,
RGBTypes, RGBRoutines, RGBUtils;
type
TEraseMode = (ermNone, ermErase, ermReplace);
TDrawMode = (dmFillAndOutline, dmOutline, dmFill);
TRandomDensity = Word;
const
MAXRANDOMDENSITY = $FFFF;
type
TMaskFillMode = (mfAdd, mfRemove, mfXOR);
{ TRGBMask }
TRGBMask = class(TRGB8BitmapCore)
private
FBGPen: TPen;
FFGPen: TPen;
FFillMode: TMaskFillMode;
FMaskedPixels: Integer;
protected
procedure AddPixel(X, Y: Integer);
procedure RemovePixel(X, Y: Integer);
procedure XORPixel(X, Y: Integer);
procedure CreatePens; virtual;
function GetFillProcedure: TDrawPixelProcedure; virtual;
function GetMaskedPixelsCount: Integer;
public
constructor Create(AWidth, AHeight: Integer); override;
constructor CreateAsCopy(ABitmap: TRGBBitmapCore); override;
destructor Destroy; override;
procedure LoadFromLazIntfImageAlpha(AImage: TLazIntfImage); override;
procedure SwapWith(ABitmap: TRGBBitmapCore); override;
procedure Assign(Source: TPersistent); override;
procedure UpdateMaskedPixels;
procedure Draw(X, Y: Integer; AMask: TRGBMask);
procedure DrawShapeTo(ACanvas: TCanvas; X, Y: Integer);
procedure StretchDrawShapeTo(ACanvas: TCanvas; DstX, DstY, DstWidth, DstHeight: Integer);
procedure StretchDrawShapePortionTo(ACanvas: TCanvas; DstX, DstY, DstWidth, DstHeight: Integer;
DX, DY, DW, DH: Integer);
procedure DrawTo(ACanvas: TCanvas; X, Y: Integer);
procedure StretchTrunc(AWidth, AHeight: Integer); virtual;
procedure Rectangle(X1, Y1, X2, Y2: Integer);
procedure Ellipse(X1, Y1, X2, Y2: Integer);
procedure Clear; override;
procedure ClearWhite; override;
procedure Invert; override;
public
function IsEmpty: Boolean;
function GetMaskedRect: TRect;
property BackgroundPen: TPen read FBGPen;
property ForegroundPen: TPen read FFGPen;
property FillMode: TMaskFillMode read FFillMode write FFillMode;
end;
{ TRGB32Canvas }
TRGB32Canvas = class
private
FDrawMode: TDrawMode;
FEraseMode: TEraseMode;
FFillColor: TRGB32Pixel;
FFloodFillTolerance: TPixelDifference;
FOutlineColor: TRGB32Pixel;
FOwner: TRGB32BitmapCore;
FPaperColor: TRGB32Pixel;
FRandomDensity: TRandomDensity;
FRandomEnabled: Boolean;
FRectangleRoundness: Integer;
function GetFillColor: TColor;
function GetOutlineColor: TColor;
function GetPaperColor: TColor;
procedure SetFillColor(const AValue: TColor);
procedure SetOutlineColor(const AValue: TColor);
procedure SetPaperColor(const AValue: TColor);
protected
function PixelMasked(X, Y: Integer): Boolean;
function SamePixelUnsafe(X, Y: Integer; Value: TRGB32Pixel): Boolean;
function SamePixelUnmasked(X, Y: Integer; Value: TRGB32Pixel): Boolean;
procedure DrawOutlinePixel(X, Y: Integer);
procedure DrawFillPixel(X, Y: Integer);
procedure DrawPaperPixel(X, Y: Integer);
procedure DrawReplacePixel(X, Y: Integer);
procedure DrawRandomOutlinePixel(X, Y: Integer);
procedure DrawRandomFillPixel(X, Y: Integer);
procedure DrawRandomPaperPixel(X, Y: Integer);
procedure DrawEmptyPixel(X, Y: Integer);
function GetOutlineProcedure: TDrawPixelProcedure; virtual;
function GetFillProcedure: TDrawPixelProcedure; virtual;
public
constructor Create(AOwner: TRGB32BitmapCore);
procedure SetColor(X, Y: Integer; Value: TColor);
function GetColor(X, Y: Integer): TColor;
procedure Fill(Color: TColor);
procedure FillRect(X1, Y1, X2, Y2: Integer);
procedure FillEllipse(X1, Y1, X2, Y2: Integer);
procedure Line(X1, Y1, X2, Y2: Integer);
procedure Rectangle(X1, Y1, X2, Y2: Integer);
procedure Ellipse(X1, Y1, X2, Y2: Integer);
procedure FloodFill(X, Y: Integer);
procedure MaskFloodFill(X, Y: Integer);
public
procedure DrawTo(ACanvas: TCanvas; X, Y: Integer);
procedure StretchDrawTo(ACanvas: TCanvas; DstX, DstY, DstWidth, DstHeight: Integer);
public
property EraseMode: TEraseMode read FEraseMode write FEraseMode;
property DrawMode: TDrawMode read FDrawMode write FDrawMode;
property FloodFillTolerance: TPixelDifference read FFloodFillTolerance
write FFloodFillTolerance;
property FillColor: TColor read GetFillColor write SetFillColor;
property OutlineColor: TColor read GetOutlineColor write SetOutlineColor;
property PaperColor: TColor read GetPaperColor write SetPaperColor;
property RandomEnabled: Boolean read FRandomEnabled write FRandomEnabled;
property RandomDensity: TRandomDensity read FRandomDensity write FRandomDensity;
property RectangleRoundness: Integer read FRectangleRoundness write FRectangleRoundness;
end;
TSmoothMethod = (smAreaPixel, smBilinear, smBicubic);
{ TRGB32Bitmap }
TRGB32Bitmap = class(TRGB32BitmapCore)
private
FCanvas: TRGB32Canvas;
FMask: TRGBMask;
protected
function CreateDefaultLazIntfImage: TLazIntfImage;
public
constructor Create(AWidth, AHeight: Integer); override;
constructor CreateAsCopy(ABitmap: TRGBBitmapCore); override;
constructor CreateFromLazIntfImage(AImage: TLazIntfImage); override;
constructor CreateFromFile(const FileName: String); virtual;
constructor CreateFromBitmap(ABitmap: TRasterImage); virtual;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure SwapWith(ABitmap: TRGBBitmapCore); override;
procedure SaveToLazIntfImage(AImage: TLazIntfImage; const ARect: TRect); override;
procedure SaveToStream(Stream: TStream); virtual;
procedure SaveToStream(Stream: TStream; AWriterClass: TFPCustomImageWriterClass); virtual;
procedure SaveToStream(Stream: TStream; const ARect: TRect;
AWriterClass: TFPCustomImageWriterClass); virtual;
procedure SaveToFile(const FileName: String); virtual;
procedure SaveToLazarusResource(const FileName, Name: String); virtual;
public
procedure Draw(X, Y: Integer; ABitmap: TRGB32Bitmap);
procedure StretchTrunc(AWidth, AHeight: Integer); virtual;
procedure StretchSmooth(AWidth, AHeight: Integer; Method: TSmoothMethod); virtual;
procedure Grayscale; virtual;
procedure Disable; virtual;
procedure CutToClipboard; virtual;
procedure CopyToClipboard; virtual;
procedure Delete; virtual;
procedure FlipHorz; override;
procedure FlipVert; override;
procedure Rotate90; override;
procedure Rotate180; override;
procedure Rotate270; override;
public
property Canvas: TRGB32Canvas read FCanvas;
property Mask: TRGBMask read FMask write FMask;
end;
implementation
function AbsByte(Src: Integer): Byte; inline;
begin
if Src >= 0 then Result := Src
else Result := -Src;
end;
function RGB32PixelDifference(A, B: TRGB32Pixel): TPixelDifference; inline;
begin
Result := AbsByte(((A shr 16) and $FF) - ((B shr 16) and $FF))
+ AbsByte(((A shr 8) and $FF) - ((B shr 8) and $FF))
+ AbsByte((A and $FF) - (B and $FF));
end;
{ TRGB32Bitmap }
function TRGB32Bitmap.CreateDefaultLazIntfImage: TLazIntfImage;
var
RID: TRawImageDescription;
DC: HDC;
begin
DC := GetDC(0);
try
RawImage_DescriptionFromDevice(DC, RID);
finally
ReleaseDC(0, DC);
end;
Result := TLazIntfImage.Create(0, 0);
Result.DataDescription := RID;
end;
constructor TRGB32Bitmap.Create(AWidth, AHeight: Integer);
begin
inherited;
FCanvas := TRGB32Canvas.Create(Self);
FMask := TRGBMask.Create(AWidth, AHeight);
end;
constructor TRGB32Bitmap.CreateAsCopy(ABitmap: TRGBBitmapCore);
begin
inherited;
FCanvas := TRGB32Canvas.Create(Self);
if ABitmap is TRGB32Bitmap then
FMask := TRGBMask.CreateAsCopy((ABitmap as TRGB32Bitmap).Mask)
else
FMask := TRGBMask.Create(ABitmap.Width, ABitmap.Height);
end;
constructor TRGB32Bitmap.CreateFromLazIntfImage(AImage: TLazIntfImage);
begin
inherited CreateFromLazIntfImage(AImage);
FMask.LoadFromLazIntfImageAlpha(AImage);
end;
constructor TRGB32Bitmap.CreateFromFile(const FileName: String);
var
Image: TLazIntfImage;
begin
Image := CreateDefaultLazIntfImage;
try
Image.LoadFromFile(FileName);
CreateFromLazIntfImage(Image);
finally
Image.Free;
end;
end;
constructor TRGB32Bitmap.CreateFromBitmap(ABitmap: TRasterImage);
var
Image: TLazIntfImage;
begin
Image := ABitmap.CreateIntfImage;
try
CreateFromLazIntfImage(Image);
finally
Image.Free;
end;
end;
destructor TRGB32Bitmap.Destroy;
begin
FCanvas.Free;
FMask.Free;
inherited;
end;
procedure TRGB32Bitmap.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TRGB32Bitmap then
begin
Mask.Assign((Source as TRGB32Bitmap).Mask);
end;
end;
procedure TRGB32Bitmap.SwapWith(ABitmap: TRGBBitmapCore);
begin
inherited SwapWith(ABitmap);
if ABitmap is TRGB32Bitmap then
begin
Mask.SwapWith((ABitmap as TRGB32Bitmap).Mask);
end;
end;
procedure TRGB32Bitmap.SaveToLazIntfImage(AImage: TLazIntfImage; const ARect: TRect);
begin
inherited SaveToLazIntfImage(AImage, ARect);
if not Mask.IsEmpty then FMask.SaveToLazIntfImageAlpha(AImage, ARect);
end;
procedure TRGB32Bitmap.SaveToStream(Stream: TStream);
begin
SaveToStream(Stream, Bounds(0, 0, Width, Height), TLazWriterXPM);
end;
procedure TRGB32Bitmap.SaveToStream(Stream: TStream;
AWriterClass: TFPCustomImageWriterClass);
begin
SaveToStream(Stream, Bounds(0, 0, Width, Height), AWriterClass);
end;
procedure TRGB32Bitmap.SaveToStream(Stream: TStream; const ARect: TRect;
AWriterClass: TFPCustomImageWriterClass);
var
Image: TLazIntfImage;
Writer: TFPCustomImageWriter;
begin
Image := CreateDefaultLazIntfImage;
Writer := AWriterClass.Create;
try
SaveToLazIntfImage(Image, ARect);
Image.SaveToStream(Stream, Writer);
finally
Writer.Free;
Image.Free;
end;
end;
procedure TRGB32Bitmap.SaveToFile(const FileName: String);
var
Image: TLazIntfImage;
begin
Image := CreateDefaultLazIntfImage;
try
inherited SaveToLazIntfImage(Image);
Image.SaveToFile(FileName);
finally
Image.Free;
end;
end;
procedure TRGB32Bitmap.SaveToLazarusResource(const FileName, Name: String);
var
PixmapStream, ResourceStream: TMemoryStream;
FileStream: TFileStream;
begin
PixmapStream := TMemoryStream.Create;
ResourceStream := TMemoryStream.Create;
try
SaveToStream(PixmapStream);
PixmapStream.Position := 0;
BinaryToLazarusResourceCode(PixmapStream, ResourceStream, Name, 'XPM');
ResourceStream.Position := 0;
FileStream := TFileStream.Create(FileName, fmCreate);
try
FileStream.CopyFrom(ResourceStream, ResourceStream.Size);
finally
FileStream.Free;
end;
finally
PixmapStream.Free;
ResourceStream.Free;
end;
end;
procedure TRGB32Bitmap.Draw(X, Y: Integer; ABitmap: TRGB32Bitmap);
begin
DrawRGB32Bitmap(Self, X, Y, ABitmap);
end;
procedure TRGB32Bitmap.StretchTrunc(AWidth, AHeight: Integer);
var
Result: TRGB32Bitmap;
begin
if (AWidth = Width) and (AHeight = Height) then Exit;
Result := TRGB32Bitmap.Create(AWidth, AHeight);
try
StretchRGB32BitmapTrunc(Result, Self);
inherited SwapWith(Result);
Mask.StretchTrunc(AWidth, AHeight);
finally
FreeAndNil(Result);
end;
end;
procedure TRGB32Bitmap.StretchSmooth(AWidth, AHeight: Integer; Method: TSmoothMethod);
begin
//
end;
procedure TRGB32Bitmap.Grayscale;
begin
GrayscaleRGB32Bitmap(Self);
end;
procedure TRGB32Bitmap.Disable;
begin
DisableRGB32Bitmap(Self);
end;
procedure TRGB32Bitmap.CutToClipboard;
begin
CopyToClipboard;
Delete;
end;
procedure TRGB32Bitmap.CopyToClipboard;
var
PixmapStream, BitmapStream: TMemoryStream;
PixmapWriter, BitmapWriter: TFPCustomImageWriter;
Image: TLazIntfImage;
R: TRect;
begin
PixmapStream := TMemoryStream.Create;
BitmapStream := TMemoryStream.Create;
Image := CreateDefaultLazIntfImage;
PixmapWriter := TLazWriterXPM.Create;
BitmapWriter := TFPWriterBMP.Create;
try
R := Mask.GetMaskedRect;
SaveToLazIntfImage(Image, R);
Clipboard.Open;
try
Clipboard.Clear;
Image.SaveToStream(PixmapStream, PixmapWriter);
Clipboard.AddFormat(PredefinedClipboardFormat(pcfPixmap), PixmapStream);
Image.SaveToStream(BitmapStream, BitmapWriter);
Clipboard.AddFormat(PredefinedClipboardFormat(pcfBitmap), BitmapStream);
finally
Clipboard.Close;
end;
finally
PixmapStream.Free;
BitmapStream.Free;
Image.Free;
PixmapWriter.Free;
BitmapWriter.Free;
end;
end;
procedure TRGB32Bitmap.Delete;
begin
Canvas.Fill(Canvas.PaperColor);
end;
procedure TRGB32Bitmap.FlipHorz;
begin
inherited FlipHorz;
Mask.FlipHorz;
end;
procedure TRGB32Bitmap.FlipVert;
begin
inherited FlipVert;
Mask.FlipVert;
end;
procedure TRGB32Bitmap.Rotate90;
begin
inherited Rotate90;
Mask.Rotate90;
end;
procedure TRGB32Bitmap.Rotate180;
begin
inherited Rotate180;
Mask.Rotate180;
end;
procedure TRGB32Bitmap.Rotate270;
begin
inherited Rotate270;
Mask.Rotate270;
end;
{ TRGB32Canvas }
constructor TRGB32Canvas.Create(AOwner: TRGB32BitmapCore);
begin
inherited Create;
FOwner := AOwner;
FRandomDensity := MAXRANDOMDENSITY;
FFloodFillTolerance := 0;
FRectangleRoundness := 0;
end;
procedure TRGB32Canvas.SetColor(X, Y: Integer; Value: TColor);
begin
FOwner.Set32Pixel(X, Y, ColorToRGB32Pixel(Value));
end;
function TRGB32Canvas.GetColor(X, Y: Integer): TColor;
var
P: PRGB32Pixel;
begin
P := FOwner.Get32PixelPtr(X, Y);
if P <> nil then Result := RGB32PixelToColor(P^)
else Result := clNone;
end;
function TRGB32Canvas.GetFillColor: TColor;
begin
Result := RGB32PixelToColor(FFillColor);
end;
function TRGB32Canvas.GetOutlineColor: TColor;
begin
Result := RGB32PixelToColor(FOutlineColor);
end;
function TRGB32Canvas.GetPaperColor: TColor;
begin
Result := RGB32PixelToColor(FPaperColor);
end;
procedure TRGB32Canvas.SetFillColor(const AValue: TColor);
begin
FFillColor := ColorToRGB32Pixel(AValue);
end;
procedure TRGB32Canvas.SetOutlineColor(const AValue: TColor);
begin
FOutlineColor := ColorToRGB32Pixel(AValue);
end;
procedure TRGB32Canvas.SetPaperColor(const AValue: TColor);
begin
FPaperColor := ColorToRGB32Pixel(AValue);
end;
function TRGB32Canvas.PixelMasked(X, Y: Integer): Boolean;
var
P: PRGB8Pixel;
begin
if not (FOwner is TRGB32Bitmap) then Result := True
else
if (FOwner as TRGB32Bitmap).Mask.IsEmpty then Result := True
else
begin
P := (FOwner as TRGB32Bitmap).Mask.Get8PixelPtr(X, Y);
Result := (P <> nil) and (P^ = $FF);
end;
end;
function TRGB32Canvas.SamePixelUnsafe(X, Y: Integer; Value: TRGB32Pixel): Boolean;
begin
Result := PixelMasked(X, Y) and (RGB32PixelDifference(FOwner.Get32PixelUnsafe(X, Y), Value)
<= FFloodFillTolerance);
end;
function TRGB32Canvas.SamePixelUnmasked(X, Y: Integer; Value: TRGB32Pixel): Boolean;
begin
Result := RGB32PixelDifference(FOwner.Get32PixelUnsafe(X, Y), Value)
<= FFloodFillTolerance;
end;
procedure TRGB32Canvas.DrawOutlinePixel(X, Y: Integer);
begin
if PixelMasked(X, Y) then FOwner.Set32Pixel(X, Y, FOutlineColor);
end;
procedure TRGB32Canvas.DrawFillPixel(X, Y: Integer);
begin
if PixelMasked(X, Y) then FOwner.Set32Pixel(X, Y, FFillColor);
end;
procedure TRGB32Canvas.DrawPaperPixel(X, Y: Integer);
begin
if PixelMasked(X, Y) then FOwner.Set32Pixel(X, Y, FPaperColor);
end;
procedure TRGB32Canvas.DrawReplacePixel(X, Y: Integer);
var
P: PRGB32Pixel;
begin
if not PixelMasked(X, Y) then Exit;
P := FOwner.Get32PixelPtr(X, Y);
if (P <> nil) and (P^ = FFillColor) then P^ := FPaperColor;
end;
procedure TRGB32Canvas.DrawRandomOutlinePixel(X, Y: Integer);
begin
if PixelMasked(X, Y) and (Random(MAXRANDOMDENSITY) < FRandomDensity) then
FOwner.Set32Pixel(X, Y, FOutlineColor);
end;
procedure TRGB32Canvas.DrawRandomFillPixel(X, Y: Integer);
begin
if PixelMasked(X, Y) and (Random(MAXRANDOMDENSITY) < FRandomDensity) then
FOwner.Set32Pixel(X, Y, FFillColor);
end;
procedure TRGB32Canvas.DrawRandomPaperPixel(X, Y: Integer);
begin
if PixelMasked(X, Y) and (Random(MAXRANDOMDENSITY) < FRandomDensity) then
FOwner.Set32Pixel(X, Y, FPaperColor);
end;
procedure TRGB32Canvas.DrawEmptyPixel(X, Y: Integer);
begin
//
end;
function TRGB32Canvas.GetOutlineProcedure: TDrawPixelProcedure;
begin
if not FRandomEnabled then
begin
case DrawMode of
dmFillAndOutline, dmOutline:
begin
case EraseMode of
ermNone: Result := @DrawOutlinePixel;
ermErase: Result := @DrawPaperPixel;
ermReplace: Result := @DrawReplacePixel;
end;
end;
else
Result := @DrawEmptyPixel;
end;
end
else
begin
case EraseMode of
ermNone: Result := @DrawRandomFillPixel;
ermErase: Result := @DrawRandomPaperPixel;
ermReplace: Result := @DrawRandomFillPixel;
end;
end;
end;
function TRGB32Canvas.GetFillProcedure: TDrawPixelProcedure;
begin
if not FRandomEnabled then
begin
case DrawMode of
dmFillAndOutline, dmFill:
begin
case EraseMode of
ermNone: Result := @DrawFillPixel;
ermErase: Result := @DrawPaperPixel;
ermReplace: Result := @DrawReplacePixel;
end;
end;
else
Result := @DrawEmptyPixel;
end;
end
else
begin
case EraseMode of
ermNone: Result := @DrawRandomFillPixel;
ermErase: Result := @DrawRandomPaperPixel;
ermReplace: Result := @DrawRandomFillPixel;
end;
end;
end;
procedure TRGB32Canvas.Fill(Color: TColor);
var
P: PRGB32Pixel;
C: TRGB32Pixel;
PM: PRGB8Pixel;
I, J: Integer;
begin
C := ColorToRGB32Pixel(Color);
for J := 0 to Pred(FOwner.Height) do
begin
P := FOwner.Get32PixelPtr(0, J);
if (FOwner is TRGB32Bitmap) and not (FOwner as TRGB32Bitmap).Mask.IsEmpty then
begin
PM := (FOwner as TRGB32Bitmap).Mask.Get8PixelPtr(0, J);
for I := 0 to Pred(FOwner.Width) do
begin
if PM^ = $FF then P^ := C;
Inc(P);
Inc(PM);
end;
end
else FillDWord(P^, FOwner.Width, C);
end;
end;
procedure TRGB32Canvas.FillRect(X1, Y1, X2, Y2: Integer);
begin
FillPixelRect(X1, Y1, X2, Y2, GetFillProcedure);
end;
procedure TRGB32Canvas.FillEllipse(X1, Y1, X2, Y2: Integer);
begin
EllipticRectangle(X1, Y1, X2, Y2, 0, 0, GetFillProcedure, GetFillProcedure);
end;
procedure TRGB32Canvas.Line(X1, Y1, X2, Y2: Integer);
begin
LineBresenham(X1, Y1, X2, Y2, GetOutlineProcedure);
end;
procedure TRGB32Canvas.Rectangle(X1, Y1, X2, Y2: Integer);
var
R1, R2: Integer;
begin
R1 := Max(0, Succ(Abs(X2 - X1)) - RectangleRoundness);
R2 := Max(0, Succ(Abs(Y2 - Y1)) - RectangleRoundness);
EllipticRectangle(X1, Y1, X2, Y2, R1, R2, GetOutlineProcedure, GetFillProcedure);
end;
procedure TRGB32Canvas.Ellipse(X1, Y1, X2, Y2: Integer);
begin
EllipticRectangle(X1, Y1, X2, Y2, 0, 0, GetOutlineProcedure, GetFillProcedure);
end;
procedure TRGB32Canvas.FloodFill(X, Y: Integer);
begin
case EraseMode of
ermNone: FloodFillScanLine(X, Y, FOwner.Width, FOwner.Height, @FOwner.Get32PixelUnsafe,
@SamePixelUnsafe, @DrawFillPixel);
ermErase: FloodFillScanLine(X, Y, FOwner.Width, FOwner.Height, @FOwner.Get32PixelUnsafe,
@SamePixelUnsafe, @DrawPaperPixel);
end;
end;
procedure TRGB32Canvas.MaskFloodFill(X, Y: Integer);
begin
if not (FOwner is TRGB32Bitmap) then Exit;
FloodFillScanLine(X, Y, FOwner.Width, FOwner.Height, @FOwner.Get32PixelUnsafe,
@SamePixelUnmasked, (FOwner as TRGB32Bitmap).Mask.GetFillProcedure);;
end;
procedure TRGB32Canvas.DrawTo(ACanvas: TCanvas; X, Y: Integer);
begin
if ACanvas <> nil then
with FOwner do
DrawRGB32Bitmap(ACanvas.Handle, X, Y, 0, 0, Width, Height, FOwner);
end;
procedure TRGB32Canvas.StretchDrawTo(ACanvas: TCanvas; DstX, DstY, DstWidth,
DstHeight: Integer);
begin
if ACanvas <> nil then
with FOwner do
StretchDrawRGB32Bitmap(ACanvas.Handle, DstX, DstY, DstWidth, DstHeight,
0, 0, Width, Height, FOwner);
end;
{ TRGBMask }
procedure TRGBMask.AddPixel(X, Y: Integer);
var
P: PRGB8Pixel;
begin
P := Get8PixelPtr(X, Y);
if P <> nil then
begin
if P^ <> $FF then Inc(FMaskedPixels);
P^ := $FF;
end;
end;
procedure TRGBMask.RemovePixel(X, Y: Integer);
var
P: PRGB8Pixel;
begin
P := Get8PixelPtr(X, Y);
if P <> nil then
begin
if P^ = $FF then Dec(FMaskedPixels);
P^ := 0;
end;
end;
procedure TRGBMask.XORPixel(X, Y: Integer);
var
P: PRGB8Pixel;
begin
P := Get8PixelPtr(X, Y);
if P <> nil then
begin
if P^ = 0 then Inc(FMaskedPixels);
if P^ = $FF then Dec(FMaskedPixels);
P^ := $FF - P^;
end;
end;
procedure TRGBMask.CreatePens;
begin
FBGPen := TPen.Create;
FBGPen.Color := clYellow;
FFGPen := TPen.Create;
FFGPen.Color := clBlue;
//FFGPen.Style := psDot;
end;
function TRGBMask.GetFillProcedure: TDrawPixelProcedure;
begin
case FillMode of
mfAdd: Result := @AddPixel;
mfRemove: Result := @RemovePixel;
mfXOR: Result := @XORPixel;
end;
end;
function TRGBMask.GetMaskedPixelsCount: Integer;
var
I, J: Integer;
P: PRGB8Pixel;
begin
Result := 0;
for J := 0 to Pred(Height) do
begin
P := Get8PixelPtr(0, J);
for I := 0 to Pred(Width) do
begin
if P^ = $FF then Inc(Result);
Inc(P);
end;
end;
end;
constructor TRGBMask.Create(AWidth, AHeight: Integer);
begin
inherited Create(AWidth, AHeight);
Clear;
CreatePens;
end;
constructor TRGBMask.CreateAsCopy(ABitmap: TRGBBitmapCore);
begin
inherited CreateAsCopy(ABitmap);
UpdateMaskedPixels;
CreatePens;
end;
procedure TRGBMask.SwapWith(ABitmap: TRGBBitmapCore);
begin
inherited SwapWith(ABitmap);
UpdateMaskedPixels;
end;
procedure TRGBMask.Assign(Source: TPersistent);
begin
inherited Assign(Source);
UpdateMaskedPixels;
end;
procedure TRGBMask.UpdateMaskedPixels;
begin
FMaskedPixels := GetMaskedPixelsCount;
end;
procedure TRGBMask.Draw(X, Y: Integer; AMask: TRGBMask);
begin
DrawRGB8Bitmap(Self, X, Y, AMask);
UpdateMaskedPixels;
end;
destructor TRGBMask.Destroy;
begin
FBGPen.Free;
FFGPen.Free;
inherited Destroy;
end;
procedure TRGBMask.LoadFromLazIntfImageAlpha(AImage: TLazIntfImage);
begin
inherited LoadFromLazIntfImageAlpha(AImage);
UpdateMaskedPixels;
end;
procedure TRGBMask.DrawShapeTo(ACanvas: TCanvas; X, Y: Integer);
begin
StretchDrawShapeTo(ACanvas, X, Y, Width, Height);
end;
procedure TRGBMask.StretchDrawShapeTo(ACanvas: TCanvas; DstX, DstY, DstWidth,
DstHeight: Integer);
begin
StretchDrawShapePortionTo(ACanvas, DstX, DstY, DstWidth, DstHeight,
0, 0, Width, Height);
end;
procedure TRGBMask.StretchDrawShapePortionTo(ACanvas: TCanvas; DstX, DstY,
DstWidth, DstHeight: Integer; DX, DY, DW, DH: Integer);
begin
if ACanvas <> nil then
StretchDrawRGBMaskShapePortion(ACanvas.Handle, DstX, DstY, DstWidth, DstHeight,
Self, DX, DY, DW, DH, FBGPen.Reference.Handle, FFGPen.Reference.Handle);
end;
procedure TRGBMask.DrawTo(ACanvas: TCanvas; X, Y: Integer);
begin
if ACanvas <> nil then
DrawRGB8Bitmap(ACanvas.Handle, X, Y, 0, 0, Width, Height, Self);
end;
procedure TRGBMask.StretchTrunc(AWidth, AHeight: Integer);
var
Result: TRGBMask;
begin
if (AWidth = Width) and (AHeight = Height) then Exit;
Result := TRGBMask.Create(AWidth, AHeight);
try
StretchRGB8BitmapTrunc(Result, Self);
SwapWith(Result);
FMaskedPixels := GetMaskedPixelsCount;
finally
FreeAndNil(Result);
end;
end;
procedure TRGBMask.Rectangle(X1, Y1, X2, Y2: Integer);
begin
FillPixelRect(X1, Y1, X2, Y2, GetFillProcedure);
end;
procedure TRGBMask.Ellipse(X1, Y1, X2, Y2: Integer);
begin
EllipticRectangle(X1, Y1, X2, Y2, 0, 0, GetFillProcedure, GetFillProcedure);
end;
procedure TRGBMask.Clear;
begin
inherited Clear;
FMaskedPixels := 0;
end;
procedure TRGBMask.ClearWhite;
begin
inherited ClearWhite;
FMaskedPixels := Width * Height;
end;
procedure TRGBMask.Invert;
begin
inherited Invert;
FMaskedPixels := Width * Height - FMaskedPixels;
end;
function TRGBMask.IsEmpty: Boolean;
begin
Result := FMaskedPixels = 0;
end;
function TRGBMask.GetMaskedRect: TRect;
var
I, J: Integer;
LineMasked: Boolean;
P: PRGB8Pixel;
begin
Result := Rect(Width, Height, 0, 0);
for J := 0 to Pred(Height) do
begin
P := Get8PixelPtr(0, J);
LineMasked := False;
for I := 0 to Pred(Width) do
begin
if P^ = $FF then
begin
LineMasked := True;
if I < Result.Left then Result.Left := I;
if Succ(I) > Result.Right then Result.Right := Succ(I);
end;
Inc(P);
end;
if LineMasked then
begin
if J < Result.Top then Result.Top := J;
if Succ(J) > Result.Bottom then Result.Bottom := Succ(J);
end;
end;
SortRect(Result);
end;
end.