2
/***************************************************************************
6
***************************************************************************/
8
*****************************************************************************
10
* This file is part of the Lazarus Component Library (LCL) *
12
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
13
* for details about the copyright. *
15
* This program is distributed in the hope that it will be useful, *
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
19
*****************************************************************************
21
Author: Felipe Monteiro de Carvalho
24
Classes and functions for extending TFPImageCanvas to support more stretching
25
filters and to support all features from the LCL TCanvas
27
TLazCanvas also fixes various small problems and incompatibilities between
28
TFPImageCanvas versions, making the interface smoother for its users
30
Dont use anything from the LCL here as this unit should be kept strictly independent
31
only LCLProc for DebugLn is allowed, but only during debuging
36
{.$define lazcanvas_debug}
42
Classes, SysUtils, contnrs, Math,
44
fpimgcanv, fpcanvas, fpimage, clipping, pixtools, fppixlcanv,
47
{$ifdef lazcanvas_debug}, LCLProc{$endif};
51
{ TFPSharpInterpolation }
53
// This does a very sharp and square interpolation for stretching,
54
// similar to StretchBlt from the Windows API
55
TFPSharpInterpolation = class (TFPCustomInterpolation)
57
procedure Execute (x,y,w,h : integer); override;
62
TLazCanvasState = class
64
Brush: TFPCustomBrush;
67
BaseWindowOrg: TPoint;
70
ClipRegion: TFPCustomRegion;
71
destructor Destroy; override;
76
TLazCanvas = class(TFPImageCanvas)
78
FAssignedBrush: TFPCustomBrush;
79
FAssignedFont: TFPCustomFont;
80
FAssignedPen: TFPCustomPen;
81
FBaseWindowOrg: TPoint;
82
{$if defined(ver2_4) or defined(ver2_5) or defined(ver2_6)}
83
FLazClipRegion: TFPCustomRegion;
85
FWindowOrg: TPoint; // already in absolute coords with BaseWindowOrg summed up
86
GraphicStateList: TFPList; // TLazCanvasState
87
function GetAssignedBrush: TFPCustomBrush;
88
function GetAssignedPen: TFPCustomPen;
89
function GetAssignedFont: TFPCustomFont;
90
function GetWindowOrg: TPoint;
91
procedure SetWindowOrg(AValue: TPoint);
93
procedure SetColor (x,y:integer; const AValue:TFPColor); override;
94
function DoCreateDefaultFont : TFPCustomFont; override;
95
// Routines broken/unimplemented/incompatible in FPC
96
procedure DoRectangle (const Bounds:TRect); override;
97
procedure DoRectangleFill (const Bounds:TRect); override;
98
procedure DoPolygonFill (const points:array of TPoint); override;
99
// Routines which don't work with out extended clipping in TFPImageCanvas
100
procedure DoLine (x1,y1,x2,y2:integer); override;
103
NativeDC: PtrInt; // Utilized by LCL-CustomDrawn
104
ExtraFontData: TObject; // Utilized by LCL-CustomDrawn
105
constructor create (AnImage : TFPCustomImage);
106
destructor destroy; override;
107
procedure SetLazClipRegion(ARegion: TLazRegion);
108
// Canvas states list
109
function SaveState: Integer;
110
procedure RestoreState(AIndex: Integer);
111
// A simple operation to bring the Canvas in the default LCL TCanvas state
112
procedure ResetCanvasState;
113
// Alpha blending operations
114
procedure AlphaBlend(ASource: TLazCanvas;
115
const ADestX, ADestY, ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer);
116
procedure AlphaBlendIgnoringDestPixels(ASource: TLazCanvas;
117
const ADestX, ADestY, ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer);
118
procedure CanvasCopyRect(ASource: TLazCanvas;
119
const ADestX, ADestY, ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer);
120
// Compatibility with older FPC versions
121
{$if defined(ver2_4) or defined(ver2_5)}
122
procedure FillRect(const ARect: TRect);
123
procedure FillRect(X1,Y1,X2,Y2: Integer);
125
// Fills the entire drawing with a color
126
// AIgnoreClippingAndWindowOrg speeds up the drawing a lot, but it is dangerous,
127
// don't use it unless you know what you are doing!
128
procedure FillColor(AColor: TFPColor; AIgnoreClippingAndWindowOrg: Boolean = False);
129
// Utilized by LCLIntf.SelectObject and by RestoreState
130
// This needed to be added because Pen/Brush.Assign raises exceptions
131
procedure AssignPenData(APen: TFPCustomPen);
132
procedure AssignBrushData(ABrush: TFPCustomBrush);
133
procedure AssignFontData(AFont: TFPCustomFont);
134
// These properties are utilized to implement LCLIntf.SelectObject
135
// to keep track of which brush handle was assigned to this canvas
136
// They are not utilized by TLazCanvas itself
137
property AssignedPen: TFPCustomPen read GetAssignedPen write FAssignedPen;
138
property AssignedBrush: TFPCustomBrush read GetAssignedBrush write FAssignedBrush;
139
property AssignedFont: TFPCustomFont read GetAssignedFont write FAssignedFont;
141
// SetWindowOrg operations will be relative to BaseWindowOrg,
142
// This is very useful for implementing the non-native wincontrol,
143
// because operations of SetWindowOrg inside a non-native wincontrol will be
144
// based upon the BaseWindowOrg which is set relative to the Form canvas
145
property BaseWindowOrg: TPoint read FBaseWindowOrg write FBaseWindowOrg;
146
{$if defined(ver2_4) or defined(ver2_5) or defined(ver2_6)}
147
property ClipRegion: TFPCustomRegion read FLazClipRegion write FLazClipRegion;
149
property WindowOrg: TPoint read GetWindowOrg write SetWindowOrg;
156
destructor TLazCanvasState.Destroy;
158
if Brush <> nil then Brush.Free;
159
if Pen <> nil then Pen.Free;
165
function TLazCanvas.GetAssignedBrush: TFPCustomBrush;
167
if FAssignedBrush = nil then
168
Result := TFPEmptyBrush.Create
170
Result := FAssignedBrush;
173
function TLazCanvas.GetAssignedPen: TFPCustomPen;
175
if FAssignedPen = nil then
176
Result := TFPEmptyPen.Create
178
Result := FAssignedPen;
181
function TLazCanvas.GetAssignedFont: TFPCustomFont;
183
if FAssignedFont = nil then
184
Result := TFPEmptyFont.Create
186
Result := FAssignedFont;
189
function TLazCanvas.GetWindowOrg: TPoint;
191
Result := Point(FWindowOrg.X-FBaseWindowOrg.X, FWindowOrg.Y-FBaseWindowOrg.Y)
194
procedure TLazCanvas.SetWindowOrg(AValue: TPoint);
196
FWindowOrg.X := AValue.X+FBaseWindowOrg.X;
197
FWindowOrg.Y := AValue.Y+FBaseWindowOrg.Y;
198
{$ifdef lazcanvas_debug}
199
DebugLn(Format('[TLazCanvas.SetWindowOrg] AValue=%d,%d BaseWindowOrg=%d,%d', [AValue.X, AValue.Y, FBaseWindowOrg.X, FBaseWindowOrg.y]));
203
procedure TLazCanvas.SetColor(x, y: integer; const AValue: TFPColor);
207
lx := x + FWindowOrg.X;
208
ly := y + FWindowOrg.Y;
209
{$if defined(ver2_4) or defined(ver2_5) or defined(ver2_6)}
210
if Clipping and (not FLazClipRegion.IsPointInRegion(lx, ly)) then
212
if (lx >= 0) and (lx < width) and (ly >= 0) and (ly < height) then
213
Image.Colors[lx,ly] := AValue;
215
if Clipping and (not FClipRegion.IsPointInRegion(lx, ly)) then
217
if (lx >= 0) and (lx < width) and (ly >= 0) and (ly < height) then
218
FImage.Colors[lx,ly] := AValue;
222
function TLazCanvas.DoCreateDefaultFont: TFPCustomFont;
224
result := TFPEmptyFont.Create;
225
Result.Size := 0; // To allow it to use the default platform size
226
Result.FPColor := colBlack;
229
// The coordinates utilized by DoRectangle in fcl-image are not TCanvas compatible
230
// so we reimplement it here
231
procedure TLazCanvas.DoRectangle (const Bounds:TRect);
232
var pattern : longword;
234
procedure CheckLine (x1,y1, x2,y2 : integer);
237
// CheckLineClipping (ClipRect, x1,y1, x2,y2);
239
DrawSolidLine (self, x1,y1, x2,y2, Pen.FPColor)
242
procedure CheckPLine (x1,y1, x2,y2 : integer);
245
// CheckLineClipping (ClipRect, x1,y1, x2,y2);
247
DrawPatternLine (self, x1,y1, x2,y2, pattern, Pen.FPColor)
255
b.right := b.Right-1;
256
b.bottom := b.bottom-1;
257
if pen.style = psSolid then
258
for r := 1 to pen.width do
262
CheckLine (left,top,left,bottom);
263
CheckLine (left,bottom,right,bottom);
264
CheckLine (right,bottom,right,top);
265
CheckLine (right,top,left,top);
269
else if pen.style <> psClear then
271
if pen.style = psPattern then
272
pattern := Pen.pattern
274
pattern := PenPatterns[pen.style];
277
CheckPLine (left,top,left,bottom);
278
CheckPLine (left,bottom,right,bottom);
279
CheckPLine (right,bottom,right,top);
280
CheckPLine (right,top,left,top);
285
procedure TLazCanvas.DoRectangleFill(const Bounds: TRect);
291
// CheckRectClipping (ClipRect, B);
294
bsSolid : FillRectangleColor (self, left,top, right,bottom);
295
bsPattern : FillRectanglePattern (self, left,top, right,bottom, brush.pattern);
297
if assigned (brush.image) then
298
if RelativeBrushImage then
299
FillRectangleImageRel (self, left,top, right,bottom, brush.image)
301
FillRectangleImage (self, left,top, right,bottom, brush.image)
303
raise PixelCanvasException.Create (sErrNoImage);
304
bsBDiagonal : FillRectangleHashDiagonal (self, b, HashWidth);
305
bsFDiagonal : FillRectangleHashBackDiagonal (self, b, HashWidth);
308
FillRectangleHashHorizontal (self, b, HashWidth);
309
FillRectangleHashVertical (self, b, HashWidth);
313
FillRectangleHashDiagonal (self, b, HashWidth);
314
FillRectangleHashBackDiagonal (self, b, HashWidth);
316
bsHorizontal : FillRectangleHashHorizontal (self, b, HashWidth);
317
bsVertical : FillRectangleHashVertical (self, b, HashWidth);
321
// unimplemented in FPC
322
procedure TLazCanvas.DoPolygonFill(const points: array of TPoint);
327
if Brush.Style = bsClear then Exit;
329
// Find the Bounding Box of the Polygon
330
lBoundingBox := Rect(0, 0, 0, 0);
331
for i := low(Points) to High(Points) do
333
lBoundingBox.Left := Min(Points[i].X, lBoundingBox.Left);
334
lBoundingBox.Top := Min(Points[i].Y, lBoundingBox.Top);
335
lBoundingBox.Right := Max(Points[i].X, lBoundingBox.Right);
336
lBoundingBox.Bottom := Max(Points[i].Y, lBoundingBox.Bottom);
339
// Now scan all points using IsPointInPolygon
340
for x := lBoundingBox.Left to lBoundingBox.Right do
341
for y := lBoundingBox.Top to lBoundingBox.Bottom do
343
if IsPointInPolygon(X, Y, Points) then SetColor(X, Y, Brush.FPColor);
347
procedure TLazCanvas.DoLine(x1, y1, x2, y2: integer);
348
procedure DrawOneLine (xx1,yy1, xx2,yy2:integer);
351
CheckLineClipping (ClipRect, xx1,yy1, xx2,yy2);
352
DrawSolidLine (self, xx1,yy1, xx2,yy2, Pen.FPColor);
355
procedure SolidThickLine;
356
var w1, w2, r : integer;
359
// determine lines above and under
360
w1 := pen.width div 2;
362
if w1+w2 = pen.width then
364
// determine slanting
365
MoreHor := (abs(x2-x1) < abs(y2-y1));
367
begin // add lines left/right
369
DrawOneLine (x1-r,y1, x2-r,y2);
371
DrawOneLine (x1+r,y1, x2+r,y2);
374
begin // add lines above/under
376
DrawOneLine (x1,y1-r, x2,y2-r);
378
DrawOneLine (x1,y1+r, x2,y2+r);
383
{ We can are not clip here because we clip in each drawn pixel
384
or introduce a more complex algorithm to take into account lazregions
386
CheckLineClipping (ClipRect, x1,y1, x2,y2);}
390
DrawSolidLine (self, x1,y1, x2,y2, Pen.FPColor);
391
if pen.width > 1 then
395
DrawPatternLine (self, x1,y1, x2,y2, pen.pattern);
396
// Patterned lines have width always at 1
397
psDash, psDot, psDashDot, psDashDotDot :
398
DrawPatternLine (self, x1,y1, x2,y2, PenPatterns[Pen.Style]);
402
constructor TLazCanvas.create(AnImage: TFPCustomImage);
404
inherited Create(AnImage);
405
GraphicStateList := TFPList.Create;
406
HasNoImage := AnImage = nil;
409
destructor TLazCanvas.destroy;
411
GraphicStateList.Free;
412
if FAssignedBrush <> nil then FAssignedBrush.Free;
413
if FAssignedPen <> nil then FAssignedPen.Free;
417
procedure TLazCanvas.SetLazClipRegion(ARegion: TLazRegion);
420
{$if defined(ver2_4) or defined(ver2_5) or defined(ver2_6)}
421
ClipRect := TLazRegionRect(ARegion.Parts.Items[0]).Rect;
422
FLazClipRegion := ARegion;
424
ClipRegion := ARegion;
428
function TLazCanvas.SaveState: Integer;
430
lState: TLazCanvasState;
432
lState := TLazCanvasState.Create;
434
lState.Brush := Brush.CopyBrush;
435
lState.Pen := Pen.CopyPen;
436
lState.Font := Font.CopyFont;
437
lState.BaseWindowOrg := BaseWindowOrg;
438
lState.WindowOrg := WindowOrg;
439
lState.Clipping := Clipping;
441
Result := GraphicStateList.Add(lState);
444
// if AIndex is positive, it represents the wished saved dc instance
445
// if AIndex is negative, it's a relative number from last pushed state
446
procedure TLazCanvas.RestoreState(AIndex: Integer);
448
lState: TLazCanvasState;
450
if AIndex < 0 then AIndex := AIndex + GraphicStateList.Count;
451
lState := TLazCanvasState(GraphicStateList.Items[AIndex]);
452
GraphicStateList.Delete(AIndex);
453
if lState = nil then Exit;
455
AssignPenData(lState.Pen);
456
AssignBrushData(lState.Brush);
457
AssignFontData(lState.Font);
458
BaseWindowOrg := lState.BaseWindowOrg;
459
WindowOrg := lState.WindowOrg;
460
Clipping := lState.Clipping;
465
procedure TLazCanvas.ResetCanvasState;
467
Pen.FPColor := colBlack;
468
Pen.Style := psSolid;
470
Brush.FPColor := colWhite;
471
Brush.Style := bsSolid;
474
procedure TLazCanvas.AlphaBlend(ASource: TLazCanvas;
475
const ADestX, ADestY, ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer);
477
x, y, CurDestX, CurDestY, CurSrcX, CurSrcY: Integer;
478
MaskValue, InvMaskValue: Word;
479
CurColor, SrcColor: TFPColor;
480
lDrawWidth, lDrawHeight: Integer;
482
// Take care not to draw outside the destination area
483
lDrawWidth := Min(Self.Width - ADestX, ASource.Width - ASourceX);
484
lDrawHeight := Min(Self.Height - ADestY, ASource.Height - ASourceY);
485
lDrawWidth := Min(lDrawWidth, ASourceWidth);
486
lDrawHeight := Min(lDrawHeight, ASourceHeight);
487
//DebugLn(Format('[TLazCanvas.AlphaBlend] lDrawWidth=%d lDrawHeight=%d',
488
// [lDrawWidth, lDrawHeight]));
489
for y := 0 to lDrawHeight - 1 do
491
for x := 0 to lDrawWidth - 1 do
493
CurDestX := ADestX + x;
494
CurDestY := ADestY + y;
495
CurSrcX := ASourceX + x;
496
CurSrcY := ASourceY + y;
498
// Never draw outside the destination
499
if (CurDestX < 0) or (CurDestY < 0) then Continue;
501
MaskValue := ASource.Colors[CurSrcX, CurSrcY].alpha;
502
InvMaskValue := $FFFF - MaskValue;
504
if MaskValue = $FFFF then
506
Self.Colors[CurDestX, CurDestY] := ASource.Colors[CurSrcX, CurSrcY];
508
else if MaskValue > $00 then
510
CurColor := Self.Colors[CurDestX, CurDestY];
511
SrcColor := ASource.Colors[CurSrcX, CurSrcY];
513
CurColor.Red := Round(
514
CurColor.Red * InvMaskValue / $FFFF +
515
SrcColor.Red * MaskValue / $FFFF);
517
CurColor.Green := Round(
518
CurColor.Green * InvMaskValue / $FFFF +
519
SrcColor.Green * MaskValue / $FFFF);
521
CurColor.Blue := Round(
522
CurColor.Blue * InvMaskValue / $FFFF +
523
SrcColor.Blue * MaskValue / $FFFF);
525
CurColor.alpha := alphaOpaque;
527
{DebugLn(Format('Alpha blending pixels Old=%d %d Src=%d %d New=%d %d alpha=%d',
528
[Self.Colors[CurDestX, CurDestY].Red, Self.Colors[CurDestX, CurDestY].Green,
529
SrcColor.Red, SrcColor.Green,
530
CurColor.Red, CurColor.Green,
534
Self.Colors[CurDestX, CurDestY] := CurColor;
540
// This is a safer version in case one doesnt trust the destination pixels
541
// It will draw as if the target area contained opaque white
542
procedure TLazCanvas.AlphaBlendIgnoringDestPixels(ASource: TLazCanvas;
543
const ADestX, ADestY, ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer
546
x, y, CurDestX, CurDestY, CurSrcX, CurSrcY: Integer;
547
MaskValue, InvMaskValue: Word;
548
CurColor, SrcColor: TFPColor;
549
lDrawWidth, lDrawHeight: Integer;
551
// Take care not to draw outside the destination area
552
lDrawWidth := Min(Self.Width - ADestX, ASource.Width - ASourceX);
553
lDrawHeight := Min(Self.Height - ADestY, ASource.Height - ASourceY);
554
lDrawWidth := Min(lDrawWidth, ASourceWidth);
555
lDrawHeight := Min(lDrawHeight, ASourceHeight);
556
//DebugLn(Format('[TLazCanvas.AlphaBlendIgnoringDestPixels] lDrawWidth=%d lDrawHeight=%d',
557
//[lDrawWidth, lDrawHeight]));
558
for y := 0 to lDrawHeight - 1 do
560
for x := 0 to lDrawWidth - 1 do
562
CurDestX := ADestX + x;
563
CurDestY := ADestY + y;
564
CurSrcX := ASourceX + x;
565
CurSrcY := ASourceY + y;
567
// Never draw outside the destination
568
if (CurDestX < 0) or (CurDestY < 0) then Continue;
570
MaskValue := ASource.Colors[CurSrcX, CurSrcY].alpha;
571
InvMaskValue := $FFFF - MaskValue;
573
if MaskValue = $FFFF then
575
Self.Colors[CurDestX, CurDestY] := ASource.Colors[CurSrcX, CurSrcY];
577
// Theorically it should be > 0 but we make a filter here to exclude low-alpha pixels
578
// because those cause small white pixels in the image
579
else if MaskValue > $4000 then
581
SrcColor := ASource.Colors[CurSrcX, CurSrcY];
583
CurColor.Red := InvMaskValue + (SrcColor.Red * MaskValue) div $FFFF;
584
CurColor.Green := InvMaskValue + (SrcColor.Green * MaskValue) div $FFFF;
585
CurColor.Blue := InvMaskValue + (SrcColor.Blue * MaskValue) div $FFFF;
586
CurColor.alpha := alphaOpaque;
588
Self.Colors[CurDestX, CurDestY] := CurColor;
594
procedure TLazCanvas.CanvasCopyRect(ASource: TLazCanvas; const ADestX, ADestY,
595
ASourceX, ASourceY, ASourceWidth, ASourceHeight: Integer);
597
x, y, CurDestX, CurDestY, CurSrcX, CurSrcY: Integer;
598
lDrawWidth, lDrawHeight: Integer;
601
// Take care not to draw outside the source and also not outside the destination area
602
lDrawWidth := Min(Self.Width - ADestX, ASource.Width - ASourceX);
603
lDrawHeight := Min(Self.Height - ADestY, ASource.Height - ASourceY);
604
lDrawWidth := Min(lDrawWidth, ASourceWidth);
605
lDrawHeight := Min(lDrawHeight, ASourceHeight);
607
for y := 0 to lDrawHeight - 1 do
609
for x := 0 to lDrawWidth - 1 do
611
CurDestX := ADestX + x;
612
CurDestY := ADestY + y;
613
CurSrcX := ASourceX + x;
614
CurSrcY := ASourceY + y;
616
// Never draw outside the destination
617
if (CurDestX < 0) or (CurDestY < 0) then Continue;
619
lColor := ASource.Colors[CurSrcX, CurSrcY];
620
Self.Colors[CurDestX, CurDestY] := lColor;
625
{$if defined(ver2_4) or defined(ver2_5)}
626
procedure TLazCanvas.FillRect(const ARect: TRect);
628
if (Brush.style <> bsClear) then
630
//if not (brush is TFPCustomDrawBrush) then
631
DoRectangleFill (ARect)
634
// TFPCustomDrawBrush(Brush).Rectangle (left,top,right,bottom);
638
procedure TLazCanvas.FillRect(X1, Y1, X2, Y2: Integer);
640
FillRect (Rect(X1,Y1,X2,Y2));
644
procedure TLazCanvas.FillColor(AColor: TFPColor;
645
AIgnoreClippingAndWindowOrg: Boolean);
649
if AIgnoreClippingAndWindowOrg then
651
for y := 0 to Height-1 do
652
for x := 0 to Width-1 do
653
Image.Colors[x, y] := AColor;
657
for y := 0 to Height-1 do
658
for x := 0 to Width-1 do
659
SetColor(x, y, AColor);
663
procedure TLazCanvas.AssignPenData(APen: TFPCustomPen);
665
if APen = nil then Exit;
666
Pen.FPColor := APen.FPColor;
667
Pen.Style := APen.Style;
668
Pen.Width := APen.Width;
671
procedure TLazCanvas.AssignBrushData(ABrush: TFPCustomBrush);
673
if ABrush = nil then Exit;
674
Brush.FPColor := ABrush.FPColor;
675
Brush.Style := ABrush.Style;
678
procedure TLazCanvas.AssignFontData(AFont: TFPCustomFont);
680
if AFont = nil then Exit;
681
Font.FPColor := AFont.FPColor;
682
Font.Name := AFont.Name;
683
Font.Size := AFont.Size;
684
Font.Bold := AFont.Bold;
685
Font.Italic := AFont.Italic;
686
Font.Underline := AFont.Underline;
687
{$IF (FPC_FULLVERSION=20601) or (FPC_FULLVERSION>=20701)} //changed in 2.6.1 and 2.7; remove when FPC 2.6.2+ only is supported
688
Font.StrikeThrough := AFont.StrikeThrough;
690
Font.StrikeTrough := AFont.StrikeTrough; //old version with typo
694
{ TFPWindowsSharpInterpolation }
696
procedure TFPSharpInterpolation.Execute(x, y, w, h: integer);
697
// paint Image on Canvas at x,y,w*h
699
srcx, srcy: Integer; // current coordinates in the source image
700
dx, dy, dw, dh: Integer; // current coordinates in the destination canvas
701
lWidth, lHeight: Integer; // Image size
704
if (w<=0) or (h<=0) or (image.Width=0) or (image.Height=0) then
707
lWidth := Image.Width-1;
708
lHeight := Image.Height-1;
712
for dx := 0 to w-1 do
713
for dy := 0 to h-1 do
715
srcx := Round((dx / dw) * lWidth);
716
srcy := Round((dy / dh) * lHeight);
717
lColor := Image.Colors[srcx, srcy];
718
Canvas.Colors[dx+x, dy+y] := lColor;