source: trunk/Packages/bgrabitmap/bgragradientscanner.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 61.4 KB
Line 
1unit BGRAGradientScanner;
2
3{$mode objfpc}{$H+}
4
5interface
6
7{ This unit contains scanners that generate gradients }
8
9uses
10 Classes, SysUtils, BGRABitmapTypes, BGRATransform;
11
12type
13 TBGRAColorInterpolation = (ciStdRGB, ciLinearRGB, ciLinearHSLPositive, ciLinearHSLNegative, ciGSBPositive, ciGSBNegative);
14 TBGRAGradientRepetition = (grPad, grRepeat, grReflect, grSine);
15
16 { TBGRASimpleGradient }
17
18 TBGRASimpleGradient = class(TBGRACustomGradient)
19 protected
20 FColor1,FColor2: TBGRAPixel;
21 ec1,ec2: TExpandedPixel;
22 FRepetition: TBGRAGradientRepetition;
23 constructor Create(AColor1,AColor2: TBGRAPixel; ARepetition: TBGRAGradientRepetition); overload;
24 constructor Create(AColor1,AColor2: TExpandedPixel; ARepetition: TBGRAGradientRepetition); overload;
25 function InterpolateToBGRA(position: word): TBGRAPixel; virtual; abstract;
26 function InterpolateToExpanded(position: word): TExpandedPixel; virtual; abstract;
27 public
28 class function CreateAny(AInterpolation: TBGRAColorInterpolation; AColor1,AColor2: TBGRAPixel; ARepetition: TBGRAGradientRepetition): TBGRASimpleGradient; overload;
29 class function CreateAny(AInterpolation: TBGRAColorInterpolation; AColor1,AColor2: TExpandedPixel; ARepetition: TBGRAGradientRepetition): TBGRASimpleGradient; overload;
30 function GetColorAt(position: integer): TBGRAPixel; override;
31 function GetColorAtF(position: single): TBGRAPixel; override;
32 function GetExpandedColorAt(position: integer): TExpandedPixel; override;
33 function GetExpandedColorAtF(position: single): TExpandedPixel; override;
34 function GetAverageColor: TBGRAPixel; override;
35 function GetAverageExpandedColor: TExpandedPixel; override;
36 function GetMonochrome: boolean; override;
37 property Repetition: TBGRAGradientRepetition read FRepetition write FRepetition;
38 end;
39
40 { TBGRASimpleGradientWithoutGammaCorrection }
41
42 TBGRASimpleGradientWithoutGammaCorrection = class(TBGRASimpleGradient)
43 protected
44 function InterpolateToBGRA(position: word): TBGRAPixel; override;
45 function InterpolateToExpanded(position: word): TExpandedPixel; override;
46 public
47 constructor Create(Color1,Color2: TBGRAPixel; ARepetition: TBGRAGradientRepetition = grPad); overload;
48 constructor Create(Color1,Color2: TExpandedPixel; ARepetition: TBGRAGradientRepetition = grPad); overload;
49 end;
50
51 { TBGRASimpleGradientWithGammaCorrection }
52
53 TBGRASimpleGradientWithGammaCorrection = class(TBGRASimpleGradient)
54 protected
55 function InterpolateToBGRA(position: word): TBGRAPixel; override;
56 function InterpolateToExpanded(position: word): TExpandedPixel; override;
57 public
58 constructor Create(Color1,Color2: TBGRAPixel; ARepetition: TBGRAGradientRepetition = grPad); overload;
59 constructor Create(Color1,Color2: TExpandedPixel; ARepetition: TBGRAGradientRepetition = grPad); overload;
60 end;
61
62 THueGradientOption = (hgoRepeat, hgoReflect, //repetition
63 hgoPositiveDirection, hgoNegativeDirection, //hue orientation
64 hgoHueCorrection, hgoLightnessCorrection); //color interpolation
65 THueGradientOptions = set of THueGradientOption;
66
67 { TBGRAHueGradient }
68
69 TBGRAHueGradient = class(TBGRASimpleGradient)
70 private
71 hsla1,hsla2: THSLAPixel;
72 hue1,hue2: longword;
73 FOptions: THueGradientOptions;
74 procedure Init(c1,c2: THSLAPixel; AOptions: THueGradientOptions);
75 function InterpolateToHSLA(position: word): THSLAPixel;
76 protected
77 function InterpolateToBGRA(position: word): TBGRAPixel; override;
78 function InterpolateToExpanded(position: word): TExpandedPixel; override;
79 public
80 constructor Create(Color1,Color2: TBGRAPixel; options: THueGradientOptions); overload;
81 constructor Create(Color1,Color2: TExpandedPixel; options: THueGradientOptions); overload;
82 constructor Create(Color1,Color2: THSLAPixel; options: THueGradientOptions); overload;
83 constructor Create(AHue1,AHue2: Word; Saturation,Lightness: Word; options: THueGradientOptions); overload;
84 function GetMonochrome: boolean; override;
85 end;
86
87 TGradientInterpolationFunction = function(t: single): single of object;
88
89 { TBGRAMultiGradient }
90
91 TBGRAMultiGradient = class(TBGRACustomGradient)
92 private
93 FColors: array of TBGRAPixel;
94 FPositions: array of integer;
95 FPositionsF: array of single;
96 FEColors: array of TExpandedPixel;
97 FCycle: Boolean;
98 FInterpolationFunction: TGradientInterpolationFunction;
99 procedure Init(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection, ACycle: boolean);
100 public
101 GammaCorrection: boolean;
102 function CosineInterpolation(t: single): single;
103 function HalfCosineInterpolation(t: single): single;
104 constructor Create(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean = false);
105 function GetColorAt(position: integer): TBGRAPixel; override;
106 function GetExpandedColorAt(position: integer): TExpandedPixel; override;
107 function GetAverageColor: TBGRAPixel; override;
108 function GetMonochrome: boolean; override;
109 property InterpolationFunction: TGradientInterpolationFunction read FInterpolationFunction write FInterpolationFunction;
110 end;
111
112 TBGRAGradientScannerInternalScanNextFunc = function():single of object;
113 TBGRAGradientScannerInternalScanAtFunc = function(const p: TPointF):single of object;
114
115 { TBGRAGradientScanner }
116
117 TBGRAGradientScanner = class(TBGRACustomScanner)
118 protected
119 FGradientType: TGradientType;
120 FOrigin,FDir1,FDir2: TPointF;
121 FRelativeFocal: TPointF;
122 FRadius, FFocalRadius: single;
123 FTransform, FHiddenTransform: TAffineMatrix;
124 FSinus: Boolean;
125 FGradient: TBGRACustomGradient;
126 FGradientOwner: boolean;
127 FFlipGradient: boolean;
128
129 FMatrix: TAffineMatrix;
130 FRepeatHoriz, FIsAverage: boolean;
131 FAverageColor: TBGRAPixel;
132 FAverageExpandedColor: TExpandedPixel;
133 FScanNextFunc: TBGRAGradientScannerInternalScanNextFunc;
134 FScanAtFunc: TBGRAGradientScannerInternalScanAtFunc;
135 FFocalDistance: single;
136 FFocalDirection, FFocalNormal: TPointF;
137 FRadialDenominator, FRadialDeltaSign, maxW1, maxW2: single;
138
139 FPosition: TPointF;
140 FHorizColor: TBGRAPixel;
141 FHorizExpandedColor: TExpandedPixel;
142
143 procedure Init(AGradientType: TGradientType; AOrigin, d1: TPointF; ATransform: TAffineMatrix; Sinus: Boolean=False); overload;
144 procedure Init(AGradientType: TGradientType; AOrigin, d1, d2: TPointF; ATransform: TAffineMatrix; Sinus: Boolean=False); overload;
145 procedure Init(AOrigin: TPointF; ARadius: single; AFocal: TPointF; AFocalRadius: single; ATransform: TAffineMatrix; AHiddenTransform: TAffineMatrix); overload;
146
147 procedure InitGradientType;
148 procedure InitTransform;
149 procedure InitGradient;
150
151 function ComputeRadialFocal(const p: TPointF): single;
152
153 function ScanNextLinear: single;
154 function ScanNextReflected: single;
155 function ScanNextDiamond: single;
156 function ScanNextRadial: single;
157 function ScanNextRadial2: single;
158 function ScanNextRadialFocal: single;
159 function ScanNextAngular: single;
160
161 function ScanAtLinear(const p: TPointF): single;
162 function ScanAtReflected(const p: TPointF): single;
163 function ScanAtDiamond(const p: TPointF): single;
164 function ScanAtRadial(const p: TPointF): single;
165 function ScanAtRadial2(const p: TPointF): single;
166 function ScanAtRadialFocal(const p: TPointF): single;
167 function ScanAtAngular(const p: TPointF): single;
168
169 function ScanNextInline: TBGRAPixel; inline;
170 function ScanNextExpandedInline: TExpandedPixel; inline;
171 procedure SetTransform(AValue: TAffineMatrix);
172 procedure SetFlipGradient(AValue: boolean);
173 function GetGradientColor(a: single): TBGRAPixel;
174 function GetGradientExpandedColor(a: single): TExpandedPixel;
175 public
176 constructor Create(AGradientType: TGradientType; AOrigin, d1: TPointF); overload;
177 constructor Create(AGradientType: TGradientType; AOrigin, d1, d2: TPointF); overload;
178 constructor Create(AOrigin, d1, d2, AFocal: TPointF; ARadiusRatio: single = 1; AFocalRadiusRatio: single = 0); overload;
179 constructor Create(AOrigin: TPointF; ARadius: single; AFocal: TPointF; AFocalRadius: single); overload;
180
181 constructor Create(c1, c2: TBGRAPixel; AGradientType: TGradientType; AOrigin, d1: TPointF;
182 gammaColorCorrection: boolean = True; Sinus: Boolean=False); overload;
183 constructor Create(c1, c2: TBGRAPixel; AGradientType: TGradientType; AOrigin, d1, d2: TPointF;
184 gammaColorCorrection: boolean = True; Sinus: Boolean=False); overload;
185
186 constructor Create(gradient: TBGRACustomGradient; AGradientType: TGradientType; AOrigin, d1: TPointF;
187 Sinus: Boolean=False; AGradientOwner: Boolean=False); overload;
188 constructor Create(gradient: TBGRACustomGradient; AGradientType: TGradientType; AOrigin, d1, d2: TPointF;
189 Sinus: Boolean=False; AGradientOwner: Boolean=False); overload;
190 constructor Create(gradient: TBGRACustomGradient; AOrigin: TPointF; ARadius: single; AFocal: TPointF;
191 AFocalRadius: single; AGradientOwner: Boolean=False); overload;
192
193 procedure SetGradient(c1,c2: TBGRAPixel; AGammaCorrection: boolean = true); overload;
194 procedure SetGradient(AGradient: TBGRACustomGradient; AOwner: boolean); overload;
195 destructor Destroy; override;
196 procedure ScanMoveTo(X, Y: Integer); override;
197 function ScanNextPixel: TBGRAPixel; override;
198 function ScanNextExpandedPixel: TExpandedPixel; override;
199 function ScanAt(X, Y: Single): TBGRAPixel; override;
200 function ScanAtExpanded(X, Y: Single): TExpandedPixel; override;
201 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
202 function IsScanPutPixelsDefined: boolean; override;
203 property Transform: TAffineMatrix read FTransform write SetTransform;
204 property Gradient: TBGRACustomGradient read FGradient;
205 property FlipGradient: boolean read FFlipGradient write SetFlipGradient;
206 property Sinus: boolean Read FSinus write FSinus;
207 end;
208
209 { TBGRAConstantScanner }
210
211 TBGRAConstantScanner = class(TBGRAGradientScanner)
212 constructor Create(c: TBGRAPixel);
213 end;
214
215 { TBGRARandomScanner }
216
217 TBGRARandomScanner = class(TBGRACustomScanner)
218 private
219 FOpacity: byte;
220 FGrayscale: boolean;
221 FRandomBuffer, FRandomBufferCount: integer;
222 public
223 constructor Create(AGrayscale: Boolean; AOpacity: byte);
224 function ScanAtInteger({%H-}X, {%H-}Y: integer): TBGRAPixel; override;
225 function ScanNextPixel: TBGRAPixel; override;
226 function ScanAt({%H-}X, {%H-}Y: Single): TBGRAPixel; override;
227 end;
228
229 { TBGRAGradientTriangleScanner }
230
231 TBGRAGradientTriangleScanner= class(TBGRACustomScanner)
232 protected
233 FMatrix: TAffineMatrix;
234 FColor1,FDiff2,FDiff3,FStep: TColorF;
235 FCurColor: TColorF;
236 public
237 constructor Create(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel);
238 procedure ScanMoveTo(X,Y: Integer); override;
239 procedure ScanMoveToF(X,Y: Single);
240 function ScanAt(X,Y: Single): TBGRAPixel; override;
241 function ScanNextPixel: TBGRAPixel; override;
242 function ScanNextExpandedPixel: TExpandedPixel; override;
243 end;
244
245 { TBGRASolidColorMaskScanner }
246
247 TBGRASolidColorMaskScanner = class(TBGRACustomScanner)
248 private
249 FOffset: TPoint;
250 FMask: TBGRACustomBitmap;
251 FSolidColor: TBGRAPixel;
252 FScanNext : TScanNextPixelFunction;
253 FScanAt : TScanAtFunction;
254 FMemMask: packed array of TBGRAPixel;
255 public
256 constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ASolidColor: TBGRAPixel);
257 destructor Destroy; override;
258 function IsScanPutPixelsDefined: boolean; override;
259 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
260 procedure ScanMoveTo(X,Y: Integer); override;
261 function ScanNextPixel: TBGRAPixel; override;
262 function ScanAt(X,Y: Single): TBGRAPixel; override;
263 property Color: TBGRAPixel read FSolidColor write FSolidColor;
264 end;
265
266 { TBGRATextureMaskScanner }
267
268 TBGRATextureMaskScanner = class(TBGRACustomScanner)
269 private
270 FOffset: TPoint;
271 FMask: TBGRACustomBitmap;
272 FTexture: IBGRAScanner;
273 FMaskScanNext,FTextureScanNext : TScanNextPixelFunction;
274 FMaskScanAt,FTextureScanAt : TScanAtFunction;
275 FGlobalOpacity: Byte;
276 FMemMask, FMemTex: packed array of TBGRAPixel;
277 public
278 constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255);
279 destructor Destroy; override;
280 function IsScanPutPixelsDefined: boolean; override;
281 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
282 procedure ScanMoveTo(X,Y: Integer); override;
283 function ScanNextPixel: TBGRAPixel; override;
284 function ScanAt(X,Y: Single): TBGRAPixel; override;
285 end;
286
287 { TBGRAOpacityScanner }
288
289 TBGRAOpacityScanner = class(TBGRACustomScanner)
290 private
291 FTexture: IBGRAScanner;
292 FOwnedScanner: TBGRACustomScanner;
293 FGlobalOpacity: Byte;
294 FScanNext : TScanNextPixelFunction;
295 FScanAt : TScanAtFunction;
296 FMemTex: packed array of TBGRAPixel;
297 public
298 constructor Create(ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255);
299 constructor Create(ATexture: TBGRACustomScanner; AGlobalOpacity: Byte; AOwned: boolean);
300 destructor Destroy; override;
301 function IsScanPutPixelsDefined: boolean; override;
302 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
303 procedure ScanMoveTo(X,Y: Integer); override;
304 function ScanNextPixel: TBGRAPixel; override;
305 function ScanAt(X,Y: Single): TBGRAPixel; override;
306 end;
307
308implementation
309
310uses BGRABlend, Math;
311
312{ TBGRASimpleGradient }
313
314constructor TBGRASimpleGradient.Create(AColor1, AColor2: TBGRAPixel; ARepetition: TBGRAGradientRepetition);
315begin
316 FColor1 := AColor1;
317 FColor2 := AColor2;
318 ec1 := GammaExpansion(AColor1);
319 ec2 := GammaExpansion(AColor2);
320 FRepetition:= ARepetition;
321end;
322
323constructor TBGRASimpleGradient.Create(AColor1, AColor2: TExpandedPixel;
324 ARepetition: TBGRAGradientRepetition);
325begin
326 FColor1 := GammaCompression(AColor1);
327 FColor2 := GammaCompression(AColor2);
328 ec1 := AColor1;
329 ec2 := AColor2;
330 FRepetition:= ARepetition;
331end;
332
333class function TBGRASimpleGradient.CreateAny(AInterpolation: TBGRAColorInterpolation;
334 AColor1, AColor2: TBGRAPixel; ARepetition: TBGRAGradientRepetition): TBGRASimpleGradient;
335begin
336 case AInterpolation of
337 ciStdRGB: result := TBGRASimpleGradientWithoutGammaCorrection.Create(AColor1,AColor2);
338 ciLinearRGB: result := TBGRASimpleGradientWithGammaCorrection.Create(AColor1,AColor2);
339 ciLinearHSLPositive: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoPositiveDirection]);
340 ciLinearHSLNegative: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoNegativeDirection]);
341 ciGSBPositive: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoPositiveDirection, hgoHueCorrection, hgoLightnessCorrection]);
342 ciGSBNegative: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoNegativeDirection, hgoHueCorrection, hgoLightnessCorrection]);
343 end;
344 result.Repetition := ARepetition;
345end;
346
347class function TBGRASimpleGradient.CreateAny(AInterpolation: TBGRAColorInterpolation;
348 AColor1, AColor2: TExpandedPixel; ARepetition: TBGRAGradientRepetition): TBGRASimpleGradient;
349begin
350 case AInterpolation of
351 ciStdRGB: result := TBGRASimpleGradientWithoutGammaCorrection.Create(AColor1,AColor2);
352 ciLinearRGB: result := TBGRASimpleGradientWithGammaCorrection.Create(AColor1,AColor2);
353 ciLinearHSLPositive: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoPositiveDirection]);
354 ciLinearHSLNegative: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoNegativeDirection]);
355 ciGSBPositive: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoPositiveDirection, hgoHueCorrection, hgoLightnessCorrection]);
356 ciGSBNegative: result := TBGRAHueGradient.Create(AColor1,AColor2,[hgoNegativeDirection, hgoHueCorrection, hgoLightnessCorrection]);
357 end;
358 result.Repetition := ARepetition;
359end;
360
361function TBGRASimpleGradient.GetAverageColor: TBGRAPixel;
362begin
363 result := InterpolateToBGRA(32768);
364end;
365
366function TBGRASimpleGradient.GetAverageExpandedColor: TExpandedPixel;
367begin
368 Result:= InterpolateToExpanded(32768);
369end;
370
371function TBGRASimpleGradient.GetColorAt(position: integer): TBGRAPixel;
372begin
373 case FRepetition of
374 grSine: begin
375 position := Sin65536(position and $ffff);
376 if position = 65536 then
377 result := FColor2
378 else
379 result := InterpolateToBGRA(position);
380 end;
381 grRepeat: result := InterpolateToBGRA(position and $ffff);
382 grReflect:
383 begin
384 position := position and $1ffff;
385 if position >= $10000 then
386 begin
387 if position = $10000 then
388 result := FColor2
389 else
390 result := InterpolateToBGRA($20000 - position);
391 end
392 else
393 result := InterpolateToBGRA(position);
394 end;
395 else
396 begin
397 if position <= 0 then
398 result := FColor1 else
399 if position >= 65536 then
400 result := FColor2 else
401 result := InterpolateToBGRA(position);
402 end;
403 end;
404end;
405
406function TBGRASimpleGradient.GetColorAtF(position: single): TBGRAPixel;
407begin
408 if FRepetition <> grPad then
409 result := GetColorAt(round(frac(position*0.5)*131072)) else //divided by 2 for reflected repetition
410 begin
411 if position <= 0 then
412 result := FColor1 else
413 if position >= 1 then
414 result := FColor2 else
415 result := GetColorAt(round(position*65536));
416 end;
417end;
418
419function TBGRASimpleGradient.GetExpandedColorAt(position: integer
420 ): TExpandedPixel;
421begin
422 case FRepetition of
423 grSine: begin
424 position := Sin65536(position and $ffff);
425 if position = 65536 then
426 result := ec2
427 else
428 result := InterpolateToExpanded(position);
429 end;
430 grRepeat: result := InterpolateToExpanded(position and $ffff);
431 grReflect:
432 begin
433 position := position and $1ffff;
434 if position >= $10000 then
435 begin
436 if position = $10000 then
437 result := ec2
438 else
439 result := InterpolateToExpanded($20000 - position);
440 end
441 else
442 result := InterpolateToExpanded(position);
443 end;
444 else
445 begin
446 if position <= 0 then
447 result := ec1 else
448 if position >= 65536 then
449 result := ec2 else
450 result := InterpolateToExpanded(position);
451 end;
452 end;
453end;
454
455function TBGRASimpleGradient.GetExpandedColorAtF(position: single
456 ): TExpandedPixel;
457begin
458 if FRepetition <> grPad then
459 result := GetExpandedColorAt(round(frac(position*0.5)*131072)) else //divided by 2 for reflected repetition
460 begin
461 if position <= 0 then
462 result := ec1 else
463 if position >= 1 then
464 result := ec2 else
465 result := GetExpandedColorAt(round(position*65536));
466 end;
467end;
468
469function TBGRASimpleGradient.GetMonochrome: boolean;
470begin
471 Result:= (FColor1 = FColor2);
472end;
473
474{ TBGRAConstantScanner }
475
476constructor TBGRAConstantScanner.Create(c: TBGRAPixel);
477begin
478 inherited Create(c,c,gtLinear,PointF(0,0),PointF(0,0),false);
479end;
480
481{ TBGRARandomScanner }
482
483constructor TBGRARandomScanner.Create(AGrayscale: Boolean; AOpacity: byte);
484begin
485 FGrayscale:= AGrayscale;
486 FOpacity:= AOpacity;
487 FRandomBufferCount := 0;
488end;
489
490function TBGRARandomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel;
491begin
492 Result:=ScanNextPixel;
493end;
494
495function TBGRARandomScanner.ScanNextPixel: TBGRAPixel;
496var rgb: integer;
497begin
498 if FGrayscale then
499 begin
500 if FRandomBufferCount = 0 then
501 begin
502 FRandomBuffer := random(256*256*256);
503 FRandomBufferCount := 3;
504 end;
505 result.red := FRandomBuffer and 255;
506 FRandomBuffer:= FRandomBuffer shr 8;
507 FRandomBufferCount -= 1;
508 result.green := result.red;
509 result.blue := result.red;
510 result.alpha:= FOpacity;
511 end else
512 begin
513 rgb := random(256*256*256);
514 Result:= BGRA(rgb and 255,(rgb shr 8) and 255,(rgb shr 16) and 255,FOpacity);
515 end;
516end;
517
518function TBGRARandomScanner.ScanAt(X, Y: Single): TBGRAPixel;
519begin
520 Result:=ScanNextPixel;
521end;
522
523{ TBGRAHueGradient }
524
525procedure TBGRAHueGradient.Init(c1, c2: THSLAPixel; AOptions: THueGradientOptions);
526begin
527 FOptions:= AOptions;
528 if (hgoLightnessCorrection in AOptions) then
529 begin
530 hsla1 := ExpandedToGSBA(ec1);
531 hsla2 := ExpandedToGSBA(ec2);
532 end else
533 begin
534 hsla1 := c1;
535 hsla2 := c2;
536 end;
537 if not (hgoHueCorrection in AOptions) then
538 begin
539 hue1 := c1.hue;
540 hue2 := c2.hue;
541 end else
542 begin
543 hue1 := HtoG(c1.hue);
544 hue2 := HtoG(c2.hue);
545 end;
546 if (hgoPositiveDirection in AOptions) and not (hgoNegativeDirection in AOptions) then
547 begin
548 if c2.hue <= c1.hue then hue2 += 65536;
549 end else
550 if not (hgoPositiveDirection in AOptions) and (hgoNegativeDirection in AOptions) then
551 begin
552 if c2.hue >= c1.hue then hue1 += 65536;
553 end;
554end;
555
556function TBGRAHueGradient.InterpolateToHSLA(position: word): THSLAPixel;
557var b,b2: LongWord;
558begin
559 b := position shr 2;
560 b2 := 16384-b;
561 result.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff;
562 result.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14;
563 result.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14;
564 result.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14;
565 if hgoLightnessCorrection in FOptions then
566 begin
567 if not (hgoHueCorrection in FOptions) then
568 result.hue := HtoG(result.hue);
569 end else
570 begin
571 if hgoHueCorrection in FOptions then
572 result.hue := GtoH(result.hue);
573 end;
574end;
575
576function TBGRAHueGradient.InterpolateToBGRA(position: word): TBGRAPixel;
577begin
578 if hgoLightnessCorrection in FOptions then
579 result := GSBAToBGRA(InterpolateToHSLA(position))
580 else
581 result := HSLAToBGRA(InterpolateToHSLA(position));
582end;
583
584function TBGRAHueGradient.InterpolateToExpanded(position: word): TExpandedPixel;
585begin
586 if hgoLightnessCorrection in FOptions then
587 result := GSBAToExpanded(InterpolateToHSLA(position))
588 else
589 result := HSLAToExpanded(InterpolateToHSLA(position));
590end;
591
592constructor TBGRAHueGradient.Create(Color1, Color2: TBGRAPixel;options: THueGradientOptions);
593begin
594 if hgoReflect in options then
595 inherited Create(Color1,Color2,grReflect)
596 else if hgoRepeat in options then
597 inherited Create(Color1,Color2,grRepeat)
598 else
599 inherited Create(Color1,Color2,grPad);
600
601 Init(BGRAToHSLA(Color1),BGRAToHSLA(Color2),options);
602end;
603
604constructor TBGRAHueGradient.Create(Color1, Color2: TExpandedPixel;
605 options: THueGradientOptions);
606begin
607 if hgoReflect in options then
608 inherited Create(Color1,Color2,grReflect)
609 else if hgoRepeat in options then
610 inherited Create(Color1,Color2,grRepeat)
611 else
612 inherited Create(Color1,Color2,grPad);
613
614 Init(ExpandedToHSLA(Color1),ExpandedToHSLA(Color2),options);
615end;
616
617constructor TBGRAHueGradient.Create(Color1, Color2: THSLAPixel; options: THueGradientOptions);
618begin
619 if hgoReflect in options then
620 inherited Create(Color1.ToExpanded,Color2.ToExpanded,grReflect)
621 else if hgoRepeat in options then
622 inherited Create(Color1.ToExpanded,Color2.ToExpanded,grRepeat)
623 else
624 inherited Create(Color1.ToExpanded,Color2.ToExpanded,grPad);
625
626 Init(Color1,Color2, options);
627end;
628
629constructor TBGRAHueGradient.Create(AHue1, AHue2: Word; Saturation,
630 Lightness: Word; options: THueGradientOptions);
631begin
632 Create(HSLA(AHue1,saturation,lightness), HSLA(AHue2,saturation,lightness), options);
633end;
634
635function TBGRAHueGradient.GetMonochrome: boolean;
636begin
637 Result:= false;
638end;
639
640{ TBGRAMultiGradient }
641
642procedure TBGRAMultiGradient.Init(Colors: array of TBGRAPixel;
643 Positions0To1: array of single; AGammaCorrection, ACycle: boolean);
644var
645 i: Integer;
646begin
647 if length(Positions0To1) <> length(colors) then
648 raise Exception.Create('Dimension mismatch');
649 if length(Positions0To1) = 0 then
650 raise Exception.Create('Empty gradient');
651 setlength(FColors,length(Colors));
652 setlength(FPositions,length(Positions0To1));
653 setlength(FPositionsF,length(Positions0To1));
654 setlength(FEColors,length(Colors));
655 for i := 0 to high(colors) do
656 begin
657 FColors[i]:= colors[i];
658 FPositions[i]:= round(Positions0To1[i]*65536);
659 FPositionsF[i]:= Positions0To1[i];
660 FEColors[i]:= GammaExpansion(colors[i]);
661 end;
662 GammaCorrection := AGammaCorrection;
663 FCycle := ACycle;
664 if FPositions[high(FPositions)] = FPositions[0] then FCycle := false;
665end;
666
667function TBGRAMultiGradient.CosineInterpolation(t: single): single;
668begin
669 result := (1-cos(t*Pi))*0.5;
670end;
671
672function TBGRAMultiGradient.HalfCosineInterpolation(t: single): single;
673begin
674 result := (1-cos(t*Pi))*0.25 + t*0.5;
675end;
676
677constructor TBGRAMultiGradient.Create(Colors: array of TBGRAPixel;
678 Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean);
679begin
680 Init(Colors,Positions0To1,AGammaCorrection, ACycle);
681end;
682
683function TBGRAMultiGradient.GetColorAt(position: integer): TBGRAPixel;
684var i: NativeInt;
685 ec: TExpandedPixel;
686 curPos,posDiff: NativeInt;
687begin
688 if FCycle then
689 position := (position-FPositions[0]) mod (FPositions[high(FPositions)] - FPositions[0]) + FPositions[0];
690 if position <= FPositions[0] then
691 result := FColors[0] else
692 if position >= FPositions[high(FPositions)] then
693 result := FColors[high(FColors)] else
694 begin
695 i := 0;
696 while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do
697 inc(i);
698
699 if Position = FPositions[i] then
700 result := FColors[i]
701 else
702 begin
703 curPos := position-FPositions[i];
704 posDiff := FPositions[i+1]-FPositions[i];
705 if FInterpolationFunction <> nil then
706 begin
707 curPos := round(FInterpolationFunction(curPos/posDiff)*65536);
708 posDiff := 65536;
709 end;
710 if GammaCorrection then
711 begin
712 if FEColors[i+1].red < FEColors[i].red then
713 ec.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else
714 ec.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff);
715 if FEColors[i+1].green < FEColors[i].green then
716 ec.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else
717 ec.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff);
718 if FEColors[i+1].blue < FEColors[i].blue then
719 ec.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else
720 ec.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff);
721 if FEColors[i+1].alpha < FEColors[i].alpha then
722 ec.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else
723 ec.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff);
724 result := GammaCompression(ec);
725 end else
726 begin
727 result.red := FColors[i].red + (curPos)*(FColors[i+1].red-FColors[i].red) div (posDiff);
728 result.green := FColors[i].green + (curPos)*(FColors[i+1].green-FColors[i].green) div (posDiff);
729 result.blue := FColors[i].blue + (curPos)*(FColors[i+1].blue-FColors[i].blue) div (posDiff);
730 result.alpha := FColors[i].alpha + (curPos)*(FColors[i+1].alpha-FColors[i].alpha) div (posDiff);
731 end;
732 end;
733 end;
734end;
735
736function TBGRAMultiGradient.GetExpandedColorAt(position: integer
737 ): TExpandedPixel;
738var i: NativeInt;
739 curPos,posDiff: NativeInt;
740 rw,gw,bw: NativeUInt;
741begin
742 if FCycle then
743 position := (position-FPositions[0]) mod (FPositions[high(FPositions)] - FPositions[0]) + FPositions[0];
744 if position <= FPositions[0] then
745 result := FEColors[0] else
746 if position >= FPositions[high(FPositions)] then
747 result := FEColors[high(FColors)] else
748 begin
749 i := 0;
750 while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do
751 inc(i);
752
753 if Position = FPositions[i] then
754 result := FEColors[i]
755 else
756 begin
757 curPos := position-FPositions[i];
758 posDiff := FPositions[i+1]-FPositions[i];
759 if FInterpolationFunction <> nil then
760 begin
761 curPos := round(FInterpolationFunction(curPos/posDiff)*65536);
762 posDiff := 65536;
763 end;
764 if GammaCorrection then
765 begin
766 if FEColors[i+1].red < FEColors[i].red then
767 result.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else
768 result.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff);
769 if FEColors[i+1].green < FEColors[i].green then
770 result.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else
771 result.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff);
772 if FEColors[i+1].blue < FEColors[i].blue then
773 result.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else
774 result.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff);
775 if FEColors[i+1].alpha < FEColors[i].alpha then
776 result.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else
777 result.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff);
778 end else
779 begin
780 rw := NativeInt(FColors[i].red shl 8) + (((curPos) shl 8)*(FColors[i+1].red-FColors[i].red)) div (posDiff);
781 gw := NativeInt(FColors[i].green shl 8) + (((curPos) shl 8)*(FColors[i+1].green-FColors[i].green)) div (posDiff);
782 bw := NativeInt(FColors[i].blue shl 8) + (((curPos) shl 8)*(FColors[i+1].blue-FColors[i].blue)) div (posDiff);
783
784 if rw >= $ff00 then result.red := $ffff
785 else result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8;
786 if gw >= $ff00 then result.green := $ffff
787 else result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8;
788 if bw >= $ff00 then result.blue := $ffff
789 else result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8;
790 result.alpha := NativeInt(FColors[i].alpha shl 8) + (((curPos) shl 8)*(FColors[i+1].alpha-FColors[i].alpha)) div (posDiff);
791 result.alpha := result.alpha + (result.alpha shr 8);
792 end;
793 end;
794 end;
795end;
796
797function TBGRAMultiGradient.GetAverageColor: TBGRAPixel;
798var sumR,sumG,sumB,sumA: integer;
799 i: Integer;
800begin
801 sumR := 0;
802 sumG := 0;
803 sumB := 0;
804 sumA := 0;
805 for i := 0 to high(FColors) do
806 begin
807 sumR += FColors[i].red;
808 sumG += FColors[i].green;
809 sumB += FColors[i].blue;
810 sumA += FColors[i].alpha;
811 end;
812 result := BGRA(sumR div length(FColors),sumG div length(FColors),
813 sumB div length(FColors),sumA div length(FColors));
814end;
815
816function TBGRAMultiGradient.GetMonochrome: boolean;
817var i: integer;
818begin
819 for i := 1 to high(FColors) do
820 if FColors[i] <> FColors[0] then
821 begin
822 result := false;
823 exit;
824 end;
825 Result:= true;
826end;
827
828{ TBGRASimpleGradientWithGammaCorrection }
829
830function TBGRASimpleGradientWithGammaCorrection.InterpolateToBGRA(position: word
831 ): TBGRAPixel;
832var b,b2: cardinal;
833 ec: TExpandedPixel;
834begin
835 b := position;
836 b2 := 65536-b;
837 ec.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16;
838 ec.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16;
839 ec.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16;
840 ec.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16;
841 result := GammaCompression(ec);
842end;
843
844function TBGRASimpleGradientWithGammaCorrection.InterpolateToExpanded(
845 position: word): TExpandedPixel;
846var b,b2: cardinal;
847begin
848 b := position;
849 b2 := 65536-b;
850 result.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16;
851 result.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16;
852 result.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16;
853 result.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16;
854end;
855
856constructor TBGRASimpleGradientWithGammaCorrection.Create(Color1,
857 Color2: TBGRAPixel; ARepetition: TBGRAGradientRepetition);
858begin
859 inherited Create(Color1,Color2,ARepetition);
860end;
861
862constructor TBGRASimpleGradientWithGammaCorrection.Create(Color1,
863 Color2: TExpandedPixel; ARepetition: TBGRAGradientRepetition);
864begin
865 inherited Create(Color1,Color2,ARepetition);
866end;
867
868{ TBGRASimpleGradientWithoutGammaCorrection }
869
870function TBGRASimpleGradientWithoutGammaCorrection.InterpolateToBGRA(
871 position: word): TBGRAPixel;
872var b,b2: cardinal;
873begin
874 b := position shr 6;
875 b2 := 1024-b;
876 result.red := (FColor1.red * b2 + FColor2.red * b + 511) shr 10;
877 result.green := (FColor1.green * b2 + FColor2.green * b + 511) shr 10;
878 result.blue := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 10;
879 result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 10;
880end;
881
882function TBGRASimpleGradientWithoutGammaCorrection.InterpolateToExpanded(
883 position: word): TExpandedPixel;
884var b,b2: cardinal;
885 rw,gw,bw: word;
886begin
887 b := position shr 6;
888 b2 := 1024-b;
889 rw := (FColor1.red * b2 + FColor2.red * b + 511) shr 2;
890 gw := (FColor1.green * b2 + FColor2.green * b + 511) shr 2;
891 bw := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 2;
892
893 if rw >= $ff00 then
894 result.red := 65535
895 else
896 result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8;
897
898 if gw >= $ff00 then
899 result.green := 65535
900 else
901 result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8;
902
903 if bw >= $ff00 then
904 result.blue := 65535
905 else
906 result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8;
907
908 result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 2;
909end;
910
911constructor TBGRASimpleGradientWithoutGammaCorrection.Create(Color1,
912 Color2: TBGRAPixel; ARepetition: TBGRAGradientRepetition);
913begin
914 inherited Create(Color1,Color2,ARepetition);
915end;
916
917constructor TBGRASimpleGradientWithoutGammaCorrection.Create(Color1,
918 Color2: TExpandedPixel; ARepetition: TBGRAGradientRepetition);
919begin
920 inherited Create(Color1,Color2,ARepetition);
921end;
922
923{ TBGRAGradientTriangleScanner }
924
925constructor TBGRAGradientTriangleScanner.Create(pt1, pt2, pt3: TPointF; c1, c2,
926 c3: TBGRAPixel);
927var ec1,ec2,ec3: TExpandedPixel;
928begin
929 FMatrix := AffineMatrix(pt2.X-pt1.X, pt3.X-pt1.X, 0,
930 pt2.Y-pt1.Y, pt3.Y-pt1.Y, 0);
931 if not IsAffineMatrixInversible(FMatrix) then
932 FMatrix := AffineMatrix(0,0,0,0,0,0)
933 else
934 FMatrix := AffineMatrixInverse(FMatrix) * AffineMatrixTranslation(-pt1.x,-pt1.y);
935
936 ec1 := GammaExpansion(c1);
937 ec2 := GammaExpansion(c2);
938 ec3 := GammaExpansion(c3);
939 FColor1[1] := ec1.red;
940 FColor1[2] := ec1.green;
941 FColor1[3] := ec1.blue;
942 FColor1[4] := ec1.alpha;
943 FDiff2[1] := ec2.red - ec1.red;
944 FDiff2[2] := ec2.green - ec1.green;
945 FDiff2[3] := ec2.blue - ec1.blue;
946 FDiff2[4] := ec2.alpha - ec1.alpha;
947 FDiff3[1] := ec3.red - ec1.red;
948 FDiff3[2] := ec3.green - ec1.green;
949 FDiff3[3] := ec3.blue - ec1.blue;
950 FDiff3[4] := ec3.alpha - ec1.alpha;
951 FStep := FDiff2*FMatrix[1,1]+FDiff3*FMatrix[2,1];
952end;
953
954procedure TBGRAGradientTriangleScanner.ScanMoveTo(X, Y: Integer);
955begin
956 ScanMoveToF(X, Y);
957end;
958
959procedure TBGRAGradientTriangleScanner.ScanMoveToF(X, Y: Single);
960var
961 Cur: TPointF;
962begin
963 Cur := FMatrix*PointF(X,Y);
964 FCurColor := FColor1+FDiff2*Cur.X+FDiff3*Cur.Y;
965end;
966
967function TBGRAGradientTriangleScanner.ScanAt(X, Y: Single): TBGRAPixel;
968begin
969 ScanMoveToF(X,Y);
970 result := ScanNextPixel;
971end;
972
973function TBGRAGradientTriangleScanner.ScanNextPixel: TBGRAPixel;
974var r,g,b,a: int64;
975begin
976 r := round(FCurColor[1]);
977 g := round(FCurColor[2]);
978 b := round(FCurColor[3]);
979 a := round(FCurColor[4]);
980 if r > 65535 then r := 65535 else
981 if r < 0 then r := 0;
982 if g > 65535 then g := 65535 else
983 if g < 0 then g := 0;
984 if b > 65535 then b := 65535 else
985 if b < 0 then b := 0;
986 if a > 65535 then a := 65535 else
987 if a < 0 then a := 0;
988 result.red := GammaCompressionTab[r];
989 result.green := GammaCompressionTab[g];
990 result.blue := GammaCompressionTab[b];
991 result.alpha := a shr 8;
992 FCurColor += FStep;
993end;
994
995function TBGRAGradientTriangleScanner.ScanNextExpandedPixel: TExpandedPixel;
996var r,g,b,a: int64;
997begin
998 r := round(FCurColor[1]);
999 g := round(FCurColor[2]);
1000 b := round(FCurColor[3]);
1001 a := round(FCurColor[4]);
1002 if r > 65535 then r := 65535 else
1003 if r < 0 then r := 0;
1004 if g > 65535 then g := 65535 else
1005 if g < 0 then g := 0;
1006 if b > 65535 then b := 65535 else
1007 if b < 0 then b := 0;
1008 if a > 65535 then a := 65535 else
1009 if a < 0 then a := 0;
1010 result.red := r;
1011 result.green := g;
1012 result.blue := b;
1013 result.alpha := a;
1014 FCurColor += FStep;
1015end;
1016
1017{ TBGRAGradientScanner }
1018
1019procedure TBGRAGradientScanner.SetTransform(AValue: TAffineMatrix);
1020begin
1021 if FTransform=AValue then Exit;
1022 FTransform:=AValue;
1023 InitTransform;
1024end;
1025
1026constructor TBGRAGradientScanner.Create(AGradientType: TGradientType; AOrigin, d1: TPointF);
1027begin
1028 FGradient := nil;
1029 SetGradient(BGRABlack,BGRAWhite,False);
1030 Init(AGradientType,AOrigin,d1,AffineMatrixIdentity,False);
1031end;
1032
1033constructor TBGRAGradientScanner.Create(AGradientType: TGradientType; AOrigin, d1,d2: TPointF);
1034begin
1035 FGradient := nil;
1036 SetGradient(BGRABlack,BGRAWhite,False);
1037 Init(AGradientType,AOrigin,d1,d2,AffineMatrixIdentity,False);
1038end;
1039
1040constructor TBGRAGradientScanner.Create(AOrigin,
1041 d1, d2, AFocal: TPointF; ARadiusRatio: single; AFocalRadiusRatio: single);
1042var
1043 m, mInv: TAffineMatrix;
1044 focalInv: TPointF;
1045begin
1046 FGradient := nil;
1047 SetGradient(BGRABlack,BGRAWhite,False);
1048
1049 m := AffineMatrix((d1-AOrigin).x, (d2-AOrigin).x, AOrigin.x,
1050 (d1-AOrigin).y, (d2-AOrigin).y, AOrigin.y);
1051 if IsAffineMatrixInversible(m) then
1052 begin
1053 mInv := AffineMatrixInverse(m);
1054 focalInv := mInv*AFocal;
1055 end else
1056 focalInv := PointF(0,0);
1057
1058 Init(PointF(0,0), ARadiusRatio, focalInv, AFocalRadiusRatio, AffineMatrixIdentity, m);
1059end;
1060
1061constructor TBGRAGradientScanner.Create(AOrigin: TPointF; ARadius: single;
1062 AFocal: TPointF; AFocalRadius: single);
1063begin
1064 FGradient := nil;
1065 SetGradient(BGRABlack,BGRAWhite,False);
1066
1067 Init(AOrigin, ARadius, AFocal, AFocalRadius, AffineMatrixIdentity, AffineMatrixIdentity);
1068end;
1069
1070procedure TBGRAGradientScanner.SetFlipGradient(AValue: boolean);
1071begin
1072 if FFlipGradient=AValue then Exit;
1073 FFlipGradient:=AValue;
1074end;
1075
1076function TBGRAGradientScanner.GetGradientColor(a: single): TBGRAPixel;
1077begin
1078 if a = EmptySingle then
1079 result := BGRAPixelTransparent
1080 else
1081 begin
1082 if FFlipGradient then a := 1-a;
1083 if FSinus then
1084 begin
1085 a := a*65536;
1086 if (a <= low(int64)) or (a >= high(int64)) then
1087 result := FAverageColor
1088 else
1089 result := FGradient.GetColorAt(Sin65536(round(a) and 65535));
1090 end else
1091 result := FGradient.GetColorAtF(a);
1092 end;
1093end;
1094
1095function TBGRAGradientScanner.GetGradientExpandedColor(a: single): TExpandedPixel;
1096begin
1097 if a = EmptySingle then
1098 QWord(result) := 0
1099 else
1100 begin
1101 if FFlipGradient then a := 1-a;
1102 if FSinus then
1103 begin
1104 a *= 65536;
1105 if (a <= low(int64)) or (a >= high(int64)) then
1106 result := FAverageExpandedColor
1107 else
1108 result := FGradient.GetExpandedColorAt(Sin65536(round(a) and 65535));
1109 end else
1110 result := FGradient.GetExpandedColorAtF(a);
1111 end;
1112end;
1113
1114procedure TBGRAGradientScanner.Init(AGradientType: TGradientType; AOrigin, d1: TPointF;
1115 ATransform: TAffineMatrix; Sinus: Boolean);
1116var d2: TPointF;
1117begin
1118 with (d1-AOrigin) do
1119 d2 := PointF(AOrigin.x+y,AOrigin.y-x);
1120 Init(AGradientType,AOrigin,d1,d2,ATransform,Sinus);
1121end;
1122
1123procedure TBGRAGradientScanner.Init(AGradientType: TGradientType; AOrigin, d1, d2: TPointF;
1124 ATransform: TAffineMatrix; Sinus: Boolean);
1125begin
1126 FGradientType:= AGradientType;
1127 FFlipGradient:= false;
1128 FOrigin := AOrigin;
1129 FDir1 := d1;
1130 FDir2 := d2;
1131 FSinus := Sinus;
1132 FTransform := ATransform;
1133 FHiddenTransform := AffineMatrixIdentity;
1134
1135 FRadius := 1;
1136 FRelativeFocal := PointF(0,0);
1137 FFocalRadius := 0;
1138
1139 InitGradientType;
1140 InitTransform;
1141end;
1142
1143procedure TBGRAGradientScanner.Init(AOrigin: TPointF; ARadius: single;
1144 AFocal: TPointF; AFocalRadius: single; ATransform: TAffineMatrix; AHiddenTransform: TAffineMatrix);
1145var maxRadius: single;
1146begin
1147 FGradientType:= gtRadial;
1148 FFlipGradient:= false;
1149 FOrigin := AOrigin;
1150 ARadius := abs(ARadius);
1151 AFocalRadius := abs(AFocalRadius);
1152 maxRadius := max(ARadius,AFocalRadius);
1153 FDir1 := AOrigin+PointF(maxRadius,0);
1154 FDir2 := AOrigin+PointF(0,maxRadius);
1155 FSinus := False;
1156 FTransform := ATransform;
1157 FHiddenTransform := AHiddenTransform;
1158
1159 FRadius := ARadius/maxRadius;
1160 FRelativeFocal := (AFocal - AOrigin)*(1/maxRadius);
1161 FFocalRadius := AFocalRadius/maxRadius;
1162
1163 InitGradientType;
1164 InitTransform;
1165end;
1166
1167procedure TBGRAGradientScanner.InitGradientType;
1168begin
1169 case FGradientType of
1170 gtReflected: begin
1171 FScanNextFunc:= @ScanNextReflected;
1172 FScanAtFunc:= @ScanAtReflected;
1173 end;
1174 gtDiamond: begin
1175 FScanNextFunc:= @ScanNextDiamond;
1176 FScanAtFunc:= @ScanAtDiamond;
1177 end;
1178 gtRadial: if (FRelativeFocal.x = 0) and (FRelativeFocal.y = 0) then
1179 begin
1180 if (FFocalRadius = 0) and (FRadius = 1) then
1181 begin
1182 FScanNextFunc:= @ScanNextRadial;
1183 FScanAtFunc:= @ScanAtRadial;
1184 end else
1185 begin
1186 FScanNextFunc:= @ScanNextRadial2;
1187 FScanAtFunc:= @ScanAtRadial2;
1188 end;
1189 end else
1190 begin
1191 FScanNextFunc:= @ScanNextRadialFocal;
1192 FScanAtFunc:= @ScanAtRadialFocal;
1193
1194 FFocalDirection := FRelativeFocal;
1195 FFocalDistance := VectLen(FFocalDirection);
1196 if FFocalDistance > 0 then FFocalDirection *= 1/FFocalDistance;
1197 FFocalNormal := PointF(-FFocalDirection.y,FFocalDirection.x);
1198 FRadialDenominator := sqr(FRadius-FFocalRadius)-sqr(FFocalDistance);
1199
1200 //case in which the second circle is bigger and the first circle is within the second
1201 if (FRadius < FFocalRadius) and (FFocalDistance <= FFocalRadius-FRadius) then
1202 FRadialDeltaSign := -1
1203 else
1204 FRadialDeltaSign := 1;
1205
1206 //clipping afer the apex
1207 if (FFocalRadius < FRadius) and (FFocalDistance > FRadius-FFocalRadius) then
1208 begin
1209 maxW1 := FRadius/(FRadius-FFocalRadius)*FFocalDistance;
1210 maxW2 := MaxSingle;
1211 end else
1212 if (FRadius < FFocalRadius) and (FFocalDistance > FFocalRadius-FRadius) then
1213 begin
1214 maxW1 := MaxSingle;
1215 maxW2 := FFocalRadius/(FFocalRadius-FRadius)*FFocalDistance;
1216 end else
1217 begin
1218 maxW1 := MaxSingle;
1219 maxW2 := MaxSingle;
1220 end;
1221 end;
1222 gtAngular: begin
1223 FScanNextFunc:= @ScanNextAngular;
1224 FScanAtFunc:= @ScanAtAngular;
1225 end;
1226 else
1227 {gtLinear:} begin
1228 FScanNextFunc:= @ScanNextLinear;
1229 FScanAtFunc:= @ScanAtLinear;
1230 end;
1231 end;
1232end;
1233
1234procedure TBGRAGradientScanner.SetGradient(c1, c2: TBGRAPixel;
1235 AGammaCorrection: boolean);
1236begin
1237 if Assigned(FGradient) and FGradientOwner then FreeAndNil(FGradient);
1238
1239 //transparent pixels have no color so
1240 //take it from other color
1241 if c1.alpha = 0 then c1 := BGRA(c2.red,c2.green,c2.blue,0);
1242 if c2.alpha = 0 then c2 := BGRA(c1.red,c1.green,c1.blue,0);
1243
1244 if AGammaCorrection then
1245 FGradient := TBGRASimpleGradientWithGammaCorrection.Create(c1,c2)
1246 else
1247 FGradient := TBGRASimpleGradientWithoutGammaCorrection.Create(c1,c2);
1248 FGradientOwner := true;
1249 InitGradient;
1250end;
1251
1252procedure TBGRAGradientScanner.SetGradient(AGradient: TBGRACustomGradient;
1253 AOwner: boolean);
1254begin
1255 if Assigned(FGradient) and FGradientOwner then FreeAndNil(FGradient);
1256 FGradient := AGradient;
1257 FGradientOwner := AOwner;
1258 InitGradient;
1259end;
1260
1261procedure TBGRAGradientScanner.InitTransform;
1262var u,v: TPointF;
1263begin
1264 u := FDir1-FOrigin;
1265 if FGradientType in[gtLinear,gtReflected] then
1266 v := PointF(u.y, -u.x)
1267 else
1268 v := FDir2-FOrigin;
1269
1270 FMatrix := FTransform * FHiddenTransform * AffineMatrix(u.x, v.x, FOrigin.x,
1271 u.y, v.y, FOrigin.y);
1272 if IsAffineMatrixInversible(FMatrix) then
1273 begin
1274 FMatrix := AffineMatrixInverse(FMatrix);
1275 FIsAverage:= false;
1276 end else
1277 begin
1278 FMatrix := AffineMatrixIdentity;
1279 FIsAverage:= true;
1280 end;
1281
1282 case FGradientType of
1283 gtReflected: FRepeatHoriz := (FMatrix[1,1]=0);
1284 gtDiamond,gtAngular: FRepeatHoriz:= FIsAverage;
1285 gtRadial: begin
1286 if FFocalRadius = FRadius then FIsAverage:= true;
1287 FRepeatHoriz:= FIsAverage;
1288 end
1289 else
1290 {gtLinear:} FRepeatHoriz := (FMatrix[1,1]=0);
1291 end;
1292
1293 if FGradient.Monochrome then
1294 begin
1295 FRepeatHoriz:= true;
1296 FIsAverage:= true;
1297 end;
1298
1299 FPosition := PointF(0,0);
1300end;
1301
1302procedure TBGRAGradientScanner.InitGradient;
1303begin
1304 FAverageColor := FGradient.GetAverageColor;
1305 FAverageExpandedColor := FGradient.GetAverageExpandedColor;
1306end;
1307
1308function TBGRAGradientScanner.ComputeRadialFocal(const p: TPointF): single;
1309var
1310 w1,w2,h,d1,d2,delta,num: single;
1311begin
1312 w1 := p*FFocalDirection;
1313 w2 := FFocalDistance-w1;
1314 if (w1 < maxW1) and (w2 < maxW2) then
1315 begin
1316 //vertical position and distances
1317 h := sqr(p*FFocalNormal);
1318 d1 := sqr(w1)+h;
1319 d2 := sqr(w2)+h;
1320 //finding t
1321 delta := sqr(FFocalRadius)*d1 + 2*FRadius*FFocalRadius*(p*(FRelativeFocal-p))+
1322 sqr(FRadius)*d2 - sqr(VectDet(p,FRelativeFocal));
1323 if delta >= 0 then
1324 begin
1325 num := -FFocalRadius*(FRadius-FFocalRadius)-(FRelativeFocal*(FRelativeFocal-p));
1326 result := (num+FRadialDeltaSign*sqrt(delta))/FRadialDenominator;
1327 end else
1328 result := EmptySingle;
1329 end else
1330 result := EmptySingle;
1331end;
1332
1333function TBGRAGradientScanner.ScanNextLinear: single;
1334begin
1335 result := FPosition.x;
1336end;
1337
1338function TBGRAGradientScanner.ScanNextReflected: single;
1339begin
1340 result := abs(FPosition.x);
1341end;
1342
1343function TBGRAGradientScanner.ScanNextDiamond: single;
1344begin
1345 result := max(abs(FPosition.x), abs(FPosition.y));
1346end;
1347
1348function TBGRAGradientScanner.ScanNextRadial: single;
1349begin
1350 result := sqrt(sqr(FPosition.x) + sqr(FPosition.y));
1351end;
1352
1353function TBGRAGradientScanner.ScanNextRadial2: single;
1354begin
1355 result := (sqrt(sqr(FPosition.x) + sqr(FPosition.y))-FFocalRadius)/(FRadius-FFocalRadius);
1356end;
1357
1358function TBGRAGradientScanner.ScanNextRadialFocal: single;
1359begin
1360 result := ComputeRadialFocal(FPosition);
1361end;
1362
1363function TBGRAGradientScanner.ScanNextAngular: single;
1364begin
1365 if FPosition.y >= 0 then
1366 result := arctan2(FPosition.y,FPosition.x)/(2*Pi)
1367 else
1368 result := 1-arctan2(-FPosition.y,FPosition.x)/(2*Pi)
1369end;
1370
1371function TBGRAGradientScanner.ScanAtLinear(const p: TPointF): single;
1372begin
1373 with (FMatrix*p) do
1374 result := x;
1375end;
1376
1377function TBGRAGradientScanner.ScanAtReflected(const p: TPointF): single;
1378begin
1379 with (FMatrix*p) do
1380 result := abs(x);
1381end;
1382
1383function TBGRAGradientScanner.ScanAtDiamond(const p: TPointF): single;
1384begin
1385 with (FMatrix*p) do
1386 result := max(abs(x), abs(y));
1387end;
1388
1389function TBGRAGradientScanner.ScanAtRadial(const p: TPointF): single;
1390begin
1391 with (FMatrix*p) do
1392 result := sqrt(sqr(x) + sqr(y));
1393end;
1394
1395function TBGRAGradientScanner.ScanAtRadial2(const p: TPointF): single;
1396begin
1397 with (FMatrix*p) do
1398 result := (sqrt(sqr(x) + sqr(y))-FFocalRadius)/(FRadius-FFocalRadius);
1399end;
1400
1401function TBGRAGradientScanner.ScanAtRadialFocal(const p: TPointF): single;
1402begin
1403 result := ComputeRadialFocal(FMatrix*p);
1404end;
1405
1406function TBGRAGradientScanner.ScanAtAngular(const p: TPointF): single;
1407begin
1408 with (FMatrix*p) do
1409 begin
1410 if y >= 0 then
1411 result := arctan2(y,x)/(2*Pi)
1412 else
1413 result := 1-arctan2(-y,x)/(2*Pi)
1414 end;
1415end;
1416
1417function TBGRAGradientScanner.ScanNextInline: TBGRAPixel;
1418begin
1419 if FIsAverage then
1420 result := FAverageColor
1421 else
1422 begin
1423 result := GetGradientColor(FScanNextFunc());
1424 FPosition += PointF(FMatrix[1,1],FMatrix[2,1]);
1425 end;
1426end;
1427
1428function TBGRAGradientScanner.ScanNextExpandedInline: TExpandedPixel;
1429begin
1430 if FIsAverage then
1431 result := FAverageExpandedColor
1432 else
1433 begin
1434 result := GetGradientExpandedColor(FScanNextFunc());
1435 FPosition += PointF(FMatrix[1,1],FMatrix[2,1]);
1436 end;
1437end;
1438
1439constructor TBGRAGradientScanner.Create(c1, c2: TBGRAPixel;
1440 AGradientType: TGradientType; AOrigin, d1: TPointF; gammaColorCorrection: boolean;
1441 Sinus: Boolean);
1442begin
1443 FGradient := nil;
1444 SetGradient(c1,c2,gammaColorCorrection);
1445 Init(AGradientType,AOrigin,d1,AffineMatrixIdentity,Sinus);
1446end;
1447
1448constructor TBGRAGradientScanner.Create(c1, c2: TBGRAPixel;
1449 AGradientType: TGradientType; AOrigin, d1, d2: TPointF; gammaColorCorrection: boolean;
1450 Sinus: Boolean);
1451begin
1452 FGradient := nil;
1453 if AGradientType in[gtLinear,gtReflected] then raise EInvalidArgument.Create('Two directions are not required for linear and reflected gradients');
1454 SetGradient(c1,c2,gammaColorCorrection);
1455 Init(AGradientType,AOrigin,d1,d2,AffineMatrixIdentity,Sinus);
1456end;
1457
1458constructor TBGRAGradientScanner.Create(gradient: TBGRACustomGradient;
1459 AGradientType: TGradientType; AOrigin, d1: TPointF; Sinus: Boolean; AGradientOwner: Boolean=False);
1460begin
1461 FGradient := gradient;
1462 FGradientOwner := AGradientOwner;
1463 Init(AGradientType,AOrigin,d1,AffineMatrixIdentity,Sinus);
1464end;
1465
1466constructor TBGRAGradientScanner.Create(gradient: TBGRACustomGradient;
1467 AGradientType: TGradientType; AOrigin, d1, d2: TPointF; Sinus: Boolean;
1468 AGradientOwner: Boolean);
1469begin
1470 if AGradientType in[gtLinear,gtReflected] then raise EInvalidArgument.Create('Two directions are not required for linear and reflected gradients');
1471 FGradient := gradient;
1472 FGradientOwner := AGradientOwner;
1473 Init(AGradientType,AOrigin,d1,d2,AffineMatrixIdentity,Sinus);
1474end;
1475
1476constructor TBGRAGradientScanner.Create(gradient: TBGRACustomGradient;
1477 AOrigin: TPointF; ARadius: single; AFocal: TPointF; AFocalRadius: single;
1478 AGradientOwner: Boolean);
1479begin
1480 FGradient := gradient;
1481 FGradientOwner := AGradientOwner;
1482 Init(AOrigin, ARadius, AFocal, AFocalRadius, AffineMatrixIdentity, AffineMatrixIdentity);
1483end;
1484
1485destructor TBGRAGradientScanner.Destroy;
1486begin
1487 if FGradientOwner then
1488 FGradient.Free;
1489 inherited Destroy;
1490end;
1491
1492procedure TBGRAGradientScanner.ScanMoveTo(X, Y: Integer);
1493begin
1494 FPosition := FMatrix*PointF(x,y);
1495 if FRepeatHoriz then
1496 begin
1497 FHorizColor := ScanNextInline;
1498 FHorizExpandedColor := ScanNextExpandedInline;
1499 end;
1500end;
1501
1502function TBGRAGradientScanner.ScanNextPixel: TBGRAPixel;
1503begin
1504 if FRepeatHoriz then
1505 result := FHorizColor
1506 else
1507 result := ScanNextInline;
1508end;
1509
1510function TBGRAGradientScanner.ScanNextExpandedPixel: TExpandedPixel;
1511begin
1512 if FRepeatHoriz then
1513 result := FHorizExpandedColor
1514 else
1515 result := ScanNextExpandedInline;
1516end;
1517
1518function TBGRAGradientScanner.ScanAt(X, Y: Single): TBGRAPixel;
1519begin
1520 if FIsAverage then
1521 result := FAverageColor
1522 else
1523 result := GetGradientColor(FScanAtFunc(PointF(X,Y)));
1524end;
1525
1526function TBGRAGradientScanner.ScanAtExpanded(X, Y: Single): TExpandedPixel;
1527begin
1528 if FIsAverage then
1529 result := FAverageExpandedColor
1530 else
1531 result := GetGradientExpandedColor(FScanAtFunc(PointF(X,Y)));
1532end;
1533
1534procedure TBGRAGradientScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
1535 mode: TDrawMode);
1536var c: TBGRAPixel;
1537begin
1538 if FRepeatHoriz then
1539 begin
1540 c := FHorizColor;
1541 case mode of
1542 dmDrawWithTransparency: DrawPixelsInline(pdest,c,count);
1543 dmLinearBlend: FastBlendPixelsInline(pdest,c,count);
1544 dmSet: FillDWord(pdest^,count,Longword(c));
1545 dmXor: XorInline(pdest,c,count);
1546 dmSetExceptTransparent: if c.alpha = 255 then FillDWord(pdest^,count,Longword(c));
1547 end;
1548 exit;
1549 end;
1550
1551 case mode of
1552 dmDrawWithTransparency:
1553 while count > 0 do
1554 begin
1555 DrawPixelInlineWithAlphaCheck(pdest,ScanNextInline);
1556 inc(pdest);
1557 dec(count);
1558 end;
1559 dmLinearBlend:
1560 while count > 0 do
1561 begin
1562 FastBlendPixelInline(pdest,ScanNextInline);
1563 inc(pdest);
1564 dec(count);
1565 end;
1566 dmXor:
1567 while count > 0 do
1568 begin
1569 PDword(pdest)^ := PDword(pdest)^ xor DWord(ScanNextInline);
1570 inc(pdest);
1571 dec(count);
1572 end;
1573 dmSet:
1574 while count > 0 do
1575 begin
1576 pdest^ := ScanNextInline;
1577 inc(pdest);
1578 dec(count);
1579 end;
1580 dmSetExceptTransparent:
1581 while count > 0 do
1582 begin
1583 c := ScanNextInline;
1584 if c.alpha = 255 then pdest^ := c;
1585 inc(pdest);
1586 dec(count);
1587 end;
1588 end;
1589end;
1590
1591function TBGRAGradientScanner.IsScanPutPixelsDefined: boolean;
1592begin
1593 result := true;
1594end;
1595
1596{ TBGRATextureMaskScanner }
1597
1598constructor TBGRATextureMaskScanner.Create(AMask: TBGRACustomBitmap;
1599 AOffset: TPoint; ATexture: IBGRAScanner; AGlobalOpacity: Byte);
1600begin
1601 FMask := AMask;
1602 FMaskScanNext := @FMask.ScanNextPixel;
1603 FMaskScanAt := @FMask.ScanAt;
1604 FOffset := AOffset;
1605 FTexture := ATexture;
1606 FTextureScanNext := @FTexture.ScanNextPixel;
1607 FTextureScanAt := @FTexture.ScanAt;
1608 FGlobalOpacity:= AGlobalOpacity;
1609end;
1610
1611destructor TBGRATextureMaskScanner.Destroy;
1612begin
1613 fillchar(FMask,sizeof(FMask),0); //avoids interface deref
1614 fillchar(FTexture,sizeof(FTexture),0);
1615 inherited Destroy;
1616end;
1617
1618function TBGRATextureMaskScanner.IsScanPutPixelsDefined: boolean;
1619begin
1620 Result:= true;
1621end;
1622
1623procedure TBGRATextureMaskScanner.ScanPutPixels(pdest: PBGRAPixel;
1624 count: integer; mode: TDrawMode);
1625var c: TBGRAPixel;
1626 alpha: byte;
1627 pmask, ptex: pbgrapixel;
1628
1629 function GetNext: TBGRAPixel; inline;
1630 begin
1631 alpha := pmask^.red;
1632 inc(pmask);
1633 result := ptex^;
1634 inc(ptex);
1635 result.alpha := ApplyOpacity(result.alpha,alpha);
1636 end;
1637
1638 function GetNextWithGlobal: TBGRAPixel; inline;
1639 begin
1640 alpha := pmask^.red;
1641 inc(pmask);
1642 result := ptex^;
1643 inc(ptex);
1644 result.alpha := ApplyOpacity( ApplyOpacity(result.alpha,alpha), FGlobalOpacity );
1645 end;
1646
1647begin
1648 if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count));
1649 if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count));
1650 ScannerPutPixels(FMask,@FMemMask[0],count,dmSet);
1651 ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet);
1652
1653 pmask := @FMemMask[0];
1654 ptex := @FMemTex[0];
1655
1656 if FGlobalOpacity <> 255 then
1657 begin
1658 case mode of
1659 dmDrawWithTransparency:
1660 while count > 0 do
1661 begin
1662 DrawPixelInlineWithAlphaCheck(pdest,GetNextWithGlobal);
1663 inc(pdest);
1664 dec(count);
1665 end;
1666 dmLinearBlend:
1667 while count > 0 do
1668 begin
1669 FastBlendPixelInline(pdest,GetNextWithGlobal);
1670 inc(pdest);
1671 dec(count);
1672 end;
1673 dmXor:
1674 while count > 0 do
1675 begin
1676 PDword(pdest)^ := PDword(pdest)^ xor DWord(GetNextWithGlobal);
1677 inc(pdest);
1678 dec(count);
1679 end;
1680 dmSet:
1681 while count > 0 do
1682 begin
1683 pdest^ := GetNextWithGlobal;
1684 inc(pdest);
1685 dec(count);
1686 end;
1687 dmSetExceptTransparent:
1688 while count > 0 do
1689 begin
1690 c := GetNextWithGlobal;
1691 if c.alpha = 255 then pdest^ := c;
1692 inc(pdest);
1693 dec(count);
1694 end;
1695 end;
1696 end else
1697 begin
1698 case mode of
1699 dmDrawWithTransparency:
1700 while count > 0 do
1701 begin
1702 DrawPixelInlineWithAlphaCheck(pdest,GetNext);
1703 inc(pdest);
1704 dec(count);
1705 end;
1706 dmLinearBlend:
1707 while count > 0 do
1708 begin
1709 FastBlendPixelInline(pdest,GetNext);
1710 inc(pdest);
1711 dec(count);
1712 end;
1713 dmXor:
1714 while count > 0 do
1715 begin
1716 PDword(pdest)^ := PDword(pdest)^ xor DWord(GetNext);
1717 inc(pdest);
1718 dec(count);
1719 end;
1720 dmSet:
1721 while count > 0 do
1722 begin
1723 pdest^ := GetNext;
1724 inc(pdest);
1725 dec(count);
1726 end;
1727 dmSetExceptTransparent:
1728 while count > 0 do
1729 begin
1730 c := GetNext;
1731 if c.alpha = 255 then pdest^ := c;
1732 inc(pdest);
1733 dec(count);
1734 end;
1735 end;
1736 end;
1737end;
1738
1739procedure TBGRATextureMaskScanner.ScanMoveTo(X, Y: Integer);
1740begin
1741 FMask.ScanMoveTo(X+FOffset.X,Y+FOffset.Y);
1742 FTexture.ScanMoveTo(X,Y);
1743end;
1744
1745function TBGRATextureMaskScanner.ScanNextPixel: TBGRAPixel;
1746var alpha: byte;
1747begin
1748 alpha := FMaskScanNext.red;
1749 result := FTextureScanNext();
1750 result.alpha := ApplyOpacity( ApplyOpacity(result.alpha,alpha), FGlobalOpacity );
1751end;
1752
1753function TBGRATextureMaskScanner.ScanAt(X, Y: Single): TBGRAPixel;
1754var alpha: byte;
1755begin
1756 alpha := FMaskScanAt(X+FOffset.X,Y+FOffset.Y).red;
1757 result := FTextureScanAt(X,Y);
1758 result.alpha := ApplyOpacity( ApplyOpacity(result.alpha,alpha), FGlobalOpacity );
1759end;
1760
1761{ TBGRASolidColorMaskScanner }
1762
1763constructor TBGRASolidColorMaskScanner.Create(AMask: TBGRACustomBitmap;
1764 AOffset: TPoint; ASolidColor: TBGRAPixel);
1765begin
1766 FMask := AMask;
1767 FScanNext := @FMask.ScanNextPixel;
1768 FScanAt := @FMask.ScanAt;
1769 FOffset := AOffset;
1770 FSolidColor := ASolidColor;
1771end;
1772
1773destructor TBGRASolidColorMaskScanner.Destroy;
1774begin
1775 fillchar(FMask,sizeof(FMask),0); //avoids interface deref
1776 inherited Destroy;
1777end;
1778
1779function TBGRASolidColorMaskScanner.IsScanPutPixelsDefined: boolean;
1780begin
1781 Result:= true;
1782end;
1783
1784procedure TBGRASolidColorMaskScanner.ScanPutPixels(pdest: PBGRAPixel;
1785 count: integer; mode: TDrawMode);
1786var c: TBGRAPixel;
1787 alpha: byte;
1788 pmask: pbgrapixel;
1789
1790 function GetNext: TBGRAPixel; inline;
1791 begin
1792 alpha := pmask^.red;
1793 inc(pmask);
1794 result := FSolidColor;
1795 result.alpha := ApplyOpacity(result.alpha,alpha);
1796 end;
1797
1798begin
1799 if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count));
1800 ScannerPutPixels(FMask,@FMemMask[0],count,dmSet);
1801
1802 pmask := @FMemMask[0];
1803
1804 case mode of
1805 dmDrawWithTransparency:
1806 while count > 0 do
1807 begin
1808 DrawPixelInlineWithAlphaCheck(pdest,GetNext);
1809 inc(pdest);
1810 dec(count);
1811 end;
1812 dmLinearBlend:
1813 while count > 0 do
1814 begin
1815 FastBlendPixelInline(pdest,GetNext);
1816 inc(pdest);
1817 dec(count);
1818 end;
1819 dmXor:
1820 while count > 0 do
1821 begin
1822 PDword(pdest)^ := PDword(pdest)^ xor DWord(GetNext);
1823 inc(pdest);
1824 dec(count);
1825 end;
1826 dmSet:
1827 while count > 0 do
1828 begin
1829 pdest^ := GetNext;
1830 inc(pdest);
1831 dec(count);
1832 end;
1833 dmSetExceptTransparent:
1834 while count > 0 do
1835 begin
1836 c := GetNext;
1837 if c.alpha = 255 then pdest^ := c;
1838 inc(pdest);
1839 dec(count);
1840 end;
1841 end;
1842end;
1843
1844procedure TBGRASolidColorMaskScanner.ScanMoveTo(X, Y: Integer);
1845begin
1846 FMask.ScanMoveTo(X+FOffset.X,Y+FOffset.Y);
1847end;
1848
1849function TBGRASolidColorMaskScanner.ScanNextPixel: TBGRAPixel;
1850var alpha: byte;
1851begin
1852 alpha := FScanNext.red;
1853 result := FSolidColor;
1854 result.alpha := ApplyOpacity(result.alpha,alpha);
1855end;
1856
1857function TBGRASolidColorMaskScanner.ScanAt(X, Y: Single): TBGRAPixel;
1858var alpha: byte;
1859begin
1860 alpha := FScanAt(X+FOffset.X,Y+FOffset.Y).red;
1861 result := FSolidColor;
1862 result.alpha := ApplyOpacity(result.alpha,alpha);
1863end;
1864
1865{ TBGRAOpacityScanner }
1866
1867constructor TBGRAOpacityScanner.Create(ATexture: IBGRAScanner;
1868 AGlobalOpacity: Byte);
1869begin
1870 FTexture := ATexture;
1871 FScanNext := @FTexture.ScanNextPixel;
1872 FScanAt := @FTexture.ScanAt;
1873 FGlobalOpacity:= AGlobalOpacity;
1874 FOwnedScanner := nil;
1875end;
1876
1877constructor TBGRAOpacityScanner.Create(ATexture: TBGRACustomScanner;
1878 AGlobalOpacity: Byte; AOwned: boolean);
1879begin
1880 FTexture := ATexture;
1881 FScanNext := @FTexture.ScanNextPixel;
1882 FScanAt := @FTexture.ScanAt;
1883 FGlobalOpacity:= AGlobalOpacity;
1884 if AOwned then
1885 FOwnedScanner := ATexture
1886 else
1887 FOwnedScanner := nil;
1888end;
1889
1890destructor TBGRAOpacityScanner.Destroy;
1891begin
1892 fillchar(FTexture,sizeof(FTexture),0);
1893 FOwnedScanner.Free;
1894 inherited Destroy;
1895end;
1896
1897function TBGRAOpacityScanner.IsScanPutPixelsDefined: boolean;
1898begin
1899 Result:= true;
1900end;
1901
1902procedure TBGRAOpacityScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
1903 mode: TDrawMode);
1904var c: TBGRAPixel;
1905 ptex: pbgrapixel;
1906
1907 function GetNext: TBGRAPixel; inline;
1908 begin
1909 result := ptex^;
1910 inc(ptex);
1911 result.alpha := ApplyOpacity(result.alpha,FGlobalOpacity);
1912 end;
1913
1914begin
1915 if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count));
1916 ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet);
1917
1918 ptex := @FMemTex[0];
1919
1920 case mode of
1921 dmDrawWithTransparency:
1922 while count > 0 do
1923 begin
1924 DrawPixelInlineWithAlphaCheck(pdest,GetNext);
1925 inc(pdest);
1926 dec(count);
1927 end;
1928 dmLinearBlend:
1929 while count > 0 do
1930 begin
1931 FastBlendPixelInline(pdest,GetNext);
1932 inc(pdest);
1933 dec(count);
1934 end;
1935 dmXor:
1936 while count > 0 do
1937 begin
1938 PDword(pdest)^ := PDword(pdest)^ xor DWord(GetNext);
1939 inc(pdest);
1940 dec(count);
1941 end;
1942 dmSet:
1943 while count > 0 do
1944 begin
1945 pdest^ := GetNext;
1946 inc(pdest);
1947 dec(count);
1948 end;
1949 dmSetExceptTransparent:
1950 while count > 0 do
1951 begin
1952 c := GetNext;
1953 if c.alpha = 255 then pdest^ := c;
1954 inc(pdest);
1955 dec(count);
1956 end;
1957 end;
1958end;
1959
1960procedure TBGRAOpacityScanner.ScanMoveTo(X, Y: Integer);
1961begin
1962 FTexture.ScanMoveTo(X,Y);
1963end;
1964
1965function TBGRAOpacityScanner.ScanNextPixel: TBGRAPixel;
1966begin
1967 result := FScanNext();
1968 result.alpha := ApplyOpacity(result.alpha, FGlobalOpacity );
1969end;
1970
1971function TBGRAOpacityScanner.ScanAt(X, Y: Single): TBGRAPixel;
1972begin
1973 result := FScanAt(X,Y);
1974 result.alpha := ApplyOpacity(result.alpha, FGlobalOpacity );
1975end;
1976
1977initialization
1978
1979 Randomize;
1980
1981end.
1982
Note: See TracBrowser for help on using the repository browser.