
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@494 8e941d3f-bd1b-0410-a28a-d453659cc2b4
1026 lines
26 KiB
ObjectPascal
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.
|
|
|