Changeset 494 for GraphicTest
- Timestamp:
- Dec 22, 2016, 8:49:19 PM (8 years ago)
- Location:
- GraphicTest
- Files:
-
- 43 added
- 7 deleted
- 78 edited
Legend:
- Unmodified
- Added
- Removed
-
GraphicTest/GraphicTest.lpi
r472 r494 91 91 <HasResources Value="True"/> 92 92 <ResourceBaseClass Value="Form"/> 93 <UnitName Value="UMainForm"/>94 93 </Unit1> 95 94 <Unit2> 96 95 <Filename Value="UPlatform.pas"/> 97 96 <IsPartOfProject Value="True"/> 98 <UnitName Value="UPlatform"/>99 97 </Unit2> 100 98 <Unit3> 101 99 <Filename Value="UDrawMethod.pas"/> 102 100 <IsPartOfProject Value="True"/> 103 <UnitName Value="UDrawMethod"/>104 101 </Unit3> 105 102 <Unit4> 106 103 <Filename Value="UFastBitmap.pas"/> 107 104 <IsPartOfProject Value="True"/> 108 <UnitName Value="UFastBitmap"/>109 105 </Unit4> 110 106 <Unit5> … … 114 110 <HasResources Value="True"/> 115 111 <ResourceBaseClass Value="Form"/> 116 <UnitName Value="UDrawForm"/>117 112 </Unit5> 118 113 <Unit6> 119 114 <Filename Value="Methods/UCanvasPixels.pas"/> 120 115 <IsPartOfProject Value="True"/> 121 <UnitName Value="UCanvasPixels"/>122 116 </Unit6> 123 117 <Unit7> … … 128 122 <Filename Value="Methods/ULazIntfImageColorsCopy.pas"/> 129 123 <IsPartOfProject Value="True"/> 130 <UnitName Value="ULazIntfImageColorsCopy"/>131 124 </Unit8> 132 125 <Unit9> … … 137 130 <Filename Value="Methods/UBGRABitmapPaintBox.pas"/> 138 131 <IsPartOfProject Value="True"/> 139 <UnitName Value="UBGRABitmapPaintBox"/>140 132 </Unit10> 141 133 <Unit11> 142 134 <Filename Value="Methods/UBitmapRawImageDataPaintBox.pas"/> 143 135 <IsPartOfProject Value="True"/> 144 <UnitName Value="UBitmapRawImageDataPaintBox"/>145 136 </Unit11> 146 137 <Unit12> … … 159 150 <Filename Value="Methods/UOpenGLMethod.pas"/> 160 151 <IsPartOfProject Value="True"/> 161 <UnitName Value="UOpenGLMethod"/>162 152 </Unit15> 163 153 <Unit16> 164 154 <Filename Value="Methods/UOpenGLPBOMethod.pas"/> 165 155 <IsPartOfProject Value="True"/> 166 <UnitName Value="UOpenGLPBOMethod"/>167 156 </Unit16> 168 157 <Unit17> 169 158 <Filename Value="Methods/UGraphics32Method.pas"/> 170 159 <IsPartOfProject Value="True"/> 171 <UnitName Value="UGraphics32Method"/>172 160 </Unit17> 173 161 </Units> -
GraphicTest/GraphicTest.lpr
r471 r494 8 8 {$ENDIF}{$ENDIF} 9 9 Interfaces, // this includes the LCL widgetset 10 Forms, lazopenglcontext, UMainForm, UPlatform, UDrawMethod, UFastBitmap,10 Forms, openglcontext, UMainForm, UPlatform, UDrawMethod, UFastBitmap, 11 11 UDrawForm, bgrabitmappack, 12 12 {$IFDEF GRAPHICS32}GR32_L,{$ENDIF} -
GraphicTest/Packages/bgrabitmap/bgraanimatedgif.pas
r472 r494 2 2 3 3 {$mode objfpc}{$H+} 4 {$i bgrabitmap.inc} 4 5 5 6 interface 6 7 7 8 uses 8 Classes, SysUtils, Graphics, FPImage, BGRABitmap, BGRABitmapTypes; 9 Classes, SysUtils, BGRAGraphics, FPImage, BGRABitmap, BGRABitmapTypes, 10 BGRAPalette, BGRAGifFormat; 9 11 10 12 type 11 TDisposeMode = (dmNone, dmKeep, dmErase, dmRestore); 12 13 TGifSubImage = record 14 Image: TBGRABitmap; 15 Position: TPoint; 16 Delay: integer; 17 disposeMode: TDisposeMode; 18 TransparentColor: TBGRAPixel; 19 end; 20 TGifSubImageArray = array of TGifSubImage; 21 13 TDisposeMode = BGRAGifFormat.TDisposeMode; 14 TGifSubImage = BGRAGifFormat.TGifSubImage; 15 TGifSubImageArray = BGRAGifFormat.TGifSubImageArray; 16 17 //how to deal with the background under the GIF animation 22 18 TGifBackgroundMode = (gbmSimplePaint, gbmEraseBackground, 23 19 gbmSaveBackgroundOnce, gbmUpdateBackgroundContinuously); … … 27 23 TBGRAAnimatedGif = class(TGraphic) 28 24 private 25 FAspectRatio: single; 29 26 FWidth, FHeight: integer; 30 27 FBackgroundColor: TColor; … … 34 31 FTimeAccumulator: double; 35 32 FCurrentImage, FWantedImage: integer; 36 F FullAnimationTime: double;33 FTotalAnimationTime: int64; 37 34 FPreviousDisposeMode: TDisposeMode; 38 35 … … 41 38 FImageChanged: boolean; 42 39 40 procedure CheckFrameIndex(AIndex: integer); 43 41 function GetCount: integer; 42 function GetFrameDelayMs(AIndex: integer): integer; 43 function GetFrameDisposeMode(AIndex: integer): TDisposeMode; 44 function GetFrameHasLocalPalette(AIndex: integer): boolean; 45 function GetFrameImage(AIndex: integer): TBGRABitmap; 46 function GetFrameImagePos(AIndex: integer): TPoint; 44 47 function GetTimeUntilNextImage: integer; 45 48 procedure Render(StretchWidth, StretchHeight: integer); 49 procedure SetAspectRatio(AValue: single); 50 procedure SetBackgroundColor(AValue: TColor); 51 procedure SetFrameDelayMs(AIndex: integer; AValue: integer); 52 procedure SetFrameDisposeMode(AIndex: integer; AValue: TDisposeMode); 53 procedure SetFrameHasLocalPalette(AIndex: integer; AValue: boolean); 54 procedure SetFrameImage(AIndex: integer; AValue: TBGRABitmap); 55 procedure SetFrameImagePos(AIndex: integer; AValue: TPoint); 46 56 procedure UpdateSimple(Canvas: TCanvas; ARect: TRect; 47 57 DrawOnlyIfChanged: boolean = True); … … 56 66 protected 57 67 FImages: TGifSubImageArray; 58 procedure LoadImages(stream: TStream);59 68 60 69 {TGraphic} … … 64 73 function GetTransparent: boolean; override; 65 74 function GetWidth: integer; override; 66 procedure SetHeight(Value: integer); override; 67 procedure SetTransparent(Value: boolean); override; 68 procedure SetWidth(Value: integer); override; 75 procedure SetHeight({%H-}Value: integer); override; 76 procedure SetTransparent({%H-}Value: boolean); override; 77 procedure SetWidth({%H-}Value: integer); override; 78 procedure ClearViewer; virtual; 69 79 70 80 public … … 76 86 constructor Create; override; 77 87 function Duplicate: TBGRAAnimatedGif; 88 function AddFrame(AImage: TFPCustomImage; X,Y: integer; ADelayMs: integer; 89 ADisposeMode: TDisposeMode = dmErase; AHasLocalPalette: boolean = false) : integer; 90 procedure InsertFrame(AIndex: integer; AImage: TBGRABitmap; X,Y: integer; ADelayMs: integer; 91 ADisposeMode: TDisposeMode = dmErase; AHasLocalPalette: boolean = false); 78 92 79 93 {TGraphic} … … 84 98 class function GetFileExtensions: string; override; 85 99 100 procedure SetSize(AWidth,AHeight: integer); virtual; 101 procedure SaveToStream(Stream: TStream; AQuantizer: TBGRAColorQuantizerAny; 102 ADitheringAlgorithm: TDitheringAlgorithm); virtual; overload; 86 103 procedure Clear; override; 87 104 destructor Destroy; override; … … 93 110 procedure Hide(Canvas: TCanvas; ARect: TRect); overload; 94 111 95 property BackgroundColor: TColor Read FBackgroundColor ;112 property BackgroundColor: TColor Read FBackgroundColor write SetBackgroundColor; 96 113 property Count: integer Read GetCount; 97 114 property Width: integer Read FWidth; … … 102 119 property CurrentImage: integer Read FCurrentImage Write SetCurrentImage; 103 120 property TimeUntilNextImageMs: integer read GetTimeUntilNextImage; 104 end; 105 106 { TFPReaderGIF } 107 108 TFPReaderGIF = class(TFPCustomImageReader) 121 property FrameImage[AIndex: integer]: TBGRABitmap read GetFrameImage write SetFrameImage; 122 property FrameHasLocalPalette[AIndex: integer]: boolean read GetFrameHasLocalPalette write SetFrameHasLocalPalette; 123 property FrameImagePos[AIndex: integer]: TPoint read GetFrameImagePos write SetFrameImagePos; 124 property FrameDelayMs[AIndex: integer]: integer read GetFrameDelayMs write SetFrameDelayMs; 125 property FrameDisposeMode[AIndex: integer]: TDisposeMode read GetFrameDisposeMode write SetFrameDisposeMode; 126 property AspectRatio: single read FAspectRatio write SetAspectRatio; 127 property TotalAnimationTimeMs: Int64 read FTotalAnimationTime; 128 end; 129 130 { TBGRAReaderGIF } 131 132 TBGRAReaderGIF = class(TFPCustomImageReader) 109 133 protected 110 134 procedure InternalRead(Str: TStream; Img: TFPCustomImage); override; 111 135 function InternalCheck(Str: TStream): boolean; override; 136 end; 137 138 { TBGRAWriterGIF } 139 140 TBGRAWriterGIF = class(TFPCustomImageWriter) 141 protected 142 procedure InternalWrite(Str: TStream; Img: TFPCustomImage); override; 112 143 end; 113 144 … … 119 150 implementation 120 151 121 uses BGRABlend, lazutf8classes;152 uses BGRABlend, BGRAUTF8{$IFDEF BGRABITMAP_USE_LCL}, Graphics{$ENDIF}; 122 153 123 154 const … … 128 159 {$ENDIF} 129 160 130 type131 TGIFSignature = packed array[1..6] of char;132 133 TGIFScreenDescriptor = packed record134 Width, Height: word;135 flags, background, map: byte;136 end;137 138 TGIFImageDescriptor = packed record139 x, y, Width, Height: word;140 flags: byte;141 end;142 143 TGIFExtensionBlock = packed record144 functioncode: byte;145 end;146 147 TGIFGraphicControlExtension = packed record148 flags: byte;149 delaytime: word;150 transcolor: byte;151 end;152 161 153 162 { TBGRAAnimatedGif } … … 156 165 begin 157 166 Result := 'gif'; 167 end; 168 169 procedure TBGRAAnimatedGif.SetSize(AWidth, AHeight: integer); 170 begin 171 ClearViewer; 172 FWidth := AWidth; 173 FHeight := AHeight; 174 end; 175 176 procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream; 177 AQuantizer: TBGRAColorQuantizerAny; 178 ADitheringAlgorithm: TDitheringAlgorithm); 179 var data: TGIFData; 180 begin 181 data.Height:= Height; 182 data.Width := Width; 183 data.AspectRatio := 1; 184 data.BackgroundColor := BackgroundColor; 185 data.Images := FImages; 186 GIFSaveToStream(data, Stream, AQuantizer, ADitheringAlgorithm); 158 187 end; 159 188 … … 167 196 begin 168 197 FInternalVirtualScreen := TBGRABitmap.Create(FWidth, FHeight); 169 if Count = 0then198 if (Count = 0) and (BackgroundColor <> clNone) then 170 199 FInternalVirtualScreen.Fill(BackgroundColor) 171 200 else … … 197 226 if not FPaused then 198 227 FTimeAccumulator += (curDate - FPrevDate) * 24 * 60 * 60 * 1000; 199 if F FullAnimationTime > 0 then FTimeAccumulator:= frac(FTimeAccumulator/FFullAnimationTime)*FFullAnimationTime;228 if FTotalAnimationTime > 0 then FTimeAccumulator:= frac(FTimeAccumulator/FTotalAnimationTime)*FTotalAnimationTime; 200 229 nextImage := FCurrentImage; 201 while FTimeAccumulator > FImages[nextImage].Delay do202 begin 203 FTimeAccumulator -= FImages[nextImage].Delay ;230 while FTimeAccumulator > FImages[nextImage].DelayMs do 231 begin 232 FTimeAccumulator -= FImages[nextImage].DelayMs; 204 233 Inc(nextImage); 205 234 if nextImage >= Count then … … 244 273 FInternalVirtualScreen.PutImage(Position.X, Position.Y, Image, 245 274 dmSetExceptTransparent); 246 FPreviousDisposeMode := disposeMode;275 FPreviousDisposeMode := DisposeMode; 247 276 end; 248 277 … … 262 291 end; 263 292 293 procedure TBGRAAnimatedGif.SetAspectRatio(AValue: single); 294 begin 295 if AValue < 0.25 then AValue := 0.25; 296 if AValue > 4 then AValue := 4; 297 if FAspectRatio=AValue then Exit; 298 FAspectRatio:=AValue; 299 end; 300 301 procedure TBGRAAnimatedGif.SetBackgroundColor(AValue: TColor); 302 begin 303 if FBackgroundColor=AValue then Exit; 304 FBackgroundColor:=AValue; 305 end; 306 307 procedure TBGRAAnimatedGif.SetFrameDelayMs(AIndex: integer; AValue: integer); 308 begin 309 CheckFrameIndex(AIndex); 310 if AValue < 0 then AValue := 0; 311 FTotalAnimationTime := FTotalAnimationTime + AValue - FImages[AIndex].DelayMs; 312 FImages[AIndex].DelayMs := AValue; 313 end; 314 315 procedure TBGRAAnimatedGif.SetFrameDisposeMode(AIndex: integer; 316 AValue: TDisposeMode); 317 begin 318 CheckFrameIndex(AIndex); 319 FImages[AIndex].DisposeMode := AValue; 320 end; 321 322 procedure TBGRAAnimatedGif.SetFrameHasLocalPalette(AIndex: integer; 323 AValue: boolean); 324 begin 325 CheckFrameIndex(AIndex); 326 FImages[AIndex].HasLocalPalette := AValue; 327 328 end; 329 330 procedure TBGRAAnimatedGif.SetFrameImage(AIndex: integer; AValue: TBGRABitmap); 331 var ACopy: TBGRABitmap; 332 begin 333 CheckFrameIndex(AIndex); 334 ACopy := AValue.Duplicate as TBGRABitmap; 335 FImages[AIndex].Image.FreeReference; 336 FImages[AIndex].Image := ACopy; 337 end; 338 339 procedure TBGRAAnimatedGif.SetFrameImagePos(AIndex: integer; AValue: TPoint); 340 begin 341 CheckFrameIndex(AIndex); 342 FImages[AIndex].Position := AValue; 343 end; 344 264 345 procedure TBGRAAnimatedGif.UpdateSimple(Canvas: TCanvas; ARect: TRect; 265 346 DrawOnlyIfChanged: boolean = True); … … 284 365 end; 285 366 367 procedure TBGRAAnimatedGif.CheckFrameIndex(AIndex: integer); 368 begin 369 if (AIndex < 0) or (AIndex >= Count) then Raise ERangeError.Create('Index out of bounds'); 370 end; 371 286 372 function TBGRAAnimatedGif.GetCount: integer; 287 373 begin 288 374 Result := length(FImages); 375 end; 376 377 function TBGRAAnimatedGif.GetFrameDelayMs(AIndex: integer): integer; 378 begin 379 CheckFrameIndex(AIndex); 380 result := FImages[AIndex].DelayMs; 381 end; 382 383 function TBGRAAnimatedGif.GetFrameDisposeMode(AIndex: integer): TDisposeMode; 384 begin 385 CheckFrameIndex(AIndex); 386 result := FImages[AIndex].DisposeMode; 387 end; 388 389 function TBGRAAnimatedGif.GetFrameHasLocalPalette(AIndex: integer): boolean; 390 begin 391 CheckFrameIndex(AIndex); 392 result := FImages[AIndex].HasLocalPalette; 393 end; 394 395 function TBGRAAnimatedGif.GetFrameImage(AIndex: integer): TBGRABitmap; 396 begin 397 CheckFrameIndex(AIndex); 398 result := FImages[AIndex].Image; 399 end; 400 401 function TBGRAAnimatedGif.GetFrameImagePos(AIndex: integer): TPoint; 402 begin 403 CheckFrameIndex(AIndex); 404 result := FImages[AIndex].Position; 289 405 end; 290 406 … … 300 416 acc := FTimeAccumulator; 301 417 if not FPaused then acc += (Now- FPrevDate) * 24 * 60 * 60 * 1000; 302 if acc >= FImages[FCurrentImage].Delay then418 if acc >= FImages[FCurrentImage].DelayMs then 303 419 result := 0 304 420 else 305 result := round(FImages[FCurrentImage].Delay -FTimeAccumulator);421 result := round(FImages[FCurrentImage].DelayMs-FTimeAccumulator); 306 422 end; 307 423 end; 308 424 309 425 constructor TBGRAAnimatedGif.Create(filenameUTF8: string); 310 var311 Stream: TFileStreamUTF8;312 426 begin 313 427 inherited Create; 314 428 Init; 315 Stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead or fmShareDenyWrite); 316 LoadFromStream(Stream); 317 Stream.Free; 429 LoadFromFile(filenameUTF8); 318 430 end; 319 431 … … 348 460 end; 349 461 462 function TBGRAAnimatedGif.AddFrame(AImage: TFPCustomImage; X, Y: integer; 463 ADelayMs: integer; ADisposeMode: TDisposeMode; AHasLocalPalette: boolean 464 ): integer; 465 begin 466 result := length(FImages); 467 setlength(FImages, length(FImages)+1); 468 if ADelayMs < 0 then ADelayMs:= 0; 469 with FImages[result] do 470 begin 471 Image := TBGRABitmap.Create(AImage); 472 Position := Point(x,y); 473 DelayMs := ADelayMs; 474 HasLocalPalette := AHasLocalPalette; 475 DisposeMode := ADisposeMode; 476 end; 477 inc(FTotalAnimationTime, ADelayMs); 478 end; 479 480 procedure TBGRAAnimatedGif.InsertFrame(AIndex: integer; AImage: TBGRABitmap; X, 481 Y: integer; ADelayMs: integer; ADisposeMode: TDisposeMode; 482 AHasLocalPalette: boolean); 483 var i: integer; 484 begin 485 if (AIndex < 0) or (AIndex > Count) then 486 raise ERangeError.Create('Index out of bounds'); 487 setlength(FImages, length(FImages)+1); 488 if ADelayMs < 0 then ADelayMs:= 0; 489 for i := high(FImages) downto AIndex+1 do 490 FImages[i] := FImages[i-1]; 491 with FImages[AIndex] do 492 begin 493 Image := AImage.Duplicate as TBGRABitmap; 494 Position := Point(x,y); 495 DelayMs := ADelayMs; 496 HasLocalPalette := AHasLocalPalette; 497 DisposeMode := ADisposeMode; 498 end; 499 inc(FTotalAnimationTime, ADelayMs); 500 end; 501 350 502 procedure TBGRAAnimatedGif.LoadFromStream(Stream: TStream); 503 var data: TGIFData; 504 i: integer; 505 begin 506 data := GIFLoadFromStream(Stream); 507 508 ClearViewer; 509 Clear; 510 FWidth := data.Width; 511 FHeight := data.Height; 512 FBackgroundColor := data.BackgroundColor; 513 FAspectRatio:= data.AspectRatio; 514 515 SetLength(FImages, length(data.Images)); 516 FTotalAnimationTime:= 0; 517 for i := 0 to high(FImages) do 518 begin 519 FImages[i] := data.Images[i]; 520 FTotalAnimationTime += FImages[i].DelayMs; 521 end; 522 end; 523 524 procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream); 525 begin 526 SaveToStream(Stream, BGRAColorQuantizerFactory, daFloydSteinberg); 527 end; 528 529 procedure TBGRAAnimatedGif.LoadFromFile(const AFilenameUTF8: string); 530 var stream: TFileStreamUTF8; 531 begin 532 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite); 533 try 534 LoadFromStream(stream); 535 finally 536 Stream.Free; 537 end; 538 end; 539 540 procedure TBGRAAnimatedGif.SaveToFile(const AFilenameUTF8: string); 541 var 542 Stream: TFileStreamUTF8; 543 begin 544 Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate); 545 try 546 SaveToStream(Stream); 547 finally 548 Stream.Free; 549 end; 550 end; 551 552 procedure TBGRAAnimatedGif.Draw(ACanvas: TCanvas; const Rect: TRect); 553 begin 554 if FBackgroundImage <> nil then 555 FreeAndNil(FBackgroundImage); 556 SaveBackgroundOnce(ACanvas, Rect); 557 558 if FPreviousVirtualScreen <> nil then 559 begin 560 FPreviousVirtualScreen.FreeReference; 561 FPreviousVirtualScreen := nil; 562 end; 563 564 Render(Rect.Right - Rect.Left, Rect.Bottom - Rect.Top); 565 FStretchedVirtualScreen.Draw(ACanvas, Rect.Left, Rect.Top, false); 566 FImageChanged := False; 567 568 FPreviousVirtualScreen := TBGRABitmap(FStretchedVirtualScreen.Duplicate); 569 end; 570 571 function TBGRAAnimatedGif.GetEmpty: boolean; 572 begin 573 Result := (length(FImages) = 0); 574 end; 575 576 function TBGRAAnimatedGif.GetHeight: integer; 577 begin 578 Result := FHeight; 579 end; 580 581 function TBGRAAnimatedGif.GetTransparent: boolean; 582 begin 583 Result := True; 584 end; 585 586 function TBGRAAnimatedGif.GetWidth: integer; 587 begin 588 Result := FWidth; 589 end; 590 591 procedure TBGRAAnimatedGif.SetHeight(Value: integer); 592 begin 593 //not implemented 594 end; 595 596 procedure TBGRAAnimatedGif.SetTransparent(Value: boolean); 597 begin 598 //not implemented 599 end; 600 601 procedure TBGRAAnimatedGif.SetWidth(Value: integer); 602 begin 603 //not implemented 604 end; 605 606 procedure TBGRAAnimatedGif.ClearViewer; 351 607 begin 352 608 FCurrentImage := -1; … … 368 624 FPreviousVirtualScreen := nil; 369 625 370 EraseColor := clBlack;371 626 FPreviousDisposeMode := dmNone; 372 373 FWidth := 0;374 FHeight := 0;375 376 if Stream <> nil then377 LoadImages(Stream);378 end;379 380 procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream);381 begin382 //not implemented383 end;384 385 procedure TBGRAAnimatedGif.LoadFromFile(const AFilenameUTF8: string);386 var stream: TFileStreamUTF8;387 begin388 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);389 try390 LoadFromStream(Stream);391 finally392 Stream.Free;393 end;394 end;395 396 procedure TBGRAAnimatedGif.SaveToFile(const AFilenameUTF8: string);397 var398 Stream: TFileStreamUTF8;399 begin400 Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);401 try402 SaveToStream(Stream);403 finally404 Stream.Free;405 end;406 end;407 408 {$HINTS OFF}409 procedure TBGRAAnimatedGif.LoadImages(stream: TStream);410 411 procedure DumpData;412 var413 Count: byte;414 begin415 repeat416 stream.Read(Count, 1);417 stream.position := stream.position + Count;418 until (Count = 0) or (stream.position >= stream.size);419 end;420 421 type422 TRGB = packed record423 r, g, b: byte;424 end;425 426 TPalette = array of TBGRAPixel;427 428 function rgbToColor(rgb: TRGB): TBGRAPixel;429 begin430 Result.red := rgb.r;431 Result.green := rgb.g;432 Result.blue := rgb.b;433 Result.alpha := 255;434 end;435 436 const437 GIFScreenDescriptor_GlobalColorTableFlag = $80;438 GIFImageDescriptor_LocalColorTableFlag = $80;439 GIFImageDescriptor_InterlacedFlag = $40;440 GIFGraphicControlExtension_TransparentFlag = $01;441 442 const443 ilstart: array[1..4] of longint = (0, 4, 2, 1);444 ilstep: array[1..4] of longint = (8, 8, 4, 2);445 446 var447 NewImages: array of TGifSubImage;448 NbImages: integer;449 450 GIFSignature: TGIFSignature;451 GIFScreenDescriptor: TGIFScreenDescriptor;452 GIFBlockID: char;453 GIFImageDescriptor: TGIFImageDescriptor;454 455 globalPalette: TPalette;456 localPalette: TPalette;457 458 transcolorIndex: integer;459 delay: integer;460 disposeMode: TDisposeMode;461 462 procedure LoadGlobalPalette;463 var464 NbEntries, i: integer;465 rgb: TRGB;466 begin467 NbEntries := 1 shl (GIFScreenDescriptor.flags and $07 + 1);468 setlength(globalPalette, NbEntries);469 for i := 0 to NbEntries - 1 do470 begin471 stream.Read(rgb, 3);472 globalPalette[i] := rgbToColor(rgb);473 end;474 end;475 476 procedure LoadLocalPalette;477 var478 NbEntries, i: integer;479 rgb: TRGB;480 begin481 NbEntries := 1 shl (GIFImageDescriptor.flags and $07 + 1);482 setlength(localPalette, NbEntries);483 for i := 0 to NbEntries - 1 do484 begin485 stream.Read(rgb, 3);486 localPalette[i] := rgbToColor(rgb);487 end;488 end;489 490 procedure decodeGIFLZW(image: TBGRABitmap; const pal: TPalette; interlaced: boolean);491 var492 xd, yd: longint;493 const494 tablen = 4095;495 type496 Pstr = ^Tstr;497 498 Tstr = record499 prefix: Pstr;500 suffix: longint;501 end;502 Pstrtab = ^Tstrtab;503 Tstrtab = array[0..tablen] of Tstr;504 505 var506 strtab: Pstrtab;507 oldcode, curcode, clearcode, endcode: longint;508 codesize, codelen, codemask: longint;509 stridx: longint;510 bitbuf, bitsinbuf: longint;511 bytbuf: packed array[0..255] of byte;512 bytinbuf, bytbufidx: byte;513 endofsrc: boolean;514 xcnt, ycnt, ystep, pass: longint;515 516 procedure InitStringTable;517 var518 i: longint;519 begin520 new(strtab);521 clearcode := 1 shl codesize;522 endcode := clearcode + 1;523 stridx := endcode + 1;524 codelen := codesize + 1;525 codemask := (1 shl codelen) - 1;526 for i := 0 to clearcode - 1 do527 begin528 strtab^[i].prefix := nil;529 strtab^[i].suffix := i;530 end;531 for i := clearcode to tablen do532 begin533 strtab^[i].prefix := nil;534 strtab^[i].suffix := 0;535 end;536 end;537 538 procedure ClearStringTable;539 var540 i: longint;541 begin542 clearcode := 1 shl codesize;543 endcode := clearcode + 1;544 stridx := endcode + 1;545 codelen := codesize + 1;546 codemask := (1 shl codelen) - 1;547 for i := clearcode to tablen do548 begin549 strtab^[i].prefix := nil;550 strtab^[i].suffix := 0;551 end;552 end;553 554 procedure DoneStringTable;555 begin556 dispose(strtab);557 end;558 559 function GetNextCode: longint;560 begin561 while (bitsinbuf < codelen) do562 begin563 if (bytinbuf = 0) then564 begin565 stream.Read(bytinbuf, 1);566 if (bytinbuf = 0) then567 endofsrc := True;568 stream.Read(bytbuf, bytinbuf);569 bytbufidx := 0;570 end;571 bitbuf := bitbuf or (longint(byte(bytbuf[bytbufidx])) shl bitsinbuf);572 Inc(bytbufidx);573 Dec(bytinbuf);574 Inc(bitsinbuf, 8);575 end;576 Result := bitbuf and codemask;577 {DBG(bitbuf AND codemask);}578 bitbuf := bitbuf shr codelen;579 Dec(bitsinbuf, codelen);580 end;581 582 procedure AddStr2Tab(prefix: Pstr; suffix: longint);583 begin584 strtab^[stridx].prefix := prefix;585 strtab^[stridx].suffix := suffix;586 Inc(stridx);587 case stridx of588 0..1: codelen := 1;589 2..3: codelen := 2;590 4..7: codelen := 3;591 8..15: codelen := 4;592 16..31: codelen := 5;593 32..63: codelen := 6;594 64..127: codelen := 7;595 128..255: codelen := 8;596 256..511: codelen := 9;597 512..1023: codelen := 10;598 1024..2047: codelen := 11;599 2048..4096: codelen := 12;600 end;601 codemask := (1 shl codelen) - 1;602 end;603 604 function Code2Str(code: longint): Pstr;605 begin606 Result := addr(strtab^[code]);607 end;608 609 procedure WriteStr(s: Pstr);610 var611 colorIndex: integer;612 begin613 if (s^.prefix <> nil) then614 WriteStr(s^.prefix);615 if (ycnt >= yd) then616 begin617 if interlaced then618 begin619 while (ycnt >= yd) and (pass < 5) do620 begin621 Inc(pass);622 ycnt := ilstart[pass];623 ystep := ilstep[pass];624 end;625 end;626 end;627 628 colorIndex := s^.suffix;629 if (colorIndex <> transcolorIndex) and (colorIndex >= 0) and630 (colorIndex < length(pal)) then631 image.setpixel(xcnt, ycnt, pal[colorIndex]);632 633 Inc(xcnt);634 if (xcnt >= xd) then635 begin636 xcnt := 0;637 Inc(ycnt, ystep);638 639 if not interlaced then640 if (ycnt >= yd) then641 begin642 Inc(pass);643 end;644 645 end;646 end;647 648 function firstchar(s: Pstr): byte;649 begin650 while (s^.prefix <> nil) do651 s := s^.prefix;652 Result := s^.suffix;653 end;654 655 begin656 {DBG('lzw start');}657 endofsrc := False;658 xd := image.Width;659 yd := image.Height;660 xcnt := 0;661 if interlaced then662 begin663 pass := 1;664 ycnt := ilstart[pass];665 ystep := ilstep[pass];666 end667 else668 begin669 pass := 4;670 ycnt := 0;671 ystep := 1;672 end;673 oldcode := 0;674 bitbuf := 0;675 bitsinbuf := 0;676 bytinbuf := 0;677 bytbufidx := 0;678 codesize := 0;679 stream.Read(codesize, 1);680 {DBG(codesize);}681 InitStringTable;682 curcode := getnextcode;683 {DBG(curcode);}684 while (curcode <> endcode) and (pass < 5) and not endofsrc{ AND NOT finished} do685 begin686 {DBG('-----');687 DBG(curcode);688 DBGw(stridx);}689 if (curcode = clearcode) then690 begin691 ClearStringTable;692 repeat693 curcode := getnextcode;694 {DBG('lzw clear');}695 until (curcode <> clearcode);696 if (curcode = endcode) then697 break;698 WriteStr(code2str(curcode));699 oldcode := curcode;700 end701 else702 begin703 if (curcode < stridx) then704 begin705 WriteStr(Code2Str(curcode));706 AddStr2Tab(Code2Str(oldcode), firstchar(Code2Str(curcode)));707 oldcode := curcode;708 end709 else710 begin711 if (curcode > stridx) then712 break;713 AddStr2Tab(Code2Str(oldcode), firstchar(Code2Str(oldcode)));714 WriteStr(Code2Str(stridx - 1));715 oldcode := curcode;716 end;717 end;718 curcode := getnextcode;719 end;720 DoneStringTable;721 {putimage(0,0,image);}722 {DBG('lzw end');723 DBG(bytinbuf);}724 if not endofsrc then725 DumpData;726 {DBG('lzw finished');}727 end;728 729 procedure LoadImage;730 var731 imgWidth, imgHeight: integer;732 img: TBGRABitmap;733 Interlaced: boolean;734 palette: TPalette;735 begin736 stream.Read(GIFImageDescriptor, sizeof(GIFImageDescriptor));737 GIFImageDescriptor.Width := LEtoN(GIFImageDescriptor.Width);738 GIFImageDescriptor.Height := LEtoN(GIFImageDescriptor.Height);739 GIFImageDescriptor.x := LEtoN(GIFImageDescriptor.x);740 GIFImageDescriptor.y := LEtoN(GIFImageDescriptor.y);741 if (GIFImageDescriptor.flags and GIFImageDescriptor_LocalColorTableFlag =742 GIFImageDescriptor_LocalColorTableFlag) then743 LoadLocalPalette744 else745 localPalette := nil;746 747 if localPalette <> nil then748 palette := localPalette749 else750 palette := globalPalette;751 imgWidth := GIFImageDescriptor.Width;752 imgHeight := GIFImageDescriptor.Height;753 754 if length(NewImages) <= NbImages then755 setlength(NewImages, length(NewImages) * 2 + 1);756 img := TBGRABitmap.Create(imgWidth, imgHeight);757 img.Fill(BGRAPixelTransparent);758 NewImages[NbImages].Image := img;759 NewImages[NbImages].Position := point(GIFImageDescriptor.x, GIFImageDescriptor.y);760 NewImages[NbImages].Delay := Delay;761 NewImages[NbImages].disposeMode := disposeMode;762 763 if (transcolorIndex >= 0) and (transcolorIndex < length(palette)) then764 NewImages[nbImages].TransparentColor := palette[transcolorIndex]765 else766 NewImages[nbImages].TransparentColor := BGRAPixelTransparent;767 768 Inc(NbImages);769 770 Interlaced := GIFImageDescriptor.flags and GIFImageDescriptor_InterlacedFlag =771 GIFImageDescriptor_InterlacedFlag;772 DecodeGIFLZW(img, palette, Interlaced);773 end;774 775 procedure ChangeImages;776 var777 i: integer;778 begin779 Clear;780 SetLength(FImages, NbImages);781 FFullAnimationTime:= 0;782 for i := 0 to Count - 1 do783 begin784 FImages[i] := NewImages[i];785 FFullAnimationTime += NewImages[i].Delay;786 end;787 end;788 789 procedure ReadExtension;790 var791 GIFExtensionBlock: TGIFExtensionBlock;792 GIFGraphicControlExtension: TGIFGraphicControlExtension;793 mincount, Count: byte;794 795 begin796 stream.Read(GIFExtensionBlock, sizeof(GIFExtensionBlock));797 case GIFExtensionBlock.functioncode of798 $F9:799 begin800 stream.Read(Count, 1);801 if Count < sizeof(GIFGraphicControlExtension) then802 mincount := 0803 else804 begin805 mincount := sizeof(GIFGraphicControlExtension);806 stream.Read(GIFGraphicControlExtension, mincount);807 GIFGraphicControlExtension.delaytime := LEtoN(GIFGraphicControlExtension.delaytime);808 809 if GIFGraphicControlExtension.flags and810 GIFGraphicControlExtension_TransparentFlag =811 GIFGraphicControlExtension_TransparentFlag then812 transcolorIndex := GIFGraphicControlExtension.transcolor813 else814 transcolorIndex := -1;815 if GIFGraphicControlExtension.delaytime <> 0 then816 Delay := GIFGraphicControlExtension.delaytime * 10;817 disposeMode := TDisposeMode((GIFGraphicControlExtension.flags shr 2) and 7);818 end;819 stream.Position := Stream.Position + Count - mincount;820 DumpData;821 end;822 else823 begin824 DumpData;825 end;826 end;827 end;828 829 begin830 NewImages := nil;831 NbImages := 0;832 transcolorIndex := -1;833 Delay := 100;834 FBackgroundColor := clBlack;835 FWidth := 0;836 FHeight := 0;837 disposeMode := dmErase;838 839 stream.Read(GIFSignature, sizeof(GIFSignature));840 if (GIFSignature[1] = 'G') and (GIFSignature[2] = 'I') and (GIFSignature[3] = 'F') then841 begin842 stream.Read(GIFScreenDescriptor, sizeof(GIFScreenDescriptor));843 GIFScreenDescriptor.Width := LEtoN(GIFScreenDescriptor.Width);844 GIFScreenDescriptor.Height := LEtoN(GIFScreenDescriptor.Height);845 FWidth := GIFScreenDescriptor.Width;846 FHeight := GIFScreenDescriptor.Height;847 if (GIFScreenDescriptor.flags and GIFScreenDescriptor_GlobalColorTableFlag =848 GIFScreenDescriptor_GlobalColorTableFlag) then849 begin850 LoadGlobalPalette;851 if GIFScreenDescriptor.background < length(globalPalette) then852 FBackgroundColor :=853 BGRAToColor(globalPalette[GIFScreenDescriptor.background]);854 end;855 repeat856 stream.Read(GIFBlockID, sizeof(GIFBlockID));857 case GIFBlockID of858 ';': ;859 ',': LoadImage;860 '!': ReadExtension;861 else862 begin863 raise Exception.Create('TBGRAAnimatedGif: unexpected block type');864 break;865 end;866 end;867 until (GIFBlockID = ';') or (stream.Position >= stream.size);868 end869 else870 raise Exception.Create('TBGRAAnimatedGif: invalid header');871 ChangeImages;872 end;873 874 procedure TBGRAAnimatedGif.Draw(ACanvas: TCanvas; const Rect: TRect);875 begin876 if FBackgroundImage <> nil then877 FreeAndNil(FBackgroundImage);878 SaveBackgroundOnce(ACanvas, Rect);879 880 if FPreviousVirtualScreen <> nil then881 begin882 FPreviousVirtualScreen.FreeReference;883 FPreviousVirtualScreen := nil;884 end;885 886 Render(Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);887 FStretchedVirtualScreen.Draw(ACanvas, Rect.Left, Rect.Top, false);888 FImageChanged := False;889 890 FPreviousVirtualScreen := TBGRABitmap(FStretchedVirtualScreen.Duplicate);891 end;892 893 function TBGRAAnimatedGif.GetEmpty: boolean;894 begin895 Result := (length(FImages) = 0);896 end;897 898 function TBGRAAnimatedGif.GetHeight: integer;899 begin900 Result := FHeight;901 end;902 903 function TBGRAAnimatedGif.GetTransparent: boolean;904 begin905 Result := True;906 end;907 908 function TBGRAAnimatedGif.GetWidth: integer;909 begin910 Result := FWidth;911 end;912 913 procedure TBGRAAnimatedGif.SetHeight(Value: integer);914 begin915 //not implemented916 end;917 918 procedure TBGRAAnimatedGif.SetTransparent(Value: boolean);919 begin920 //not implemented921 end;922 923 procedure TBGRAAnimatedGif.SetWidth(Value: integer);924 begin925 //not implemented926 627 end; 927 628 … … 947 648 FWantedImage := Index; 948 649 end; 949 950 {$HINTS ON}951 650 952 651 procedure TBGRAAnimatedGif.Clear; … … 1045 744 end; 1046 745 1047 PChangePix := PLongWord(FPreviousVirtualScreen. ScanLine[0]);1048 PNewPix := PLongWord(FStretchedVirtualScreen. ScanLine[0]);1049 PBackground := PLongWord(FBackgroundImage. ScanLine[0]);1050 PNewBackground := PLongWord(NewBackgroundImage. ScanLine[0]);746 PChangePix := PLongWord(FPreviousVirtualScreen.Data); 747 PNewPix := PLongWord(FStretchedVirtualScreen.Data); 748 PBackground := PLongWord(FBackgroundImage.Data); 749 PNewBackground := PLongWord(NewBackgroundImage.Data); 1051 750 for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do 1052 751 begin … … 1095 794 else 1096 795 begin 1097 PChangePix := PLongWord(FPreviousVirtualScreen. ScanLine[0]);1098 PNewPix := PLongWord(FStretchedVirtualScreen. ScanLine[0]);1099 PBackground := PLongWord(FBackgroundImage. ScanLine[0]);796 PChangePix := PLongWord(FPreviousVirtualScreen.Data); 797 PNewPix := PLongWord(FStretchedVirtualScreen.Data); 798 PBackground := PLongWord(FBackgroundImage.Data); 1100 799 for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do 1101 800 begin … … 1154 853 begin 1155 854 shape := TBGRABitmap(FPreviousVirtualScreen.Duplicate); 1156 p := shape. ScanLine[0];855 p := shape.Data; 1157 856 for n := shape.NbPixels - 1 downto 0 do 1158 857 begin … … 1172 871 begin 1173 872 shape := TBGRABitmap(FPreviousVirtualScreen.Duplicate); 1174 p := shape. ScanLine[0];1175 pback := FBackgroundImage. ScanLine[0];873 p := shape.Data; 874 pback := FBackgroundImage.Data; 1176 875 for n := shape.NbPixels - 1 downto 0 do 1177 876 begin … … 1227 926 else 1228 927 begin 1229 PChangePix := PLongWord(FPreviousVirtualScreen. ScanLine[0]);1230 PNewPix := PLongWord(FStretchedVirtualScreen. ScanLine[0]);928 PChangePix := PLongWord(FPreviousVirtualScreen.Data); 929 PNewPix := PLongWord(FStretchedVirtualScreen.Data); 1231 930 for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do 1232 931 begin … … 1274 973 end; 1275 974 1276 { T FPReaderGIF }1277 1278 procedure T FPReaderGIF.InternalRead(Str: TStream; Img: TFPCustomImage);975 { TBGRAReaderGIF } 976 977 procedure TBGRAReaderGIF.InternalRead(Str: TStream; Img: TFPCustomImage); 1279 978 var 1280 979 gif: TBGRAAnimatedGif; … … 1300 999 end; 1301 1000 1302 {$HINTS OFF} 1303 function TFPReaderGIF.InternalCheck(Str: TStream): boolean; 1001 function TBGRAReaderGIF.InternalCheck(Str: TStream): boolean; 1304 1002 var 1305 1003 GIFSignature: TGIFSignature; … … 1308 1006 savepos := str.Position; 1309 1007 try 1008 fillchar({%H-}GIFSignature, sizeof(GIFSignature), 0); 1310 1009 str.Read(GIFSignature, sizeof(GIFSignature)); 1311 1010 if (GIFSignature[1] = 'G') and (GIFSignature[2] = 'I') and … … 1323 1022 end; 1324 1023 1325 {$HINTS ON} 1024 { TBGRAWriterGIF } 1025 1026 procedure TBGRAWriterGIF.InternalWrite(Str: TStream; Img: TFPCustomImage); 1027 var 1028 gif: TBGRAAnimatedGif; 1029 begin 1030 gif := TBGRAAnimatedGif.Create; 1031 try 1032 gif.SetSize(Img.Width,Img.Height); 1033 gif.AddFrame(Img, 0,0,0); 1034 gif.SaveToStream(Str, BGRAColorQuantizerFactory, daFloydSteinberg); 1035 except 1036 on ex: EColorQuantizerMissing do 1037 begin 1038 FreeAndNil(gif); 1039 raise EColorQuantizerMissing.Create('Please define the color quantizer factory. You can do that with the following statements: Uses BGRAPalette, BGRAColorQuantization; BGRAColorQuantizerFactory:= TBGRAColorQuantizer;'); 1040 end; 1041 on ex: Exception do 1042 begin 1043 FreeAndNil(gif); 1044 raise ex; 1045 end; 1046 end; 1047 FreeAndNil(gif); 1048 end; 1326 1049 1327 1050 initialization 1051 1052 DefaultBGRAImageReader[ifGif] := TBGRAReaderGIF; 1053 DefaultBGRAImageWriter[ifGif] := TBGRAWriterGIF; 1328 1054 1329 1055 //Free Pascal Image 1330 1056 ImageHandlers.RegisterImageReader('Animated GIF', TBGRAAnimatedGif.GetFileExtensions, 1331 TFPReaderGIF); 1332 1057 TBGRAReaderGIF); 1058 ImageHandlers.RegisterImageWriter('Animated GIF', TBGRAAnimatedGif.GetFileExtensions, 1059 TBGRAWriterGIF); 1060 1061 {$IFDEF BGRABITMAP_USE_LCL} 1333 1062 //Lazarus Picture 1334 1063 TPicture.RegisterFileFormat(TBGRAAnimatedGif.GetFileExtensions, 'Animated GIF', 1335 1064 TBGRAAnimatedGif); 1336 1065 {$ENDIF} 1337 1066 end. 1338 1067 -
GraphicTest/Packages/bgrabitmap/bgraarrow.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, BGRABitmapTypes, Graphics;8 Classes, SysUtils, BGRABitmapTypes, BGRAGraphics; 9 9 10 10 type 11 12 11 { TBGRAArrow } 13 12 14 TBGRAArrow = class 13 TBGRAArrow = class(TBGRACustomArrow) 15 14 private 16 15 FLineCap: TPenEndCap; … … 38 37 ATipStyle: TPenJoinStyle; ALineCap: TPenEndCap; const AWidth: single; AOffsetX: single; 39 38 ARepeatCount: integer; ARelativePenWidth: single; ATriangleBackOffset: single): ArrayOfTPointF; 40 function GetIsEndDefined: boolean;41 function GetIsStartDefined: boolean;42 procedure SetEndOffsetX(AValue: single);43 procedure SetEndRepeatCount(AValue: integer);44 procedure SetEndSizeFactor(AValue: TPointF);45 procedure SetLineCap(AValue: TPenEndCap);46 procedure SetStartOffsetX(AValue: single);47 procedure SetStartRepeatCount(AValue: integer);48 procedure SetStartSizeFactor(AValue: TPointF);49 39 procedure SetWidth(AValue: single); 40 protected 41 function GetEndRepeatCount: integer; override; 42 function GetEndSizeFactor: TPointF; override; 43 function GetIsEndDefined: boolean; override; 44 function GetIsStartDefined: boolean; override; 45 function GetEndOffsetX: single; override; 46 function GetStartOffsetX: single; override; 47 function GetStartRepeatCount: integer; override; 48 function GetStartSizeFactor: TPointF; override; 49 procedure SetEndOffsetX(AValue: single); override; 50 procedure SetEndRepeatCount(AValue: integer); override; 51 procedure SetEndSizeFactor(AValue: TPointF); override; 52 procedure SetStartOffsetX(AValue: single); override; 53 procedure SetStartRepeatCount(AValue: integer); override; 54 procedure SetStartSizeFactor(AValue: TPointF); override; 55 function GetLineCap: TPenEndCap; override; 56 procedure SetLineCap(AValue: TPenEndCap); override; 57 procedure SetStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); 58 procedure SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); 50 59 public 51 60 constructor Create; 52 procedure SetStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle; 53 ARelativePenWidth: single; ATriangleBackOffset: single); 54 procedure SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle; 55 ARelativePenWidth: single; ATriangleBackOffset: single); 56 function ComputeStartAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; 57 function ComputeEndAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; 58 property IsStartDefined: boolean read GetIsStartDefined; 59 property IsEndDefined: boolean read GetIsEndDefined; 60 property LineCap: TPenEndCap read FLineCap write SetLineCap; 61 property StartSize: TPointF read FStartSizeFactor write SetStartSizeFactor; 62 property EndSize: TPointF read FEndSizeFactor write SetEndSizeFactor; 63 property StartOffsetX: single read FStartOffsetX write SetStartOffsetX; 64 property EndOffsetX: single read FEndOffsetX write SetEndOffsetX; 65 property StartRepeatCount: integer read FStartRepeatCount write SetStartRepeatCount; 66 property EndRepeatCount: integer read FEndRepeatCount write SetEndRepeatCount; 61 procedure StartAsNone; override; 62 procedure StartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override; 63 procedure StartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override; 64 procedure StartAsTail; override; 65 procedure EndAsNone; override; 66 procedure EndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override; 67 procedure EndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override; 68 procedure EndAsTail; override; 69 function ComputeStartAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override; 70 function ComputeEndAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override; 71 67 72 end; 68 73 … … 258 263 end; 259 264 265 function TBGRAArrow.GetEndOffsetX: single; 266 begin 267 result := FEndOffsetX; 268 end; 269 270 function TBGRAArrow.GetStartOffsetX: single; 271 begin 272 result := FStartOffsetX; 273 end; 274 275 function TBGRAArrow.GetStartRepeatCount: integer; 276 begin 277 result := FStartRepeatCount; 278 end; 279 280 function TBGRAArrow.GetStartSizeFactor: TPointF; 281 begin 282 result := FStartSizeFactor; 283 end; 284 260 285 procedure TBGRAArrow.SetEndOffsetX(AValue: single); 261 286 begin … … 264 289 FEndComputed:= false; 265 290 FEnd := nil; 291 end; 292 293 function TBGRAArrow.GetLineCap: TPenEndCap; 294 begin 295 result := FLineCap; 266 296 end; 267 297 … … 324 354 end; 325 355 356 function TBGRAArrow.GetEndRepeatCount: integer; 357 begin 358 Result:= FEndRepeatCount; 359 end; 360 361 function TBGRAArrow.GetEndSizeFactor: TPointF; 362 begin 363 Result:= FEndSizeFactor; 364 end; 365 326 366 constructor TBGRAArrow.Create; 327 367 begin … … 329 369 FStartSizeFactor := PointF(2,2); 330 370 FEndSizeFactor := PointF(2,2); 371 end; 372 373 procedure TBGRAArrow.StartAsNone; 374 begin 375 SetStart(asNone); 376 end; 377 378 procedure TBGRAArrow.StartAsClassic(AFlipped: boolean; ACut: boolean; 379 ARelativePenWidth: single); 380 var join: TPenJoinStyle; 381 begin 382 if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter; 383 if ACut then 384 begin 385 if AFlipped then 386 SetStart(asFlippedCut,join,ARelativePenWidth) 387 else 388 SetStart(asCut,join,ARelativePenWidth) 389 end 390 else 391 begin 392 if AFlipped then 393 SetStart(asFlipped,join,ARelativePenWidth) 394 else 395 SetStart(asNormal,join,ARelativePenWidth) 396 end; 397 end; 398 399 procedure TBGRAArrow.StartAsTriangle(ABackOffset: single; ARounded: boolean; 400 AHollow: boolean; AHollowPenWidth: single); 401 var join: TPenJoinStyle; 402 begin 403 if ARounded then join := pjsRound else join := pjsMiter; 404 if AHollow then 405 SetStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset) 406 else 407 SetStart(asTriangle, join,1,ABackOffset); 408 end; 409 410 procedure TBGRAArrow.StartAsTail; 411 begin 412 SetStart(asTail); 413 end; 414 415 procedure TBGRAArrow.EndAsNone; 416 begin 417 SetEnd(asNone); 418 end; 419 420 procedure TBGRAArrow.EndAsClassic(AFlipped: boolean; ACut: boolean; 421 ARelativePenWidth: single); 422 var join: TPenJoinStyle; 423 begin 424 if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter; 425 if ACut then 426 begin 427 if AFlipped then 428 SetEnd(asFlippedCut,join,ARelativePenWidth) 429 else 430 SetEnd(asCut,join,ARelativePenWidth) 431 end 432 else 433 begin 434 if AFlipped then 435 SetEnd(asFlipped,join,ARelativePenWidth) 436 else 437 SetEnd(asNormal,join,ARelativePenWidth) 438 end; 439 end; 440 441 procedure TBGRAArrow.EndAsTriangle(ABackOffset: single; ARounded: boolean; 442 AHollow: boolean; AHollowPenWidth: single); 443 var join: TPenJoinStyle; 444 begin 445 if ARounded then join := pjsRound else join := pjsMiter; 446 if AHollow then 447 SetEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset) 448 else 449 SetEnd(asTriangle, join,1, ABackOffset); 450 end; 451 452 procedure TBGRAArrow.EndAsTail; 453 begin 454 SetEnd(asTail); 331 455 end; 332 456 -
GraphicTest/Packages/bgrabitmap/bgrabitmap.pas
r472 r494 5 5 Free easy-to-use memory bitmap 32-bit, 6 6 8-bit for each channel, transparency. 7 Channels in that order : B G R A 7 Channels can be in the following orders: 8 - B G R A (recommended for Windows, required for fpGUI) 9 - R G B A (recommended for Gtk and MacOS) 8 10 9 11 - Drawing primitives … … 38 40 39 41 {$mode objfpc}{$H+} 42 {$i bgrabitmap.inc} 40 43 41 44 interface … … 46 49 uses 47 50 Classes, SysUtils, 48 {$IFDEF LCLwin32}49 BGRAWinBitmap,51 {$IFDEF BGRABITMAP_USE_FPGUI} 52 BGRAfpGUIBitmap, 50 53 {$ELSE} 51 {$IFDEF LCLgtk} 52 BGRAGtkBitmap, 53 {$ELSE} 54 {$IFDEF LCLgtk2} 55 BGRAGtkBitmap, 56 {$ELSE} 57 {$IFDEF LCLqt} 58 BGRAQtBitmap, 59 {$ELSE} 60 BGRADefaultBitmap, 61 {$ENDIF} 62 {$ENDIF} 63 {$ENDIF} 54 {$IFDEF BGRABITMAP_USE_LCL} 55 {$IFDEF LCLwin32} 56 BGRAWinBitmap, 57 {$ELSE} 58 {$IFDEF LCLgtk} 59 BGRAGtkBitmap, 60 {$ELSE} 61 {$IFDEF LCLgtk2} 62 BGRAGtkBitmap, 63 {$ELSE} 64 {$IFDEF LCLqt} 65 BGRAQtBitmap, 66 {$ELSE} 67 {$IFDEF DARWIN} 68 BGRAMacBitmap, 69 {$ELSE} 70 BGRALCLBitmap, 71 {$ENDIF} 72 {$ENDIF} 73 {$ENDIF} 74 {$ENDIF} 75 {$ENDIF} 76 {$ELSE} 77 BGRANoGuiBitmap, 78 {$ENDIF} 64 79 {$ENDIF} 65 Graphics;80 BGRAGraphics; 66 81 67 82 type 68 {$IFDEF LCLwin32}69 TBGRABitmap = TBGRAWinBitmap;83 {$IFDEF BGRABITMAP_USE_FPGUI} 84 TBGRABitmap = class(TBGRAfpGUIBitmap); 70 85 {$ELSE} 71 {$IFDEF LCLgtk} 72 TBGRABitmap = TBGRAGtkBitmap; 73 {$ELSE} 74 {$IFDEF LCLgtk2} 75 TBGRABitmap = TBGRAGtkBitmap; 86 {$IFDEF BGRABITMAP_USE_LCL} 87 {$IFDEF LCLwin32} 88 TBGRABitmap = class(TBGRAWinBitmap); 89 {$ELSE} 90 {$IFDEF LCLgtk} 91 TBGRABitmap = class(TBGRAGtkBitmap); 92 {$ELSE} 93 {$IFDEF LCLgtk2} 94 TBGRABitmap = class(TBGRAGtkBitmap); 95 {$ELSE} 96 {$IFDEF LCLqt} 97 TBGRABitmap = class(TBGRAQtBitmap); 98 {$ELSE} 99 {$IFDEF DARWIN} 100 TBGRABitmap = class(TBGRAMacBitmap); 101 {$ELSE} 102 TBGRABitmap = class(TBGRALCLBitmap); 103 {$ENDIF} 104 {$ENDIF} 105 {$ENDIF} 106 {$ENDIF} 107 {$ENDIF} 76 108 {$ELSE} 77 {$IFDEF LCLqt} 78 TBGRABitmap = TBGRAQtBitmap; 79 {$ELSE} 80 TBGRABitmap = TBGRADefaultBitmap; 81 {$ENDIF} 109 TBGRABitmap = class(TBGRANoGUIBitmap); 82 110 {$ENDIF} 83 {$ENDIF}84 111 {$ENDIF} 85 112 … … 113 140 implementation 114 141 115 uses GraphType, BGRABitmapTypes, BGRAReadBMP, BGRAReadGif,116 BGRAReadIco, bgrareadjpeg, BGRAReadLzp, BGRAReadPCX,142 uses BGRABitmapTypes, BGRAReadBMP, BGRAReadBmpMioMap, BGRAReadGif, 143 BGRAReadIco, BGRAReadJpeg, BGRAReadLzp, BGRAReadPCX, 117 144 BGRAReadPng, BGRAReadPSD, BGRAReadTGA, BGRAReadXPM, 118 145 BGRAWriteLzp; -
GraphicTest/Packages/bgrabitmap/bgrabitmappack.lpk
r472 r494 1 <?xml version="1.0" ?>1 <?xml version="1.0" encoding="UTF-8"?> 2 2 <CONFIG> 3 3 <Package Version="4"> 4 4 <PathDelim Value="\"/> 5 5 <Name Value="BGRABitmapPack"/> 6 <AddToProjectUsesSection Value="True"/>7 6 <Author Value="Circular"/> 8 7 <CompilerOptions> … … 19 18 <CodeGeneration> 20 19 <Optimizations> 20 <OptimizationLevel Value="3"/> 21 21 <VariablesInRegisters Value="True"/> 22 <OptimizationLevel Value="3"/>23 22 </Optimizations> 24 23 </CodeGeneration> … … 28 27 </Debugging> 29 28 </Linking> 30 <Other>31 <CompilerMessages>32 <UseMsgFile Value="True"/>33 </CompilerMessages>34 <CompilerPath Value="$(CompPath)"/>35 </Other>36 29 </CompilerOptions> 37 30 <Description Value="Drawing routines with alpha blending and antialiasing"/> 38 31 <License Value="modified LGPL"/> 39 <Version Major=" 8" Minor="1"/>40 <Files Count=" 91">32 <Version Major="9" Minor="3"/> 33 <Files Count="108"> 41 34 <Item1> 42 35 <Filename Value="bgraanimatedgif.pas"/> … … 148 141 </Item27> 149 142 <Item28> 150 <Filename Value="bgras cene3dinterface.inc"/>151 < Type Value="Binary"/>143 <Filename Value="bgraslicescaling.pas"/> 144 <UnitName Value="BGRASliceScaling"/> 152 145 </Item28> 153 146 <Item29> 154 <Filename Value="bgras licescaling.pas"/>155 <UnitName Value="BGRAS liceScaling"/>147 <Filename Value="bgrasse.pas"/> 148 <UnitName Value="BGRASSE"/> 156 149 </Item29> 157 150 <Item30> 158 <Filename Value="bgras se.pas"/>159 <UnitName Value="BGRAS SE"/>151 <Filename Value="bgrastreamlayers.pas"/> 152 <UnitName Value="BGRAStreamLayers"/> 160 153 </Item30> 161 154 <Item31> 162 <Filename Value="bgra streamlayers.pas"/>163 <UnitName Value="BGRA StreamLayers"/>155 <Filename Value="bgratext.pas"/> 156 <UnitName Value="BGRAText"/> 164 157 </Item31> 165 158 <Item32> 166 <Filename Value="bgratext .pas"/>167 <UnitName Value="BGRAText "/>159 <Filename Value="bgratextfx.pas"/> 160 <UnitName Value="BGRATextFX"/> 168 161 </Item32> 169 162 <Item33> 170 <Filename Value="bgrat extfx.pas"/>171 <UnitName Value="BGRAT extFX"/>163 <Filename Value="bgratransform.pas"/> 164 <UnitName Value="BGRATransform"/> 172 165 </Item33> 173 166 <Item34> 174 <Filename Value="bgrat ransform.pas"/>175 <UnitName Value="BGRAT ransform"/>167 <Filename Value="bgratypewriter.pas"/> 168 <UnitName Value="BGRATypewriter"/> 176 169 </Item34> 177 170 <Item35> 178 <Filename Value="bgra typewriter.pas"/>179 <UnitName Value="BGRA Typewriter"/>171 <Filename Value="bgravectorize.pas"/> 172 <UnitName Value="BGRAVectorize"/> 180 173 </Item35> 181 174 <Item36> 182 <Filename Value="b gravectorize.pas"/>183 < UnitName Value="BGRAVectorize"/>175 <Filename Value="blendpixelinline.inc"/> 176 <Type Value="Binary"/> 184 177 </Item36> 185 178 <Item37> 186 <Filename Value="blendpixel inline.inc"/>179 <Filename Value="blendpixels.inc"/> 187 180 <Type Value="Binary"/> 188 181 </Item37> 189 182 <Item38> 190 <Filename Value="blendpixels .inc"/>183 <Filename Value="blendpixelsover.inc"/> 191 184 <Type Value="Binary"/> 192 185 </Item38> 193 186 <Item39> 194 <Filename Value="bl endpixelsover.inc"/>187 <Filename Value="blurfast.inc"/> 195 188 <Type Value="Binary"/> 196 189 </Item39> 197 190 <Item40> 198 <Filename Value="blur fast.inc"/>191 <Filename Value="blurnormal.inc"/> 199 192 <Type Value="Binary"/> 200 193 </Item40> 201 194 <Item41> 202 <Filename Value=" blurnormal.inc"/>195 <Filename Value="csscolorconst.inc"/> 203 196 <Type Value="Binary"/> 204 197 </Item41> 205 198 <Item42> 206 <Filename Value=" csscolorconst.inc"/>199 <Filename Value="lightingclasses3d.inc"/> 207 200 <Type Value="Binary"/> 208 201 </Item42> 209 202 <Item43> 210 <Filename Value=" filldensity256.inc"/>203 <Filename Value="lineartexscan.inc"/> 211 204 <Type Value="Binary"/> 212 205 </Item43> 213 206 <Item44> 214 <Filename Value=" filldensitysegment256.inc"/>207 <Filename Value="lineartexscan2.inc"/> 215 208 <Type Value="Binary"/> 216 209 </Item44> 217 210 <Item45> 218 <Filename Value=" lightingclasses3d.inc"/>211 <Filename Value="multishapeline.inc"/> 219 212 <Type Value="Binary"/> 220 213 </Item45> 221 214 <Item46> 222 <Filename Value=" lineartexscan.inc"/>215 <Filename Value="perspectivecolorscan.inc"/> 223 216 <Type Value="Binary"/> 224 217 </Item46> 225 218 <Item47> 226 <Filename Value=" lineartexscan2.inc"/>219 <Filename Value="perspectivescan.inc"/> 227 220 <Type Value="Binary"/> 228 221 </Item47> 229 222 <Item48> 230 <Filename Value=" multishapeline.inc"/>223 <Filename Value="perspectivescan2.inc"/> 231 224 <Type Value="Binary"/> 232 225 </Item48> 233 226 <Item49> 234 <Filename Value="p erspectivecolorscan.inc"/>227 <Filename Value="phongdraw.inc"/> 235 228 <Type Value="Binary"/> 236 229 </Item49> 237 230 <Item50> 238 <Filename Value="p erspectivescan.inc"/>231 <Filename Value="phongdrawsse.inc"/> 239 232 <Type Value="Binary"/> 240 233 </Item50> 241 234 <Item51> 242 <Filename Value="p erspectivescan2.inc"/>235 <Filename Value="phonglight.inc"/> 243 236 <Type Value="Binary"/> 244 237 </Item51> 245 238 <Item52> 246 <Filename Value="phong draw.inc"/>239 <Filename Value="phonglightsse.inc"/> 247 240 <Type Value="Binary"/> 248 241 </Item52> 249 242 <Item53> 250 <Filename Value="p hongdrawsse.inc"/>243 <Filename Value="polyaliaspersp.inc"/> 251 244 <Type Value="Binary"/> 252 245 </Item53> 253 246 <Item54> 254 <Filename Value=" phonglight.inc"/>247 <Filename Value="shapes3d.inc"/> 255 248 <Type Value="Binary"/> 256 249 </Item54> 257 250 <Item55> 258 <Filename Value=" phonglightsse.inc"/>259 < Type Value="Binary"/>251 <Filename Value="bgrasse.inc"/> 252 <UnitName Value="bgrasse"/> 260 253 </Item55> 261 254 <Item56> 262 <Filename Value=" polyaliaspersp.inc"/>263 < Type Value="Binary"/>255 <Filename Value="bgragrayscalemask.pas"/> 256 <UnitName Value="BGRAGrayscaleMask"/> 264 257 </Item56> 265 258 <Item57> 266 <Filename Value=" renderdensity256.inc"/>267 < Type Value="Binary"/>259 <Filename Value="bgrareadbmp.pas"/> 260 <UnitName Value="BGRAReadBMP"/> 268 261 </Item57> 269 262 <Item58> 270 <Filename Value=" shapes3d.inc"/>271 < Type Value="Binary"/>263 <Filename Value="bgrareadgif.pas"/> 264 <UnitName Value="BGRAReadGif"/> 272 265 </Item58> 273 266 <Item59> 274 <Filename Value=" winstream.inc"/>275 < Type Value="Binary"/>267 <Filename Value="bgrareadpcx.pas"/> 268 <UnitName Value="BGRAReadPCX"/> 276 269 </Item59> 277 270 <Item60> 278 <Filename Value="bgra sse.inc"/>279 <UnitName Value=" bgrasse"/>271 <Filename Value="bgrareadpng.pas"/> 272 <UnitName Value="BGRAReadPng"/> 280 273 </Item60> 281 274 <Item61> 282 <Filename Value=" sseloadv.inc"/>283 <UnitName Value=" sseloadv"/>275 <Filename Value="bgrareadpsd.pas"/> 276 <UnitName Value="BGRAReadPSD"/> 284 277 </Item61> 285 278 <Item62> 286 <Filename Value=" ssesavev.inc"/>287 <UnitName Value=" ssesavev"/>279 <Filename Value="bgrathumbnail.pas"/> 280 <UnitName Value="BGRAThumbnail"/> 288 281 </Item62> 289 282 <Item63> 290 <Filename Value="bgra grayscalemask.pas"/>291 <UnitName Value="BGRA GrayscaleMask"/>283 <Filename Value="bgrareadtga.pas"/> 284 <UnitName Value="BGRAReadTGA"/> 292 285 </Item63> 293 286 <Item64> 294 <Filename Value="bgraread bmp.pas"/>295 <UnitName Value="BGRARead BMP"/>287 <Filename Value="bgrareadico.pas"/> 288 <UnitName Value="BGRAReadIco"/> 296 289 </Item64> 297 290 <Item65> 298 <Filename Value="bgraread gif.pas"/>299 <UnitName Value="BGRARead Gif"/>291 <Filename Value="bgrareadjpeg.pas"/> 292 <UnitName Value="BGRAReadJpeg"/> 300 293 </Item65> 301 294 <Item66> 302 <Filename Value="bgraread pcx.pas"/>303 <UnitName Value="BGRARead PCX"/>295 <Filename Value="bgrareadlzp.pas"/> 296 <UnitName Value="BGRAReadLzp"/> 304 297 </Item66> 305 298 <Item67> 306 <Filename Value=" bgrareadpng.pas"/>307 <UnitName Value=" BGRAReadPng"/>299 <Filename Value="unzipperext.pas"/> 300 <UnitName Value="UnzipperExt"/> 308 301 </Item67> 309 302 <Item68> 310 <Filename Value="bgra readpsd.pas"/>311 <UnitName Value="BGRA ReadPSD"/>303 <Filename Value="bgralzpcommon.pas"/> 304 <UnitName Value="BGRALzpCommon"/> 312 305 </Item68> 313 306 <Item69> 314 <Filename Value="bgra thumbnail.pas"/>315 <UnitName Value="BGRA Thumbnail"/>307 <Filename Value="bgrawritelzp.pas"/> 308 <UnitName Value="BGRAWriteLzp"/> 316 309 </Item69> 317 310 <Item70> 318 <Filename Value="bgraread tga.pas"/>319 <UnitName Value="BGRARead TGA"/>311 <Filename Value="bgrareadxpm.pas"/> 312 <UnitName Value="BGRAReadXPM"/> 320 313 </Item70> 321 314 <Item71> 322 <Filename Value="bgra readico.pas"/>323 <UnitName Value="BGRA ReadIco"/>315 <Filename Value="bgrasvg.pas"/> 316 <UnitName Value="BGRASVG"/> 324 317 </Item71> 325 318 <Item72> 326 <Filename Value="bgra readjpeg.pas"/>327 <UnitName Value=" bgrareadjpeg"/>319 <Filename Value="bgraunits.pas"/> 320 <UnitName Value="BGRAUnits"/> 328 321 </Item72> 329 322 <Item73> 330 <Filename Value="bgra readlzp.pas"/>331 <UnitName Value="BGRA ReadLzp"/>323 <Filename Value="bgrasvgshapes.pas"/> 324 <UnitName Value="BGRASVGShapes"/> 332 325 </Item73> 333 326 <Item74> 334 <Filename Value=" unzipperext.pas"/>335 <UnitName Value=" UnzipperExt"/>327 <Filename Value="bgrasvgtype.pas"/> 328 <UnitName Value="BGRASVGType"/> 336 329 </Item74> 337 330 <Item75> 338 <Filename Value="bgra lzpcommon.pas"/>339 <UnitName Value="BGRA LzpCommon"/>331 <Filename Value="bgrareadbmpmiomap.pas"/> 332 <UnitName Value="BGRAReadBmpMioMap"/> 340 333 </Item75> 341 334 <Item76> 342 <Filename Value="bgra writelzp.pas"/>343 <UnitName Value="BGRA WriteLzp"/>335 <Filename Value="bgraarrow.pas"/> 336 <UnitName Value="BGRAArrow"/> 344 337 </Item76> 345 338 <Item77> 346 <Filename Value=" bgrareadxpm.pas"/>347 < UnitName Value="BGRAReadXPM"/>339 <Filename Value="vertex3d.inc"/> 340 <Type Value="Binary"/> 348 341 </Item77> 349 342 <Item78> 350 <Filename Value=" bgrasvg.pas"/>351 < UnitName Value="BGRASVG"/>343 <Filename Value="face3d.inc"/> 344 <Type Value="Binary"/> 352 345 </Item78> 353 346 <Item79> 354 <Filename Value=" bgraunits.pas"/>355 < UnitName Value="BGRAUnits"/>347 <Filename Value="part3d.inc"/> 348 <Type Value="Binary"/> 356 349 </Item79> 357 350 <Item80> 358 <Filename Value=" bgrasvgshapes.pas"/>359 < UnitName Value="BGRASVGShapes"/>351 <Filename Value="object3d.inc"/> 352 <Type Value="Binary"/> 360 353 </Item80> 361 354 <Item81> 362 <Filename Value="bgra svgtype.pas"/>363 <UnitName Value="BGRA SVGType"/>355 <Filename Value="bgrapalette.pas"/> 356 <UnitName Value="BGRAPalette"/> 364 357 </Item81> 365 358 <Item82> 366 <Filename Value="bgra readbmpmiomap.pas"/>367 <UnitName Value="BGRA ReadBmpMioMap"/>359 <Filename Value="bgracolorquantization.pas"/> 360 <UnitName Value="BGRAColorQuantization"/> 368 361 </Item82> 369 362 <Item83> 370 <Filename Value="bgra arrow.pas"/>371 <UnitName Value="BGRA Arrow"/>363 <Filename Value="bgradithering.pas"/> 364 <UnitName Value="BGRADithering"/> 372 365 </Item83> 373 366 <Item84> 374 <Filename Value=" vertex3d.inc"/>367 <Filename Value="paletteformats.inc"/> 375 368 <Type Value="Binary"/> 376 369 </Item84> 377 370 <Item85> 378 <Filename Value=" face3d.inc"/>379 < Type Value="Binary"/>371 <Filename Value="bgrautf8.pas"/> 372 <UnitName Value="BGRAUTF8"/> 380 373 </Item85> 381 374 <Item86> 382 <Filename Value=" part3d.inc"/>383 < Type Value="Binary"/>375 <Filename Value="bgralclbitmap.pas"/> 376 <UnitName Value="BGRALCLBitmap"/> 384 377 </Item86> 385 378 <Item87> 386 <Filename Value=" object3d.inc"/>387 < Type Value="Binary"/>379 <Filename Value="bgrawritepng.pas"/> 380 <UnitName Value="BGRAWritePNG"/> 388 381 </Item87> 389 382 <Item88> 390 <Filename Value="bgra palette.pas"/>391 <UnitName Value="BGRA Palette"/>383 <Filename Value="bgragifformat.pas"/> 384 <UnitName Value="BGRAGifFormat"/> 392 385 </Item88> 393 386 <Item89> 394 <Filename Value=" bgracolorquantization.pas"/>395 < UnitName Value="BGRAColorQuantization"/>387 <Filename Value="geometrytypes.inc"/> 388 <Type Value="Binary"/> 396 389 </Item89> 397 390 <Item90> 398 <Filename Value="bgra dithering.pas"/>399 < UnitName Value="BGRADithering"/>391 <Filename Value="bgracustombitmap.inc"/> 392 <Type Value="Binary"/> 400 393 </Item90> 401 394 <Item91> 402 <Filename Value=" paletteformats.inc"/>403 < Type Value="Binary"/>395 <Filename Value="bgragraphics.pas"/> 396 <UnitName Value="BGRAGraphics"/> 404 397 </Item91> 398 <Item92> 399 <Filename Value="bgrascenetypes.pas"/> 400 <UnitName Value="BGRASceneTypes"/> 401 </Item92> 402 <Item93> 403 <Filename Value="bgrarenderer3d.pas"/> 404 <UnitName Value="BGRARenderer3D"/> 405 </Item93> 406 <Item94> 407 <Filename Value="bgrawritebmpmiomap.pas"/> 408 <UnitName Value="BGRAWriteBmpMioMap"/> 409 </Item94> 410 <Item95> 411 <Filename Value="bgraopengltype.pas"/> 412 <UnitName Value="BGRAOpenGLType"/> 413 </Item95> 414 <Item96> 415 <Filename Value="bgraspritegl.pas"/> 416 <UnitName Value="BGRASpriteGL"/> 417 </Item96> 418 <Item97> 419 <Filename Value="bgraopengl.pas"/> 420 <UnitName Value="BGRAOpenGL"/> 421 </Item97> 422 <Item98> 423 <Filename Value="bgracanvasgl.pas"/> 424 <UnitName Value="BGRACanvasGL"/> 425 </Item98> 426 <Item99> 427 <Filename Value="bgrafontgl.pas"/> 428 <UnitName Value="BGRAFontGL"/> 429 </Item99> 430 <Item100> 431 <Filename Value="bgraopengl3d.pas"/> 432 <UnitName Value="BGRAOpenGL3D"/> 433 </Item100> 434 <Item101> 435 <Filename Value="blurbox.inc"/> 436 <Type Value="Text"/> 437 </Item101> 438 <Item102> 439 <Filename Value="bgraphoxo.pas"/> 440 <UnitName Value="BGRAPhoxo"/> 441 </Item102> 442 <Item103> 443 <Filename Value="bgrafilterscanner.pas"/> 444 <UnitName Value="BGRAFilterScanner"/> 445 </Item103> 446 <Item104> 447 <Filename Value="bgrafiltertype.pas"/> 448 <UnitName Value="BGRAFilterType"/> 449 </Item104> 450 <Item105> 451 <Filename Value="bgrafilterblur.pas"/> 452 <UnitName Value="BGRAFilterBlur"/> 453 </Item105> 454 <Item106> 455 <Filename Value="bgramultifiletype.pas"/> 456 <UnitName Value="bgramultifiletype"/> 457 </Item106> 458 <Item107> 459 <Filename Value="bgrawinresource.pas"/> 460 <UnitName Value="BGRAWinResource"/> 461 </Item107> 462 <Item108> 463 <Filename Value="bgralazresource.pas"/> 464 <UnitName Value="BGRALazResource"/> 465 </Item108> 405 466 </Files> 406 467 <RequiredPkgs Count="2"> -
GraphicTest/Packages/bgrabitmap/bgrabitmappack.pas
r472 r494 17 17 BGRATransform, BGRATypewriter, BGRAVectorize, BGRAGrayscaleMask, 18 18 BGRAReadBMP, BGRAReadGif, BGRAReadPCX, BGRAReadPng, BGRAReadPSD, 19 BGRAThumbnail, BGRAReadTGA, BGRAReadIco, bgrareadjpeg, BGRAReadLzp,19 BGRAThumbnail, BGRAReadTGA, BGRAReadIco, BGRAReadJpeg, BGRAReadLzp, 20 20 UnzipperExt, BGRALzpCommon, BGRAWriteLzp, BGRAReadXPM, BGRASVG, BGRAUnits, 21 21 BGRASVGShapes, BGRASVGType, BGRAReadBmpMioMap, BGRAArrow, BGRAPalette, 22 BGRAColorQuantization, BGRADithering; 22 BGRAColorQuantization, BGRADithering, BGRAUTF8, BGRALCLBitmap, BGRAWritePNG, 23 BGRAGifFormat, BGRAGraphics, BGRASceneTypes, BGRARenderer3D, 24 BGRAWriteBmpMioMap, BGRAOpenGLType, BGRASpriteGL, BGRAOpenGL, BGRACanvasGL, 25 BGRAFontGL, BGRAOpenGL3D, BGRAPhoxo, BGRAFilterScanner, BGRAFilterType, 26 BGRAFilterBlur, BGRAMultiFileType, BGRAWinResource, BGRALazResource; 23 27 24 28 implementation -
GraphicTest/Packages/bgrabitmap/bgrabitmaptypes.pas
r472 r494 7 7 8 8 --> Include BGRABitmap and BGRABitmapTypes in the 'uses' clause. 9 If you are using LCL types, add also BGRAGraphics unit. 9 10 10 11 **************************************************************************** … … 26 27 27 28 {$mode objfpc}{$H+} 29 {$i bgrabitmap.inc} 28 30 29 31 interface 30 32 31 33 uses 32 Classes, Types, Graphics, FPImage, FPImgCanv, GraphType; 34 Classes, Types, BGRAGraphics, 35 FPImage, FPImgCanv{$IFDEF BGRABITMAP_USE_LCL}, GraphType{$ENDIF}, 36 BGRAMultiFileType; 33 37 34 38 type 35 //pointer for direct pixel access 36 PBGRAPixel = ^TBGRAPixel; 37 39 TMultiFileContainer = BGRAMultiFileType.TMultiFileContainer; 38 40 Int32or64 = {$IFDEF CPU64}Int64{$ELSE}LongInt{$ENDIF}; 39 41 UInt32or64 = {$IFDEF CPU64}UInt64{$ELSE}LongWord{$ENDIF}; 40 42 41 //Each pixel is a sequence of 4 bytes containing blue, green, red and alpha channel. 42 TBGRAPixel = packed record 43 blue, green, red, alpha: byte; 44 end; 45 46 ArrayOfTBGRAPixel = array of TBGRAPixel; 47 48 //gamma expanded values 49 TExpandedPixel = packed record 50 red, green, blue, alpha: word; 51 end; 52 53 //pixel color defined in HSL colorspace 54 THSLAPixel = packed record 55 hue, saturation, lightness, alpha: word; 56 end; 57 TGSBAPixel = THSLAPixel; 58 59 //general purpose color variable with floating point values 60 TColorF = packed array[1..4] of single; 61 62 { These types are used as parameters } 63 64 TDrawMode = (dmSet, //replace pixels 65 dmSetExceptTransparent, //draw pixels with alpha=255 66 dmLinearBlend, //blend without gamma correction 67 dmDrawWithTransparency, //normal blending with gamma correction 68 dmXor); //bitwise xor for all channels 69 TChannel = (cRed, cGreen, cBlue, cAlpha); 70 TChannels = set of TChannel; 71 72 //floodfill option 73 TFloodfillMode = (fmSet, //set pixels 74 fmDrawWithTransparency, //draw fill color with transparency 75 fmProgressive); //draw fill color with transparency according to similarity with start color 76 77 TResampleMode = (rmSimpleStretch, //low quality resample 78 rmFineResample); //use resample filters and pixel-centered coordinates 79 TResampleFilter = (rfBox, //equivalent of stretch with high quality 80 rfLinear, //linear interpolation 81 rfHalfCosine, //mix of rfLinear and rfCosine 82 rfCosine, //cosine-like interpolation 83 rfBicubic, //simple bi-cubic filter (blur) 84 rfMitchell, //downsizing interpolation 85 rfSpline, //upsizing interpolation 86 rfLanczos2, //Lanczos with radius 2 87 rfLanczos3, //Lanczos with radius 3 88 rfLanczos4, //Lanczos with radius 4 89 rfBestQuality); //mix of rfMitchell and rfSpline 90 91 TDitheringAlgorithm = (daNearestNeighbor, daFloydSteinberg); 92 TAlphaChannelPaletteOption = (acIgnore, acTransparentEntry, acFullChannelInPalette); 43 {=== Miscellaneous types ===} 44 45 type 46 {* Options when doing a floodfill (also called bucket fill) } 47 TFloodfillMode = ( 48 {** Pixels that are filled are replaced } 49 fmSet, 50 {** Pixels that are filled are drawn upon with the fill color } 51 fmDrawWithTransparency, 52 {** Pixels that are filled are drawn upon to the extent that the color underneath is similar to 53 the start color. The more different the different is, the less it is drawn upon } 54 fmProgressive); 55 56 {* Specifies how much smoothing is applied to the computation of the median } 57 TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth); 58 {* Specifies the shape of a predefined blur } 59 TRadialBlurType = ( 60 {** Gaussian-like, pixel importance decreases progressively } 61 rbNormal, 62 {** Disk blur, pixel importance does not decrease progressively } 63 rbDisk, 64 {** Pixel are considered when they are at a certain distance } 65 rbCorona, 66 {** Gaussian-like, but 10 times smaller than ''rbNormal'' } 67 rbPrecise, 68 {** Gaussian-like but simplified to be computed faster } 69 rbFast, 70 {** Box blur, pixel importance does not decrease progressively 71 and the pixels are included when they are in a square. 72 This is much faster than ''rbFast'' however you may get 73 square shapes in the resulting image } 74 rbBox); 75 76 TEmbossOption = (eoTransparent, eoPreserveHue); 77 TEmbossOptions = set of TEmbossOption; 78 79 TTextLayout = BGRAGraphics.TTextLayout; 93 80 94 81 const 82 tlTop = BGRAGraphics.tlTop; 83 tlCenter = BGRAGraphics.tlCenter; 84 tlBottom = BGRAGraphics.tlBottom; 85 86 // checks the bounds of an image in the given clipping rectangle 87 function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean; 88 89 {==== Imported from GraphType ====} 90 //if this unit is defined, otherwise 91 //define here the types used by the library. 92 {$IFDEF BGRABITMAP_USE_LCL} 93 type 94 { Order of the lines in an image } 95 TRawImageLineOrder = GraphType.TRawImageLineOrder; 96 { Order of the bits in a byte containing pixel values } 97 TRawImageBitOrder = GraphType.TRawImageBitOrder; 98 { Order of the bytes in a group of byte containing pixel values } 99 TRawImageByteOrder = GraphType.TRawImageByteOrder; 100 { Definition of a single line 3D bevel } 101 TGraphicsBevelCut = GraphType.TGraphicsBevelCut; 102 103 const 104 riloTopToBottom = GraphType.riloTopToBottom; // The first line (line 0) is the top line 105 riloBottomToTop = GraphType.riloBottomToTop; // The first line (line 0) is the bottom line 106 107 riboBitsInOrder = GraphType.riboBitsInOrder; // Bit 0 is pixel 0 108 riboReversedBits = GraphType.riboReversedBits; // Bit 0 is pixel 7 (Bit 1 is pixel 6, ...) 109 110 riboLSBFirst = GraphType.riboLSBFirst; // least significant byte first (little endian) 111 riboMSBFirst = GraphType.riboMSBFirst; // most significant byte first (big endian) 112 113 fsSurface = GraphType.fsSurface; //type is defined as Graphics.TFillStyle 114 fsBorder = GraphType.fsBorder; 115 116 bvNone = GraphType.bvNone; 117 bvLowered = GraphType.bvLowered; 118 bvRaised = GraphType.bvRaised; 119 bvSpace = GraphType.bvSpace; 120 {$ELSE} 121 type 122 {* Order of the lines in an image } 123 TRawImageLineOrder = ( 124 {** The first line in memory (line 0) is the top line } 125 riloTopToBottom, 126 {** The first line in memory (line 0) is the bottom line } 127 riloBottomToTop); 128 129 {* Order of the bits in a byte containing pixel values } 130 TRawImageBitOrder = ( 131 {** The lowest bit is on the left. So with a monochrome picture, bit 0 would be pixel 0 } 132 riboBitsInOrder, 133 {** The lowest bit is on the right. So with a momochrome picture, bit 0 would be pixel 7 (bit 1 would be pixel 6, ...) } 134 riboReversedBits); 135 136 {* Order of the bytes in a group of byte containing pixel values } 137 TRawImageByteOrder = ( 138 {** Least significant byte first (little endian) } 139 riboLSBFirst, 140 {** most significant byte first (big endian) } 141 riboMSBFirst); 142 143 {* Definition of a single line 3D bevel } 144 TGraphicsBevelCut = 145 ( 146 {** No bevel } 147 bvNone, 148 {** Shape is lowered, light is on the bottom-right corner } 149 bvLowered, 150 {** Shape is raised, light is on the top-left corner } 151 bvRaised, 152 {** Shape is at the same level, there is no particular lighting } 153 bvSpace); 154 {$ENDIF} 155 156 {$DEFINE INCLUDE_INTERFACE} 157 {$I bgrapixel.inc} 158 159 {$DEFINE INCLUDE_INTERFACE} 160 {$I geometrytypes.inc} 161 162 {$DEFINE INCLUDE_INTERFACE} 163 {$i csscolorconst.inc} 164 165 {$DEFINE INCLUDE_SCANNER_INTERFACE } 166 {$I bgracustombitmap.inc} 167 168 {==== Integer math ====} 169 170 {* Computes the value modulo cycle, and if the ''value'' is negative, the result 171 is still positive } 172 function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload; 173 174 { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values. 175 They use a table to store already computed values. The return value is an integer 176 ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is 177 32768 instead of 1. The input has a period of 65536, so you can supply any integer 178 without applying a modulo. } 179 180 { Compute all values now } 181 procedure PrecalcSin65536; 182 183 {* Returns an integer approximation of the sine. Value ranges from 0 to 65535, 184 where 65536 corresponds to the next cycle } 185 function Sin65536(value: word): Int32or64; inline; 186 {* Returns an integer approximation of the cosine. Value ranges from 0 to 65535, 187 where 65536 corresponds to the next cycle } 188 function Cos65536(value: word): Int32or64; inline; 189 190 {* Returns the square root of the given byte, considering that 191 255 is equal to unity } 192 function ByteSqrt(value: byte): byte; inline; 193 194 {==== Types provided for fonts ====} 195 type 196 {* Quality to be used to render text } 197 TBGRAFontQuality = ( 198 {** Use the system capabilities. It is rather fast however it may be 199 not be smoothed. } 200 fqSystem, 201 {** Use the system capabilities to render with ClearType. This quality is 202 of course better than fqSystem however it may not be perfect.} 203 fqSystemClearType, 204 {** Garanties a high quality antialiasing. } 205 fqFineAntialiasing, 206 {** Fine antialiasing with ClearType in assuming an LCD display in red/green/blue order } 207 fqFineClearTypeRGB, 208 {** Fine antialiasing with ClearType in assuming an LCD display in blue/green/red order } 209 fqFineClearTypeBGR); 210 211 {* Measurements of a font } 212 TFontPixelMetric = record 213 {** The values have been computed } 214 Defined: boolean; 215 {** Position of the baseline, where most letters lie } 216 Baseline, 217 {** Position of the top of the small letters (x being one of them) } 218 xLine, 219 {** Position of the top of the UPPERCASE letters } 220 CapLine, 221 {** Position of the bottom of letters like g and p } 222 DescentLine, 223 {** Total line height including line spacing defined by the font } 224 Lineheight: integer; 225 end; 226 227 {* Vertical anchoring of the font. When text is drawn, a start coordinate 228 is necessary. Text can be positioned in different ways. This enum 229 defines what position it is regarding the font } 230 TFontVerticalAnchor = ( 231 {** The top of the font. Everything will be drawn below the start coordinate. } 232 fvaTop, 233 {** The center of the font } 234 fvaCenter, 235 {** The top of capital letters } 236 fvaCapLine, 237 {** The center of capital letters } 238 fvaCapCenter, 239 {** The top of small letters } 240 fvaXLine, 241 {** The center of small letters } 242 fvaXCenter, 243 {** The baseline, the bottom of most letters } 244 fvaBaseline, 245 {** The bottom of letters that go below the baseline } 246 fvaDescentLine, 247 {** The bottom of the font. Everything will be drawn above the start coordinate } 248 fvaBottom); 249 250 {* Definition of a function that handles work-break } 251 TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object; 252 253 {* Alignment for a typewriter, that does not have any more information 254 than a square shape containing glyphs } 255 TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight, twaLeft, twaMiddle, twaRight, twaBottomLeft, twaBottom, twaBottomRight); 256 {* How a typewriter must render its content on a Canvas2d } 257 TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill); 258 259 { TBGRACustomFontRenderer } 260 {* Abstract class for all font renderers } 261 TBGRACustomFontRenderer = class 262 {** Specifies the font to use. Unless the font renderer accept otherwise, 263 the name is in human readable form, like 'Arial', 'Times New Roman', ... } 264 FontName: string; 265 266 {** Specifies the set of styles to be applied to the font. 267 These can be fsBold, fsItalic, fsStrikeOut, fsUnderline. 268 So the value [fsBold,fsItalic] means that the font must be bold and italic } 269 FontStyle: TFontStyles; 270 271 {** Specifies the quality of rendering. Default value is fqSystem } 272 FontQuality : TBGRAFontQuality; 273 274 {** Specifies the rotation of the text, for functions that support text rotation. 275 It is expressed in tenth of degrees, positive values going counter-clockwise } 276 FontOrientation: integer; 277 278 {** Specifies the height of the font without taking into account additional line spacing. 279 A negative value means that it is the full height instead } 280 FontEmHeight: integer; 281 282 {** Returns measurement for the current font in pixels } 283 function GetFontPixelMetric: TFontPixelMetric; virtual; abstract; 284 285 {** Returns the total size of the string provided using the current font. 286 Orientation is not taken into account, so that the width is along the text } 287 function TextSize(sUTF8: string): TSize; virtual; abstract; 288 289 {** Draws the UTF8 encoded string, with color ''c''. 290 If align is taLeftJustify, (''x'',''y'') is the top-left corner. 291 If align is taCenter, (''x'',''y'') is at the top and middle of the text. 292 If align is taRightJustify, (''x'',''y'') is the top-right corner. 293 The value of ''FontOrientation'' is taken into account, so that the text may be rotated } 294 procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; 295 296 {** Same as above functions, except that the text is filled using texture. 297 The value of ''FontOrientation'' is taken into account, so that the text may be rotated } 298 procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; 299 300 {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' } 301 procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; 302 {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' } 303 procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; 304 305 {** Draw the UTF8 encoded string at the coordinate (''x'',''y''), clipped inside the rectangle ''ARect''. 306 Additional style information is provided by the style parameter. 307 The color ''c'' is used to fill the text. No rotation is applied. } 308 procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; 309 310 {** Same as above except a ''texture'' is used to fill the text } 311 procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; 312 313 {** Copy the path for the UTF8 encoded string into ''ADest''. 314 If ''align'' is ''taLeftJustify'', (''x'',''y'') is the top-left corner. 315 If ''align'' is ''taCenter'', (''x'',''y'') is at the top and middle of the text. 316 If ''align'' is ''taRightJustify'', (''x'',''y'') is the top-right corner. } 317 procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional 318 end; 319 320 {* Output mode for the improved renderer for readability. This is used by the font renderer based on LCL in ''BGRAText'' } 321 TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR); 322 323 {** Removes line ending and tab characters from a string (for a function 324 like ''TextOut'' that does not handle this). this works with UTF8 strings 325 as well } 326 function CleanTextOutString(s: string): string; 327 {** Remove the line ending at the specified position or return False. 328 This works with UTF8 strings however the index is the byte index } 329 function RemoveLineEnding(var s: string; indexByte: integer): boolean; 330 {** Remove the line ending at the specified position or return False. 331 The index is the character index, that may be different from the 332 byte index } 333 function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean; 334 {** Default word break handler, that simply divide when there is a space } 335 procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string); 336 337 {==== Images and resampling ====} 338 339 type 340 {* How the resample is to be computed } 341 TResampleMode = ( 342 {** Low quality resample by repeating pixels, stretching them } 343 rmSimpleStretch, 344 {** Use resample filters. This gives high 345 quality resampling however this the proportion changes slightly because 346 the first and last pixel are considered to occupy only half a unit as 347 they are considered as the border of the picture 348 (pixel-centered coordinates) } 349 rmFineResample); 350 351 {* List of resample filter to be used with ''rmFineResample'' } 352 TResampleFilter = ( 353 {** Equivalent of simple stretch with high quality and pixel-centered coordinates } 354 rfBox, 355 {** Linear interpolation giving slow transition between pixels } 356 rfLinear, 357 {** Mix of ''rfLinear'' and ''rfCosine'' giving medium speed stransition between pixels } 358 rfHalfCosine, 359 {** Cosine-like interpolation giving fast transition between pixels } 360 rfCosine, 361 {** Simple bi-cubic filter (blurry) } 362 rfBicubic, 363 {** Mitchell filter, good for downsizing interpolation } 364 rfMitchell, 365 {** Spline filter, good for upsizing interpolation, however slightly blurry } 366 rfSpline, 367 {** Lanczos with radius 2, blur is corrected } 368 rfLanczos2, 369 {** Lanczos with radius 3, high contrast } 370 rfLanczos3, 371 {** Lanczos with radius 4, high contrast } 372 rfLanczos4, 373 {** Best quality using rfMitchell or rfSpline } 374 rfBestQuality); 375 376 const 377 {** List of strings to represent resample filters } 95 378 ResampleFilterStr : array[TResampleFilter] of string = 96 379 ('Box','Linear','HalfCosine','Cosine','Bicubic','Mitchell','Spline', 97 380 'Lanczos2','Lanczos3','Lanczos4','BestQuality'); 98 381 99 function StrToResampleFilter(str: string): TResampleFilter; 382 {** Gives the sample filter represented by a string } 383 function StrToResampleFilter(str: string): TResampleFilter; 100 384 101 385 type 102 TBGRAImageFormat = (ifUnknown, ifJpeg, ifPng, ifGif, ifBmp, ifIco, ifPcx, ifPaintDotNet, ifLazPaint, ifOpenRaster, 103 ifPsd, ifTarga, ifTiff, ifXwd, ifXPixMap, ifBmpMioMap); 386 {* List of image formats } 387 TBGRAImageFormat = ( 388 {** Unknown format } 389 ifUnknown, 390 {** JPEG format, opaque, lossy compression } 391 ifJpeg, 392 {** PNG format, transparency, lossless compression } 393 ifPng, 394 {** GIF format, single transparent color, lossless in theory but only low number of colors allowed } 395 ifGif, 396 {** BMP format, transparency, no compression. Note that transparency is 397 not supported by all BMP readers so it is not recommended to avoid 398 storing images with transparency in this format } 399 ifBmp, 400 {** ICO format, contains different sizes of the same image } 401 ifIco, 402 {** PCX format, opaque, rudimentary lossless compression } 403 ifPcx, 404 {** Paint.NET format, layers, lossless compression } 405 ifPaintDotNet, 406 {** LazPaint format, layers, lossless compression } 407 ifLazPaint, 408 {** OpenRaster format, layers, lossless compression } 409 ifOpenRaster, 410 {** Phoxo format, layers } 411 ifPhoxo, 412 {** Photoshop format, layers, rudimentary lossless compression } 413 ifPsd, 414 {** Targa format (TGA), transparency, rudimentary lossless compression } 415 ifTarga, 416 {** TIFF format, limited support } 417 ifTiff, 418 {** X-Window capture, limited support } 419 ifXwd, 420 {** X-Pixmap, text encoded image, limited support } 421 ifXPixMap, 422 {** iGO BMP, limited support } 423 ifBmpMioMap); 424 425 {* Options when loading an image } 426 TBGRALoadingOption = ( 427 {** Do not clear RGB channels when alpha is zero (not recommended) } 428 loKeepTransparentRGB, 429 {** Consider BMP to be opaque if no alpha value is provided (for compatibility) } 430 loBmpAutoOpaque, 431 {** Load JPEG quickly however with a lower quality } 432 loJpegQuick); 433 TBGRALoadingOptions = set of TBGRALoadingOption; 104 434 105 435 var 436 {** List of stream readers for images } 106 437 DefaultBGRAImageReader: array[TBGRAImageFormat] of TFPCustomImageReaderClass; 438 {** List of stream writers for images } 107 439 DefaultBGRAImageWriter: array[TBGRAImageFormat] of TFPCustomImageWriterClass; 108 440 109 type 110 TBGRAFontQuality = (fqSystem, fqSystemClearType, fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR); 111 // fqSystem: use system rendering. It is fast however it may be not be smoothed. 112 // fqSystemClearType: use system rendering with ClearType. This quality is of course better than fqSystem however it may not be much smoother. 113 // fqFineAntialiasing: garanties a high quality antialiasing. This is slower. 114 // fqFineClearTypeRGB: garanties a high quality antialiasing with ClearType. The order of the color in the LCD screen is supposed to be un red/green/blue order. 115 // fqFineClearTypeBGR: same as above, except the color of the LCD screen is supposed to be in blue/green/red order. 116 117 TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth); 118 TRadialBlurType = (rbNormal, rbDisk, rbCorona, rbPrecise, rbFast, rbBox); 119 TSplineStyle = (ssInside, ssInsideWithEnds, ssCrossing, ssCrossingWithEnds, 120 ssOutside, ssRoundOutside, ssVertexToSide); 121 122 { Advanced blending modes 123 see : http://www.brighthub.com/multimedia/photography/articles/18301.aspx 124 and : http://www.pegtop.net/delphi/articles/blendmodes/ } 125 TBlendOperation = (boLinearBlend, boTransparent, //blending 126 boLighten, boScreen, boAdditive, boLinearAdd, boColorDodge, boDivide, boNiceGlow, boSoftLight, boHardLight, //lighting 127 boGlow, boReflect, boOverlay, boDarkOverlay, boDarken, boMultiply, boColorBurn, //masking 128 boDifference, boLinearDifference, boExclusion, boLinearExclusion, boSubtract, boLinearSubtract, boSubtractInverse, boLinearSubtractInverse, 129 boNegation, boLinearNegation, boXor); //negative 130 131 const 132 boGlowMask = boGlow; 133 boLinearMultiply = boMultiply; 134 boNonLinearOverlay = boDarkOverlay; 135 EmptyRect : TRect = (left:0; top:0; right:0; bottom: 0); 136 137 const 138 BlendOperationStr : array[TBlendOperation] of string = 139 ('LinearBlend', 'Transparent', 140 'Lighten', 'Screen', 'Additive', 'LinearAdd', 'ColorDodge', 'Divide', 'NiceGlow', 'SoftLight', 'HardLight', 141 'Glow', 'Reflect', 'Overlay', 'DarkOverlay', 'Darken', 'Multiply', 'ColorBurn', 142 'Difference', 'LinearDifference', 'Exclusion', 'LinearExclusion', 'Subtract', 'LinearSubtract', 'SubtractInverse', 'LinearSubtractInverse', 143 'Negation', 'LinearNegation', 'Xor'); 144 145 function StrToBlendOperation(str: string): TBlendOperation; 146 147 type 148 TGradientType = (gtLinear, gtReflected, gtDiamond, gtRadial); 149 const 150 GradientTypeStr : array[TGradientType] of string = 151 ('Linear','Reflected','Diamond','Radial'); 152 function StrToGradientType(str: string): TGradientType; 153 154 type 155 { A pen style is defined as a list of floating number. The first number is the length of the first dash, 156 the second number is the length of the first gap, the third number is the length of the second dash... 157 It must have an even number of values. } 158 TBGRAPenStyle = Array Of Single; 159 TRoundRectangleOption = (rrTopLeftSquare,rrTopRightSquare,rrBottomRightSquare,rrBottomLeftSquare, 160 rrTopLeftBevel,rrTopRightBevel,rrBottomRightBevel,rrBottomLeftBevel,rrDefault); 161 TRoundRectangleOptions = set of TRoundRectangleOption; 162 TPolygonOrder = (poNone, poFirstOnTop, poLastOnTop); //see TBGRAMultiShapeFiller in BGRAPolygon 163 164 function BGRAPenStyle(dash1, space1: single; dash2: single=0; space2: single = 0; dash3: single=0; space3: single = 0; dash4 : single = 0; space4 : single = 0): TBGRAPenStyle; 165 166 { Point, polygon and curve structures } 167 type 168 PPointF = ^TPointF; 169 TPointF = packed record 170 x, y: single; 171 end; 172 ArrayOfTPointF = array of TPointF; 173 TArcOption = (aoClosePath, aoPie, aoFillPath); 174 TArcOptions = set of TArcOption; 175 176 TCubicBezierCurve = record 177 p1,c1,c2,p2: TPointF; 178 end; 179 TQuadraticBezierCurve = record 180 p1,c,p2: TPointF; 181 end; 182 183 TArcDef = record 184 center: TPointF; 185 radius: TPointF; 186 xAngleRadCW, startAngleRadCW, endAngleRadCW: single; //see convention in BGRAPath 187 anticlockwise: boolean 188 end; 189 PArcDef = ^TArcDef; 190 191 TPoint3D = record 192 x,y,z: single; 193 end; 194 195 TBGRAArrowStyle = (asNone, asNormal, asCut, asTriangle, asHollowTriangle, asFlipped, asFlippedCut, asTail, asTailRepeat); 196 197 TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight, 198 twaLeft, twaMiddle, twaRight, 199 twaBottomLeft, twaBottom, twaBottomRight); 200 TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill); 201 202 function ConcatPointsF(const APolylines: array of ArrayOfTPointF): ArrayOfTPointF; 203 204 function Point3D(x,y,z: single): TPoint3D; 205 operator = (const v1,v2: TPoint3D): boolean; inline; 206 operator * (const v1,v2: TPoint3D): single; inline; 207 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline; 208 operator - (const v1,v2: TPoint3D): TPoint3D; inline; 209 operator - (const v: TPoint3D): TPoint3D; inline; 210 operator + (const v1,v2: TPoint3D): TPoint3D; inline; 211 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D); 212 procedure Normalize3D(var v: TPoint3D); inline; 213 214 function BezierCurve(origin, control1, control2, destination: TPointF) : TCubicBezierCurve; overload; 215 function BezierCurve(origin, control, destination: TPointF) : TQuadraticBezierCurve; overload; 216 function BezierCurve(origin, destination: TPointF) : TQuadraticBezierCurve; overload; 217 function ArcDef(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean) : TArcDef; 218 219 { Useful constants } 220 const 221 dmFastBlend = dmLinearBlend; 222 EmptySingle: single = -3.402823e38; //used as a separator in floating point lists 223 EmptyPointF: TPointF = (x: -3.402823e38; y: -3.402823e38); //used as a separator in TPointF lists 224 BGRAPixelTransparent: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 0); 225 BGRAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255); 226 BGRABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255); 227 228 { This color is needed for drawing black shapes on the standard TCanvas, because 229 when drawing with pure black, there is no way to know if something has been 230 drawn or if it is transparent } 231 clBlackOpaque = TColor($010000); 232 233 {$DEFINE INCLUDE_COLOR_CONST} 234 {$i csscolorconst.inc} 235 236 type 237 TBGRAColorDefinition = record 238 Name: string; 239 Color: TBGRAPixel; 240 end; 241 242 { TBGRAColorList } 243 244 TBGRAColorList = class 245 protected 246 FFinished: boolean; 247 FNbColors: integer; 248 FColors: array of TBGRAColorDefinition; 249 function GetByIndex(Index: integer): TBGRAPixel; 250 function GetByName(Name: string): TBGRAPixel; 251 function GetName(Index: integer): string; 252 public 253 constructor Create; 254 procedure Add(Name: string; const Color: TBGRAPixel); 255 procedure Finished; 256 function IndexOf(Name: string): integer; 257 function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer; 258 259 property ByName[Name: string]: TBGRAPixel read GetByName; 260 property ByIndex[Index: integer]: TBGRAPixel read GetByIndex; default; 261 property Name[Index: integer]: string read GetName; 262 property Count: integer read FNbColors; 263 end; 264 265 var 266 VGAColors, CSSColors: TBGRAColorList; 267 268 function isEmptyPointF(pt: TPointF): boolean; 269 270 type 271 TFontPixelMetric = record 272 Defined: boolean; 273 Baseline, xLine, CapLine, DescentLine, Lineheight: integer; 274 end; 275 276 { A scanner is like an image, but its content has no limit and can be calculated on the fly. 277 It must not implement reference counting. } 278 IBGRAScanner = interface 279 procedure ScanMoveTo(X,Y: Integer); 280 function ScanNextPixel: TBGRAPixel; 281 function ScanAt(X,Y: Single): TBGRAPixel; 282 function ScanAtInteger(X,Y: integer): TBGRAPixel; 283 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); 284 function IsScanPutPixelsDefined: boolean; 285 end; 286 287 { A path is the ability to define a contour with moveTo, lineTo... 288 It must not implement reference counting. } 289 IBGRAPath = interface 290 procedure closePath; 291 procedure moveTo(const pt: TPointF); 292 procedure lineTo(const pt: TPointF); 293 procedure polylineTo(const pts: array of TPointF); 294 procedure quadraticCurveTo(const cp,pt: TPointF); 295 procedure bezierCurveTo(const cp1,cp2,pt: TPointF); 296 procedure arc(const arcDef: TArcDef); 297 procedure copyTo(dest: IBGRAPath); 298 end; 299 300 TScanAtFunction = function (X,Y: Single): TBGRAPixel of object; 301 TScanAtIntegerFunction = function (X,Y: Integer): TBGRAPixel of object; 302 TScanNextPixelFunction = function: TBGRAPixel of object; 303 TBGRACustomGradient = class; 304 305 TBGRACustomFillInfo = class; 306 TBGRACustomFontRenderer = class; 307 308 { TBGRACustomBitmap } 309 310 TBGRACustomBitmap = class(TFPCustomImage,IBGRAScanner) // a bitmap can be used as a scanner 311 private 312 function GetFontAntialias: Boolean; 313 procedure SetFontAntialias(const AValue: Boolean); 314 protected 315 { accessors to properies } 316 function GetArrowEndRepeat: integer; virtual; abstract; 317 function GetArrowStartRepeat: integer; virtual; abstract; 318 procedure SetArrowEndRepeat(AValue: integer); virtual; abstract; 319 procedure SetArrowStartRepeat(AValue: integer); virtual; abstract; 320 function GetArrowEndOffset: single; virtual; abstract; 321 function GetArrowStartOffset: single; virtual; abstract; 322 procedure SetArrowEndOffset(AValue: single); virtual; abstract; 323 procedure SetArrowStartOffset(AValue: single); virtual; abstract; 324 function GetArrowEndSize: TPointF; virtual; abstract; 325 function GetArrowStartSize: TPointF; virtual; abstract; 326 procedure SetArrowEndSize(AValue: TPointF); virtual; abstract; 327 procedure SetArrowStartSize(AValue: TPointF); virtual; abstract; 328 function GetLineCap: TPenEndCap; virtual; abstract; 329 procedure SetLineCap(AValue: TPenEndCap); virtual; abstract; 330 function GetFontRenderer: TBGRACustomFontRenderer; virtual; abstract; 331 procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); virtual; abstract; 332 function GetHeight: integer; virtual; abstract; 333 function GetWidth: integer; virtual; abstract; 334 function GetDataPtr: PBGRAPixel; virtual; abstract; 335 function GetNbPixels: integer; virtual; abstract; 336 function CheckEmpty: boolean; virtual; abstract; 337 function GetHasTransparentPixels: boolean; virtual; abstract; 338 function GetAverageColor: TColor; virtual; abstract; 339 function GetAveragePixel: TBGRAPixel; virtual; abstract; 340 procedure SetCanvasOpacity(AValue: byte); virtual; abstract; 341 function GetScanLine(y: integer): PBGRAPixel; virtual; abstract; 342 function GetRefCount: integer; virtual; abstract; 343 function GetBitmap: TBitmap; virtual; abstract; 344 function GetLineOrder: TRawImageLineOrder; virtual; abstract; 345 function GetCanvasFP: TFPImageCanvas; virtual; abstract; 346 function GetCanvasDrawModeFP: TDrawMode; virtual; abstract; 347 procedure SetCanvasDrawModeFP(const AValue: TDrawMode); virtual; abstract; 348 function GetCanvas: TCanvas; virtual; abstract; 349 function GetCanvasOpacity: byte; virtual; abstract; 350 function GetCanvasAlphaCorrection: boolean; virtual; abstract; 351 procedure SetCanvasAlphaCorrection(const AValue: boolean); virtual; abstract; 352 function GetFontHeight: integer; virtual; abstract; 353 procedure SetFontHeight(AHeight: integer); virtual; abstract; 354 function GetFontFullHeight: integer; virtual; abstract; 355 procedure SetFontFullHeight(AHeight: integer); virtual; abstract; 356 function GetPenStyle: TPenStyle; virtual; abstract; 357 procedure SetPenStyle(const AValue: TPenStyle); virtual; abstract; 358 function GetCustomPenStyle: TBGRAPenStyle; virtual; abstract; 359 procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); virtual; abstract; 360 function GetClipRect: TRect; virtual; abstract; 361 procedure SetClipRect(const AValue: TRect); virtual; abstract; 362 function GetFontPixelMetric: TFontPixelMetric; virtual; abstract; 363 procedure ClearTransparentPixels; virtual; abstract; 364 procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract; 365 procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract; 366 367 public 368 Caption: string; //user defined caption 369 370 {-------------------font style------------------------} 371 FontName: string; //Specifies the font to use. Unless the font renderer accept otherwise, 372 //the name is in human readable form, like 'Arial', 'Times New Roman', ... 373 374 FontStyle: TFontStyles; //Specifies the set of styles to be applied to the font. 375 //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline. 376 //So the value [fsBold,fsItalic] means that the font must be bold and italic. 377 378 FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem. 379 380 FontOrientation: integer; //Specifies the rotation of the text, for functions that support text rotation. 381 //It is expressed in tenth of degrees, positive values going counter-clockwise. 382 383 //line style 384 JoinStyle: TPenJoinStyle; 385 JoinMiterLimit: single; 386 387 FillMode: TFillMode; //winding or alternate 388 LinearAntialiasing: boolean; 389 390 { The resample filter is used when resizing the bitmap, and 391 scan interpolation filter is used when the bitmap is used 392 as a scanner (IBGRAScanner) } 393 ResampleFilter, 394 ScanInterpolationFilter: TResampleFilter; 395 ScanOffset: TPoint; 396 397 constructor Create; virtual; abstract; overload; 398 constructor Create(ABitmap: TBitmap); virtual; abstract; overload; 399 constructor Create(AWidth, AHeight: integer; Color: TColor); virtual; abstract; overload; 400 constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); virtual; abstract; overload; 401 constructor Create(AFilename: string); virtual; abstract; overload; 402 constructor Create(AFilename: string; AIsUtf8Filename: boolean); virtual; abstract; overload; 403 constructor Create(AStream: TStream); virtual; abstract; overload; 404 405 function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; virtual; abstract; overload; 406 function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; overload; 407 function NewBitmap(Filename: string): TBGRACustomBitmap; virtual; abstract; overload; 408 function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; virtual; abstract; overload; 409 410 //there are UTF8 functions that are different from standard function as those 411 //depend on TFPCustomImage that does not clearly handle UTF8 412 procedure LoadFromFile(const filename: string); virtual; 413 procedure LoadFromFileUTF8(const filenameUTF8: string); virtual; 414 procedure LoadFromFileUTF8(const filenameUTF8: string; AHandler: TFPCustomImageReader); virtual; 415 procedure LoadFromStream(Str: TStream); virtual; overload; 416 procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader); virtual; overload; 417 procedure SaveToFile(const filename: string); virtual; overload; 418 procedure SaveToFile(const filename: string; Handler:TFPCustomImageWriter); virtual; overload; 419 procedure SaveToFileUTF8(const filenameUTF8: string); virtual; overload; 420 procedure SaveToFileUTF8(const filenameUTF8: string; Handler:TFPCustomImageWriter); virtual; overload; 421 procedure SaveToStreamAsPng(Str: TStream); virtual; abstract; 422 procedure SaveToStreamAs(Str: TStream; AFormat: TBGRAImageFormat); virtual; 423 procedure Assign(ARaster: TRasterImage); virtual; abstract; overload; 424 procedure Assign(MemBitmap: TBGRACustomBitmap); virtual; abstract; overload; 425 procedure Serialize(AStream: TStream); virtual; abstract; 426 procedure Deserialize(AStream: TStream); virtual; abstract; 427 428 {Pixel functions} 429 procedure SetPixel(x, y: int32or64; c: TColor); virtual; abstract; overload; 430 procedure XorPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload; 431 procedure SetPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload; 432 procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload; 433 procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload; 434 procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); virtual; abstract; overload; 435 procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; 436 procedure ErasePixel(x, y: int32or64; alpha: byte); virtual; abstract; 437 procedure AlphaPixel(x, y: int32or64; alpha: byte); virtual; abstract; 438 function GetPixel(x, y: int32or64): TBGRAPixel; virtual; abstract; overload; 439 function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract; 440 function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract; overload; 441 function GetPixelCycle(x, y: int32or64): TBGRAPixel; virtual; overload; 442 function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload; 443 function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload; 444 function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload; 445 function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload; 446 447 {Line primitives} 448 procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; 449 procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; 450 procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; overload; 451 procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); virtual; abstract; overload; 452 procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload; 453 procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; 454 procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); virtual; abstract; 455 procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract; 456 procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract; 457 procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract; 458 procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); virtual; abstract; 459 procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract; 460 procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; maxDiff: byte); virtual; abstract; 461 procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload; 462 procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); 463 procedure HorizLine(x,y,x2: Int32or64; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload; 464 465 {Shapes} 466 procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); virtual; abstract; 467 procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); virtual; abstract; 468 469 procedure ArrowStartAsNone; 470 procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); 471 procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); 472 procedure ArrowStartAsTail; 473 474 procedure ArrowEndAsNone; 475 procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); 476 procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); 477 procedure ArrowEndAsTail; 478 479 procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode= dmDrawWithTransparency); virtual; abstract; 480 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract; overload; 481 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; abstract; overload; 482 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); virtual; abstract; overload; 483 procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); virtual; abstract; overload; 484 procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload; 485 procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload; 486 procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); virtual; abstract; overload; 487 488 procedure DrawPolyLine(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode=dmDrawWithTransparency); 489 procedure DrawPolyLineAntialias(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean); virtual; overload; 490 procedure DrawPolyLineAntialias(const points: array of TPoint; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; overload; 491 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload; 492 procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload; 493 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload; 494 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload; 495 procedure DrawPolygon(const points: array of TPoint; c: TBGRAPixel; ADrawMode: TDrawMode=dmDrawWithTransparency); 496 procedure DrawPolygonAntialias(const points: array of TPoint; c: TBGRAPixel); overload; 497 procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload; 498 procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload; 499 procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload; 500 501 procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract; 502 procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract; overload; 503 procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); virtual; abstract; overload; 504 procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); virtual; abstract; overload; 505 procedure ErasePolyLine(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); 506 procedure ErasePolyLineAntialias(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); overload; 507 procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); virtual; abstract; overload; 508 procedure ErasePolygonOutline(const points: array of TPoint; alpha: byte); 509 procedure ErasePolygonOutlineAntialias(const points: array of TPoint; alpha: byte); 510 511 procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); virtual; abstract; 512 procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); virtual; abstract; 513 514 procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload; 515 procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload; 516 procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); virtual; abstract; overload; 517 procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); virtual; abstract; overload; 518 procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); virtual; abstract; overload; 519 520 procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload; 521 procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload; 522 procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True); virtual; abstract; overload; 523 procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); virtual; abstract; overload; 524 procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload; 525 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload; 526 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload; 527 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload; 528 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload; 529 530 procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel); virtual; abstract; overload; 531 procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); virtual; abstract; overload; 532 procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); virtual; abstract; overload; 533 procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload; 534 procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload; 535 536 procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract; 537 procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract; 538 procedure FillPolyAntialias(const points: array of TPointF; c: TBGRAPixel); virtual; abstract; 539 procedure FillPolyAntialias(const points: array of TPointF; texture: IBGRAScanner); virtual; abstract; 540 procedure ErasePoly(const points: array of TPointF; alpha: byte); virtual; abstract; 541 procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); virtual; abstract; 542 543 procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract; 544 procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract; 545 procedure FillShapeAntialias(shape: TBGRACustomFillInfo; c: TBGRAPixel); virtual; abstract; 546 procedure FillShapeAntialias(shape: TBGRACustomFillInfo; texture: IBGRAScanner); virtual; abstract; 547 procedure EraseShape(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract; 548 procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract; 549 550 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); virtual; abstract; 551 procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); virtual; abstract; 552 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract; 553 procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); virtual; abstract; 554 procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); virtual; abstract; 555 procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); virtual; abstract; 556 procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); virtual; abstract; 557 558 procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload; 559 procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload; 560 procedure Rectangle(x, y, x2, y2: integer; c: TColor); virtual; overload; 561 procedure Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload; 562 procedure Rectangle(r: TRect; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual;overload; 563 procedure Rectangle(r: TRect; c: TColor); virtual; overload; 564 procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single); virtual; overload; 565 procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract; overload; 566 procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload; 567 568 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload; 569 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload; 570 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); virtual; abstract; 571 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract; 572 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract; 573 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); virtual; abstract; 574 procedure FillRoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; 575 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract; 576 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract; 577 procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); virtual; abstract; 578 579 procedure EllipseInRect(r: TRect; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload; 580 procedure EllipseInRect(r: TRect; BorderColor,FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload; 581 procedure FillEllipseInRect(r: TRect; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; 582 583 procedure FillRect(r: TRect; c: TColor); virtual; overload; 584 procedure FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload; 585 procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode); virtual; overload; 586 procedure FillRect(x, y, x2, y2: integer; c: TColor); virtual; overload; 587 procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload; 588 procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload; 589 procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); virtual; abstract; 590 procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); virtual; abstract; 591 procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); virtual; abstract; 592 procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); virtual; abstract; 593 594 procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; overload; 595 procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; overload; 596 procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; 597 procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; 598 procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; overload; 599 procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; overload; 600 function TextSize(sUTF8: string): TSize; virtual; abstract; 601 602 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c or texture is used to fill the text. 603 The value of FontOrientation is taken into account, so that the text may be rotated. } 604 procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel); virtual; overload; 605 procedure TextOut(x, y: single; sUTF8: string; c: TColor); virtual; overload; 606 procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner); virtual; overload; 607 608 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary. 609 The position depends on the specified horizontal alignment halign and vertical alignement valign. 610 The color c or texture is used to fill the text. No rotation is applied. } 611 procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); virtual; overload; 612 procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); virtual; overload; 613 614 {Spline} 615 function ComputeClosedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract; 616 function ComputeOpenedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract; 617 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; virtual; abstract; 618 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract; 619 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; virtual; abstract; 620 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract; 621 622 function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract; 623 function ComputeWidePolyline(const points: array of TPointF; w: single; Closed: boolean): ArrayOfTPointF; virtual; abstract; 624 function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract; 625 626 function ComputeEllipse(x,y,rx,ry: single): ArrayOfTPointF; deprecated; 627 function ComputeEllipse(x,y,rx,ry,w: single): ArrayOfTPointF; deprecated; 628 function ComputeEllipseContour(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract; 629 function ComputeEllipseBorder(x,y,rx,ry,w: single; quality: single = 1): ArrayOfTPointF; virtual; abstract; 630 function ComputeArc65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract; 631 function ComputeArcRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract; 632 function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract; 633 function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; virtual; abstract; 634 function ComputePie65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract; 635 function ComputePieRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract; 636 637 {Filling} 638 procedure FillTransparent; virtual; 639 procedure NoClip; virtual; abstract; 640 procedure ApplyGlobalOpacity(alpha: byte); virtual; abstract; 641 procedure Fill(c: TColor); virtual; overload; 642 procedure Fill(c: TBGRAPixel); virtual; overload; 643 procedure Fill(texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload; 644 procedure Fill(texture: IBGRAScanner); virtual; abstract; overload; 645 procedure Fill(c: TBGRAPixel; start, Count: integer); virtual; abstract; overload; 646 procedure DrawPixels(c: TBGRAPixel; start, Count: integer); virtual; abstract; 647 procedure AlphaFill(alpha: byte); virtual; overload; 648 procedure AlphaFill(alpha: byte; start, Count: integer); virtual; abstract; overload; 649 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); virtual; overload; 650 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); virtual; overload; 651 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); virtual; abstract; overload; 652 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload; 653 procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); virtual; abstract; overload; 654 procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); virtual; abstract; overload; 655 procedure ReplaceColor(before, after: TColor); virtual; abstract; overload; 656 procedure ReplaceColor(before, after: TBGRAPixel); virtual; abstract; overload; 657 procedure ReplaceTransparent(after: TBGRAPixel); virtual; abstract; overload; 658 procedure FloodFill(X, Y: integer; Color: TBGRAPixel; 659 mode: TFloodfillMode; Tolerance: byte = 0); virtual; 660 procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel; 661 mode: TFloodfillMode; Tolerance: byte = 0); virtual; abstract; 662 procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel; 663 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 664 gammaColorCorrection: boolean = True; Sinus: Boolean=False); virtual; abstract; 665 procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient; 666 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 667 Sinus: Boolean=False); virtual; abstract; 668 function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel; 669 AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; virtual; abstract; 670 671 {Canvas drawing functions} 672 procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect; 673 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract; 674 procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer; 675 ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract; 676 procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); virtual; abstract; 677 procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); virtual; abstract; 678 procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); virtual; abstract; 679 procedure DrawPart(ARect: TRect; Canvas: TCanvas; x, y: integer; Opaque: boolean); virtual; 680 function GetPart(ARect: TRect): TBGRACustomBitmap; virtual; abstract; 681 function GetPtrBitmap(Top,Bottom: Integer): TBGRACustomBitmap; virtual; abstract; 682 procedure InvalidateBitmap; virtual; abstract; //call if you modify with Scanline 683 procedure LoadFromBitmapIfNeeded; virtual; abstract; //call to ensure that bitmap data is up to date 684 685 {BGRA bitmap functions} 686 procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract; 687 procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract; 688 procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract; 689 procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract; 690 procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap); 691 procedure PutImagePart(x,y: integer; Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte = 255); 692 procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload; 693 procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte=255); overload; 694 procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); virtual; abstract; overload; 695 procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload; 696 function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap): TRect; 697 procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload; 698 procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload; 699 procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload; 700 procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload; 701 procedure ComputeImageAngleAxes(x,y,w,h,angle: single; imageCenterX,imageCenterY: single; ARestoreOffsetAfterRotation: boolean; 702 out Origin,HAxis,VAxis: TPointF); 703 function GetImageAngleBounds(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; ARestoreOffsetAfterRotation: boolean = false): TRect; 704 procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); virtual; abstract; 705 procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255; 706 ALinearBlend: boolean = false); virtual; abstract; 707 function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; virtual; abstract; 708 function Equals(comp: TBGRACustomBitmap): boolean; virtual; abstract; 709 function Equals(comp: TBGRAPixel): boolean; virtual; abstract; 710 function Resample(newWidth, newHeight: integer; 711 mode: TResampleMode = rmFineResample): TBGRACustomBitmap; virtual; abstract; 712 procedure VerticalFlip; virtual; overload; 713 procedure VerticalFlip(ARect: TRect); virtual; abstract; overload; 714 procedure HorizontalFlip; virtual; overload; 715 procedure HorizontalFlip(ARect: TRect); virtual; abstract; overload; 716 function RotateCW: TBGRACustomBitmap; virtual; abstract; 717 function RotateCCW: TBGRACustomBitmap; virtual; abstract; 718 procedure Negative; virtual; abstract; 719 procedure NegativeRect(ABounds: TRect); virtual; abstract; 720 procedure LinearNegative; virtual; abstract; 721 procedure LinearNegativeRect(ABounds: TRect); virtual; abstract; 722 procedure InplaceGrayscale; virtual; abstract; 723 procedure InplaceGrayscale(ABounds: TRect); virtual; abstract; 724 procedure ConvertToLinearRGB; virtual; abstract; 725 procedure ConvertFromLinearRGB; virtual; abstract; 726 procedure SwapRedBlue; virtual; abstract; 727 procedure GrayscaleToAlpha; virtual; abstract; 728 procedure AlphaToGrayscale; virtual; abstract; 729 procedure ApplyMask(mask: TBGRACustomBitmap); overload; 730 procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect); overload; 731 procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); virtual; abstract; overload; 732 function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; virtual; abstract; 733 function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; virtual; abstract; 734 function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; virtual; abstract; 735 function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; virtual; abstract; 736 737 {Filters} 738 function FilterSmartZoom3(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract; 739 function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract; 740 function FilterSmooth: TBGRACustomBitmap; virtual; abstract; 741 function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; virtual; abstract; 742 function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; virtual; abstract; 743 function FilterContour: TBGRACustomBitmap; virtual; abstract; 744 function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract; 745 function FilterBlurRadial(radius: integer; 746 blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract; 747 function FilterBlurRadial(ABounds: TRect; radius: integer; 748 blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract; 749 function FilterBlurMotion(distance: integer; angle: single; 750 oriented: boolean): TBGRACustomBitmap; virtual; abstract; 751 function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single; 752 oriented: boolean): TBGRACustomBitmap; virtual; abstract; 753 function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract; 754 function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract; 755 function FilterEmboss(angle: single): TBGRACustomBitmap; virtual; abstract; 756 function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; virtual; abstract; 757 function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; virtual; abstract; 758 function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; 759 function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; virtual; abstract; 760 function FilterGrayscale: TBGRACustomBitmap; virtual; abstract; 761 function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; virtual; abstract; 762 function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract; 763 function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract; 764 function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; virtual; abstract; 765 function FilterSphere: TBGRACustomBitmap; virtual; abstract; 766 function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract; 767 function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract; 768 function FilterCylinder: TBGRACustomBitmap; virtual; abstract; 769 function FilterPlane: TBGRACustomBitmap; virtual; abstract; 770 771 property Width: integer Read GetWidth; //width of the image in pixels 772 property Height: integer Read GetHeight; //height of the image in pixels 773 property NbPixels: integer Read GetNbPixels; //total number of pixels. It is always true that NbPixels = Width * Height 774 775 property ScanLine[y: integer]: PBGRAPixel Read GetScanLine; //Returns the address of the left-most pixel of any line. 776 //The parameter y ranges from 0 to Height-1. 777 778 property LineOrder: TRawImageLineOrder Read GetLineOrder; //Indicates the order in which lines are stored in memory. 779 //If it is equal to riloTopToBottom, the first line is the top line. 780 //If it is equal to riloBottomToTop, the first line is the bottom line. 781 782 property Data: PBGRAPixel Read GetDataPtr; //Provides a pointer to the first pixel in memory. 783 //Depending on the LineOrder property, this can be the top-left pixel or the bottom-left pixel. 784 //There is no padding between scanlines, so the start of the next line is at the address Data + Width. 785 786 property Empty: boolean Read CheckEmpty; //Returns True if the bitmap only contains transparent pixels or has a size of zero. 787 788 property HasTransparentPixels: boolean Read GetHasTransparentPixels; //Returns True if there are transparent or semitransparent pixels, 789 //and so if the image would be stored with an alpha channel. 790 791 property RefCount: integer Read GetRefCount; 792 property Bitmap: TBitmap Read GetBitmap; //don't forget to call InvalidateBitmap before if you changed something with Scanline 793 property AverageColor: TColor Read GetAverageColor; 794 property AveragePixel: TBGRAPixel Read GetAveragePixel; 795 property CanvasFP: TFPImageCanvas read GetCanvasFP; 796 property CanvasDrawModeFP: TDrawMode read GetCanvasDrawModeFP write SetCanvasDrawModeFP; 797 property Canvas: TCanvas Read GetCanvas; 798 property CanvasOpacity: byte Read GetCanvasOpacity Write SetCanvasOpacity; 799 property CanvasAlphaCorrection: boolean 800 Read GetCanvasAlphaCorrection Write SetCanvasAlphaCorrection; 801 802 property PenStyle: TPenStyle read GetPenStyle Write SetPenStyle; 803 property CustomPenStyle: TBGRAPenStyle read GetCustomPenStyle write SetCustomPenStyle; 804 property ClipRect: TRect read GetClipRect write SetClipRect; 805 806 { Specifies the height of the font without taking into account additional line spacing. 807 A negative value means that it is the full height instead (see below). } 808 property FontHeight: integer Read GetFontHeight Write SetFontHeight; 809 810 { Specifies the height of the font, taking into account the additional line spacing defined for the font. } 811 property FontFullHeight: integer read GetFontFullHeight write SetFontFullHeight; 812 813 property FontAntialias: Boolean read GetFontAntialias write SetFontAntialias; //Simplified property to specify the quality. 814 property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric; //Returns measurement for the current font in pixels. 815 816 { Specifies the font renderer. By default it is an instance of TLCLFontRenderer of unit BGRAText. 817 Other renderers are provided in BGRATextFX unit and BGRAVectorize unit. 818 Once you assign a renderer, it will automatically be freed. 819 The renderers may provide additional styling for the font. } 820 property FontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer; 821 822 property LineCap: TPenEndCap read GetLineCap write SetLineCap; 823 property ArrowStartSize: TPointF read GetArrowStartSize write SetArrowStartSize; 824 property ArrowEndSize: TPointF read GetArrowEndSize write SetArrowEndSize; 825 property ArrowStartOffset: single read GetArrowStartOffset write SetArrowStartOffset; 826 property ArrowEndOffset: single read GetArrowEndOffset write SetArrowEndOffset; 827 property ArrowStartRepeat: integer read GetArrowStartRepeat write SetArrowStartRepeat; 828 property ArrowEndRepeat: integer read GetArrowEndRepeat write SetArrowEndRepeat; 829 830 //IBGRAScanner 831 function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual; abstract; 832 procedure ScanMoveTo(X,Y: Integer); virtual; abstract; 833 function ScanNextPixel: TBGRAPixel; virtual; abstract; 834 function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract; 835 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual; 836 function IsScanPutPixelsDefined: boolean; virtual; 837 838 protected 839 //interface 840 function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 841 function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 842 function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 843 844 end; 845 846 { TBGRACustomScanner } 847 848 TBGRACustomScanner = class(IBGRAScanner) 849 private 850 FCurX,FCurY: integer; 851 public 852 function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual; 853 procedure ScanMoveTo(X,Y: Integer); virtual; 854 function ScanNextPixel: TBGRAPixel; virtual; 855 function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract; 856 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual; 857 function IsScanPutPixelsDefined: boolean; virtual; 858 protected 859 function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 860 function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 861 function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 862 end; 863 864 { TBGRACustomGradient } 865 866 TBGRACustomGradient = class 867 public 868 function GetColorAt(position: integer): TBGRAPixel; virtual; abstract; 869 function GetColorAtF(position: single): TBGRAPixel; virtual; 870 function GetAverageColor: TBGRAPixel; virtual; abstract; 871 function GetMonochrome: boolean; virtual; abstract; 872 property Monochrome: boolean read GetMonochrome; 873 end; 874 875 { TIntersectionInfo } 876 877 TIntersectionInfo = class 878 interX: single; 879 winding: integer; 880 numSegment: integer; 881 procedure SetValues(AInterX: Single; AWinding, ANumSegment: integer); 882 end; 883 ArrayOfTIntersectionInfo = array of TIntersectionInfo; 884 885 TBGRACustomFillInfo = class 886 public 887 //returns true if the same segment number can be curved 888 function SegmentsCurved: boolean; virtual; abstract; 889 890 //returns integer bounds 891 function GetBounds: TRect; virtual; abstract; 892 893 //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if 894 //there is nothing to draw 895 function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; virtual; abstract; 896 897 //check if the point is inside the filling zone 898 function IsPointInside(x,y: single; windingMode: boolean): boolean; virtual; abstract; 899 900 //create an array that will contain computed intersections. 901 //you may augment, in this case, use CreateIntersectionInfo for new items 902 function CreateIntersectionArray: ArrayOfTIntersectionInfo; virtual; abstract; 903 function CreateIntersectionInfo: TIntersectionInfo; virtual; abstract; //creates a single info 904 procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo); virtual; abstract; 905 906 //fill a previously created array of intersections with actual intersections at the current y coordinate. 907 //nbInter gets the number of computed intersections 908 procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); virtual; abstract; 909 end; 910 911 { TBGRACustomFontRenderer } 912 913 TBGRACustomFontRenderer = class 914 FontName: string; //Specifies the font to use. Unless the font renderer accept otherwise, 915 //the name is in human readable form, like 'Arial', 'Times New Roman', ... 916 917 FontStyle: TFontStyles; //Specifies the set of styles to be applied to the font. 918 //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline. 919 //So the value [fsBold,fsItalic] means that the font must be bold and italic. 920 921 FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem. 922 923 FontOrientation: integer; //Specifies the rotation of the text, for functions that support text rotation. 924 //It is expressed in tenth of degrees, positive values going counter-clockwise. 925 926 FontEmHeight: integer; // Specifies the height of the font without taking into account additional line spacing. 927 // A negative value means that it is the full height instead. 928 929 { Returns measurement for the current font in pixels. } 930 function GetFontPixelMetric: TFontPixelMetric; virtual; abstract; 931 932 { Returns the total size of the string provided using the current font. 933 Orientation is not taken into account, so that the width is along the text. } 934 function TextSize(sUTF8: string): TSize; virtual; abstract; 935 936 { Draws the UTF8 encoded string, with color c. 937 If align is taLeftJustify, (x,y) is the top-left corner. 938 If align is taCenter, (x,y) is at the top and middle of the text. 939 If align is taRightJustify, (x,y) is the top-right corner. 940 The value of FontOrientation is taken into account, so that the text may be rotated. } 941 procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; 942 943 { Same as above functions, except that the text is filled using texture. 944 The value of FontOrientation is taken into account, so that the text may be rotated. } 945 procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; 946 947 { Same as above, except that the orientation is specified, overriding the value of the property FontOrientation. } 948 procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; 949 procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; 950 951 { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect. 952 Additional style information is provided by the style parameter. 953 The color c or texture is used to fill the text. No rotation is applied. } 954 procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; 955 procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; 956 957 { Copy the path for the UTF8 encoded string into ADest. 958 If align is taLeftJustify, (x,y) is the top-left corner. 959 If align is taCenter, (x,y) is at the top and middle of the text. 960 If align is taRightJustify, (x,y) is the top-right corner. } 961 procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional 962 end; 963 964 type 965 TBGRABitmapAny = class of TBGRACustomBitmap; //used to create instances of the same type (see NewBitmap) 966 TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR); 967 968 var 969 BGRABitmapFactory : TBGRABitmapAny; 970 BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode); 971 972 function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean; inline; 973 974 { Color functions } 975 function GetIntensity(const c: TExpandedPixel): word; inline; 976 function GetIntensity(c: TBGRAPixel): word; inline; 977 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel; 978 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel; 979 function GetLightness(c: TBGRAPixel): word; 980 function GetLightness(const c: TExpandedPixel): word; inline; 981 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel; 982 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel; 983 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel; //if you already know the current lightness of the color 984 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel; inline; 985 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel; 986 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64; 987 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel; 988 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel; 989 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel; 990 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel; 991 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel; 992 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel; 993 function GtoH(ghue: word): word; 994 function HtoG(hue: word): word; 995 function HueDiff(h1, h2: word): word; 996 function GetHue(ec: TExpandedPixel): word; 997 function ColorImportance(ec: TExpandedPixel): word; 998 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel; 999 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel; 1000 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel; 1001 function GammaExpansion(c: TBGRAPixel): TExpandedPixel; inline; 1002 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel; inline; 1003 function GammaCompression(red,green,blue,alpha: word): TBGRAPixel; inline; 1004 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel; 1005 function GrayscaleToBGRA(lightness: word): TBGRAPixel; 1006 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel; overload; 1007 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel; weight2: byte): TBGRAPixel; 1008 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel; overload; 1009 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel; weight2: integer): TBGRAPixel; overload; 1010 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel; overload; 1011 function BGRA(red, green, blue, alpha: byte): TBGRAPixel; overload; inline; 1012 function BGRA(red, green, blue: byte): TBGRAPixel; overload; inline; 1013 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel; overload; inline; 1014 function HSLA(hue, saturation, lightness: word): THSLAPixel; overload; inline; 1015 function ColorToBGRA(color: TColor): TBGRAPixel; overload; 1016 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload; 1017 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline; 1018 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel; 1019 function BGRAToColor(c: TBGRAPixel): TColor; 1020 operator = (const c1, c2: TBGRAPixel): boolean; inline; 1021 function ExpandedDiff(ec1, ec2: TExpandedPixel): word; 1022 function BGRAWordDiff(c1, c2: TBGRAPixel): word; 1023 function BGRADiff(c1, c2: TBGRAPixel): byte; 1024 operator - (const c1, c2: TColorF): TColorF; inline; 1025 operator + (const c1, c2: TColorF): TColorF; inline; 1026 operator * (const c1, c2: TColorF): TColorF; inline; 1027 operator * (const c1: TColorF; factor: single): TColorF; inline; 1028 function ColorF(red,green,blue,alpha: single): TColorF; 1029 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string; 1030 function StrToBGRA(str: string): TBGRAPixel; //full parse 1031 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; //full parse with default when error or missing values 1032 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel; //partial parse allowed 1033 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean); 1034 1035 { Get height [0..1] stored in a TBGRAPixel } 1036 function MapHeight(Color: TBGRAPixel): Single; 1037 1038 { Get TBGRAPixel to store height [0..1] } 1039 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel; 1040 1041 1042 { Gamma conversion arrays. Should be used as readonly } 1043 var 1044 // TBGRAPixel -> TExpandedPixel 1045 GammaExpansionTab: packed array[0..255] of word; 1046 1047 // TExpandedPixel -> TBGRAPixel 1048 GammaCompressionTab: packed array[0..65535] of byte; 1049 1050 { Point functions } 1051 function PointF(x, y: single): TPointF; 1052 function PointsF(const pts: array of TPointF): ArrayOfTPointF; 1053 operator = (const pt1, pt2: TPointF): boolean; inline; 1054 operator - (const pt1, pt2: TPointF): TPointF; inline; 1055 operator - (const pt2: TPointF): TPointF; inline; 1056 operator + (const pt1, pt2: TPointF): TPointF; inline; 1057 operator * (const pt1, pt2: TPointF): single; inline; //scalar product 1058 operator * (const pt1: TPointF; factor: single): TPointF; inline; 1059 operator * (factor: single; const pt1: TPointF): TPointF; inline; 1060 function PtInRect(const pt: TPoint; r: TRect): boolean; overload; 1061 function RectWithSize(left,top,width,height: integer): TRect; 1062 function VectLen(dx,dy: single): single; overload; 1063 function VectLen(v: TPointF): single; overload; 1064 1065 { Line and polygon functions } 1066 type 1067 TLineDef = record 1068 origin, dir: TPointF; 1069 end; 1070 1071 function IntersectLine(line1, line2: TLineDef): TPointF; 1072 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF; 1073 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean; 1074 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean; 1075 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean; 1076 1077 { Cyclic functions } 1078 function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload; 1079 1080 { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values. 1081 They use a table to store already computed values. The return value is an integer 1082 ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is 1083 32768 instead of 1. The input has a period of 65536, so you can supply any integer 1084 without applying a modulo. } 1085 procedure PrecalcSin65536; // compute all values now 1086 function Sin65536(value: word): Int32or64; inline; 1087 function Cos65536(value: word): Int32or64; inline; 1088 function ByteSqrt(value: byte): byte; inline; 1089 1090 function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat; 1091 function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat; 1092 function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat; 1093 function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader; 1094 function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter; 441 {** Detect the file format of a given file } 442 function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat; 443 {** Detect the file format of a given stream. ''ASuggestedExtensionUTF8'' can 444 be provided to guess the format } 445 function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat; 446 {** Returns the file format that is most likely to be stored in the 447 given filename (according to its extension) } 448 function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat; 449 {** Returns a likely image extension for the format } 450 function SuggestImageExtension(AFormat: TBGRAImageFormat): string; 451 {** Create an image reader for the given format } 452 function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader; 453 {** Create an image writer for the given format. ''AHasTransparentPixels'' 454 specifies if alpha channel must be supported } 455 function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter; 456 457 {$DEFINE INCLUDE_INTERFACE} 458 {$I bgracustombitmap.inc} 1095 459 1096 460 implementation 1097 461 1098 uses Math, SysUtils, FileUtil, lazutf8classes, LCLProc,462 uses Math, SysUtils, BGRAUTF8, 1099 463 FPReadTiff, FPReadXwd, FPReadXPM, 1100 FPWriteTiff, FPWriteJPEG, FPWritePNG, FPWriteBMP, FPWritePCX,464 FPWriteTiff, FPWriteJPEG, BGRAWritePNG, FPWriteBMP, FPWritePCX, 1101 465 FPWriteTGA, FPWriteXPM; 466 467 {$DEFINE INCLUDE_IMPLEMENTATION} 468 {$I geometrytypes.inc} 469 470 {$DEFINE INCLUDE_IMPLEMENTATION} 471 {$I csscolorconst.inc} 472 473 {$DEFINE INCLUDE_IMPLEMENTATION} 474 {$I bgracustombitmap.inc} 475 476 {$DEFINE INCLUDE_IMPLEMENTATION} 477 {$I bgrapixel.inc} 478 479 function CleanTextOutString(s: string): string; 480 var idxIn, idxOut: integer; 481 begin 482 setlength(result, length(s)); 483 idxIn := 1; 484 idxOut := 1; 485 while IdxIn <= length(s) do 486 begin 487 if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8 488 begin 489 result[idxOut] := s[idxIn]; 490 inc(idxOut); 491 end; 492 inc(idxIn); 493 end; 494 setlength(result, idxOut-1); 495 end; 496 497 function RemoveLineEnding(var s: string; indexByte: integer): boolean; 498 begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long 499 //so this function can be applied to UTF8 strings as well 500 result := false; 501 if length(s) >= indexByte then 502 begin 503 if s[indexByte] in[#13,#10] then 504 begin 505 result := true; 506 if length(s) >= indexByte+1 then 507 begin 508 if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then 509 delete(s,indexByte,2) 510 else 511 delete(s,indexByte,1); 512 end 513 else 514 delete(s,indexByte,1); 515 end; 516 end; 517 end; 518 519 function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean; 520 var indexByte: integer; 521 pIndex: PChar; 522 begin 523 pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8); 524 if pIndex = nil then 525 begin 526 result := false; 527 exit; 528 end; 529 indexByte := pIndex - @sUTF8[1]; 530 result := RemoveLineEnding(sUTF8, indexByte); 531 end; 532 533 procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string); 534 var p: integer; 535 begin 536 if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then 537 begin 538 p := length(ABefore); 539 while (p > 1) and (ABefore[p-1] <> ' ') do dec(p); 540 if p > 1 then //can put the word after 541 begin 542 AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter; 543 ABefore := copy(ABefore,1,p-1); 544 end else 545 begin //cannot put the word after, so before 546 547 end; 548 end; 549 while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1); 550 while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1); 551 end; 552 1102 553 1103 554 function StrToResampleFilter(str: string): TResampleFilter; … … 1114 565 end; 1115 566 1116 function StrToBlendOperation(str: string): TBlendOperation;1117 var op: TBlendOperation;1118 begin1119 result := boTransparent;1120 str := LowerCase(str);1121 for op := low(TBlendOperation) to high(TBlendOperation) do1122 if str = LowerCase(BlendOperationStr[op]) then1123 begin1124 result := op;1125 exit;1126 end;1127 end;1128 1129 function StrToGradientType(str: string): TGradientType;1130 var gt: TGradientType;1131 begin1132 result := gtLinear;1133 str := LowerCase(str);1134 for gt := low(TGradientType) to high(TGradientType) do1135 if str = LowerCase(GradientTypeStr[gt]) then1136 begin1137 result := gt;1138 exit;1139 end;1140 end;1141 1142 { Make a pen style. Need an even number of values. See TBGRAPenStyle }1143 function BGRAPenStyle(dash1, space1: single; dash2: single; space2: single;1144 dash3: single; space3: single; dash4: single; space4: single): TBGRAPenStyle;1145 var1146 i: Integer;1147 begin1148 if dash4 <> 0 then1149 begin1150 setlength(result,8);1151 result[6] := dash4;1152 result[7] := space4;1153 result[4] := dash3;1154 result[5] := space3;1155 result[2] := dash2;1156 result[3] := space2;1157 end else1158 if dash3 <> 0 then1159 begin1160 setlength(result,6);1161 result[4] := dash3;1162 result[5] := space3;1163 result[2] := dash2;1164 result[3] := space2;1165 end else1166 if dash2 <> 0 then1167 begin1168 setlength(result,4);1169 result[2] := dash2;1170 result[3] := space2;1171 end else1172 begin1173 setlength(result,2);1174 end;1175 result[0] := dash1;1176 result[1] := space1;1177 for i := 0 to high(result) do1178 if result[i]=0 then1179 raise exception.Create('Zero is not a valid value');1180 end;1181 1182 { Bézier curves definitions. See : http://en.wikipedia.org/wiki/B%C3%A9zier_curve }1183 1184 function ConcatPointsF(const APolylines: array of ArrayOfTPointF1185 ): ArrayOfTPointF;1186 var1187 i,pos,count:integer;1188 j: Integer;1189 begin1190 count := 0;1191 for i := 0 to high(APolylines) do1192 inc(count,length(APolylines[i]));1193 setlength(result,count);1194 pos := 0;1195 for i := 0 to high(APolylines) do1196 for j := 0 to high(APolylines[i]) do1197 begin1198 result[pos] := APolylines[i][j];1199 inc(pos);1200 end;1201 end;1202 1203 operator-(const v: TPoint3D): TPoint3D;1204 begin1205 result.x := -v.x;1206 result.y := -v.y;1207 result.z := -v.z;1208 end;1209 1210 operator + (const v1,v2: TPoint3D): TPoint3D; inline;1211 begin1212 result.x := v1.x+v2.x;1213 result.y := v1.y+v2.y;1214 result.z := v1.z+v2.z;1215 end;1216 1217 operator - (const v1,v2: TPoint3D): TPoint3D; inline;1218 begin1219 result.x := v1.x-v2.x;1220 result.y := v1.y-v2.y;1221 result.z := v1.z-v2.z;1222 end;1223 1224 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline;1225 begin1226 result.x := v1.x*factor;1227 result.y := v1.y*factor;1228 result.z := v1.z*factor;1229 end;1230 1231 function Point3D(x, y, z: single): TPoint3D;1232 begin1233 result.x := x;1234 result.y := y;1235 result.z := z;1236 end;1237 1238 operator=(const v1, v2: TPoint3D): boolean;1239 begin1240 result := (v1.x=v2.x) and (v1.y=v2.y) and (v1.z=v2.z);1241 end;1242 1243 operator * (const v1,v2: TPoint3D): single; inline;1244 begin1245 result := v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;1246 end;1247 1248 procedure Normalize3D(var v: TPoint3D); inline;1249 var len: double;1250 begin1251 len := v*v;1252 if len = 0 then exit;1253 len := sqrt(len);1254 v.x /= len;1255 v.y /= len;1256 v.z /= len;1257 end;1258 1259 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D);1260 begin1261 w.x := u.y*v.z-u.z*v.y;1262 w.y := u.z*v.x-u.x*v.z;1263 w.z := u.x*v.Y-u.y*v.x;1264 end;1265 1266 // Define a Bézier curve with two control points.1267 function BezierCurve(origin, control1, control2, destination: TPointF): TCubicBezierCurve;1268 begin1269 result.p1 := origin;1270 result.c1 := control1;1271 result.c2 := control2;1272 result.p2 := destination;1273 end;1274 1275 // Define a Bézier curve with one control point.1276 function BezierCurve(origin, control, destination: TPointF1277 ): TQuadraticBezierCurve;1278 begin1279 result.p1 := origin;1280 result.c := control;1281 result.p2 := destination;1282 end;1283 1284 //straight line1285 function BezierCurve(origin, destination: TPointF): TQuadraticBezierCurve;1286 begin1287 result.p1 := origin;1288 result.c := (origin+destination)*0.5;1289 result.p2 := destination;1290 end;1291 1292 function ArcDef(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;1293 anticlockwise: boolean): TArcDef;1294 begin1295 result.center := PointF(cx,cy);1296 result.radius := PointF(rx,ry);1297 result.xAngleRadCW:= xAngleRadCW;1298 result.startAngleRadCW := startAngleRadCW;1299 result.endAngleRadCW:= endAngleRadCW;1300 result.anticlockwise:= anticlockwise;1301 end;1302 1303 { Check if a PointF structure is empty or should be treated as a list separator }1304 function isEmptyPointF(pt: TPointF): boolean;1305 begin1306 Result := (pt.x = EmptySingle) and (pt.y = EmptySingle);1307 end;1308 1309 567 { TBGRACustomFontRenderer } 1310 568 1311 569 procedure TBGRACustomFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment); 1312 begin 1313 end; 1314 1315 { TIntersectionInfo } 1316 1317 procedure TIntersectionInfo.SetValues(AInterX: Single; AWinding, 1318 ANumSegment: integer); 1319 begin 1320 interX := AInterX; 1321 winding := AWinding; 1322 numSegment := ANumSegment; 1323 end; 1324 1325 { TBGRACustomGradient } 1326 1327 function TBGRACustomGradient.GetColorAtF(position: single): TBGRAPixel; 1328 begin 1329 position *= 65536; 1330 if position < low(integer) then 1331 result := GetColorAt(low(Integer)) 1332 else if position > high(integer) then 1333 result := GetColorAt(high(Integer)) 1334 else 1335 result := GetColorAt(round(position)); 1336 end; 1337 1338 { TBGRAColorList } 1339 1340 function TBGRAColorList.GetByIndex(Index: integer): TBGRAPixel; 1341 begin 1342 if (Index < 0) or (Index >= FNbColors) then 1343 result := BGRAPixelTransparent 1344 else 1345 result := FColors[Index].Color; 1346 end; 1347 1348 function TBGRAColorList.GetByName(Name: string): TBGRAPixel; 1349 var i: integer; 1350 begin 1351 i := IndexOf(Name); 1352 if i = -1 then 1353 result := BGRAPixelTransparent 1354 else 1355 result := FColors[i].Color; 1356 end; 1357 1358 function TBGRAColorList.GetName(Index: integer): string; 1359 begin 1360 if (Index < 0) or (Index >= FNbColors) then 1361 result := '' 1362 else 1363 result := FColors[Index].Name; 1364 end; 1365 1366 constructor TBGRAColorList.Create; 1367 begin 1368 FNbColors:= 0; 1369 FColors := nil; 1370 FFinished:= false; 1371 end; 1372 1373 procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel); 1374 begin 1375 if FFinished then 1376 raise Exception.Create('This list is already finished'); 1377 if length(FColors) = FNbColors then 1378 SetLength(FColors, FNbColors*2+1); 1379 FColors[FNbColors].Name := Name; 1380 FColors[FNbColors].Color := Color; 1381 inc(FNbColors); 1382 end; 1383 1384 procedure TBGRAColorList.Finished; 1385 begin 1386 if FFinished then exit; 1387 FFinished := true; 1388 SetLength(FColors, FNbColors); 1389 end; 1390 1391 function TBGRAColorList.IndexOf(Name: string): integer; 1392 var i: integer; 1393 begin 1394 for i := 0 to FNbColors-1 do 1395 if CompareText(Name, FColors[i].Name) = 0 then 1396 begin 1397 result := i; 1398 exit; 1399 end; 1400 result := -1; 1401 end; 1402 1403 function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer; 1404 var i: integer; 1405 MinDiff,CurDiff: Word; 1406 begin 1407 if AMaxDiff = 0 then 1408 begin 1409 for i := 0 to FNbColors-1 do 1410 if AColor = FColors[i].Color then 1411 begin 1412 result := i; 1413 exit; 1414 end; 1415 result := -1; 1416 end else 1417 begin 1418 MinDiff := AMaxDiff; 1419 result := -1; 1420 for i := 0 to FNbColors-1 do 1421 begin 1422 CurDiff := BGRAWordDiff(AColor,FColors[i].Color); 1423 if CurDiff <= MinDiff then 1424 begin 1425 result := i; 1426 MinDiff := CurDiff; 1427 if MinDiff = 0 then exit; 1428 end; 1429 end; 1430 end; 1431 end; 1432 1433 { TBGRACustomBitmap } 1434 1435 function TBGRACustomBitmap.GetFontAntialias: Boolean; 1436 begin 1437 result := FontQuality <> fqSystem; 1438 end; 1439 1440 procedure TBGRACustomBitmap.SetFontAntialias(const AValue: Boolean); 1441 begin 1442 if AValue and not FontAntialias then 1443 FontQuality := fqFineAntialiasing 1444 else if not AValue and (FontQuality <> fqSystem) then 1445 FontQuality := fqSystem; 1446 end; 1447 1448 { These declaration make sure that these methods are virtual } 1449 procedure TBGRACustomBitmap.LoadFromFile(const filename: string); 1450 begin 1451 LoadFromFileUTF8(SysToUtf8(filename)); 1452 end; 1453 1454 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string); 1455 var 1456 Stream: TStream; 1457 format: TBGRAImageFormat; 1458 reader: TFPCustomImageReader; 1459 begin 1460 stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite); 1461 try 1462 format := DetectFileFormat(Stream, ExtractFileExt(filenameUTF8)); 1463 reader := CreateBGRAImageReader(format); 1464 try 1465 LoadFromStream(stream, reader); 1466 finally 1467 reader.Free; 1468 end; 1469 finally 1470 ClearTransparentPixels; 1471 stream.Free; 1472 end; 1473 end; 1474 1475 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string; 1476 AHandler: TFPCustomImageReader); 1477 var 1478 Stream: TStream; 1479 begin 1480 stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite); 1481 try 1482 LoadFromStream(stream, AHandler); 1483 finally 1484 ClearTransparentPixels; 1485 stream.Free; 1486 end; 1487 end; 1488 1489 procedure TBGRACustomBitmap.SaveToFile(const filename: string); 1490 begin 1491 SaveToFileUTF8(SysToUtf8(filename)); 1492 end; 1493 1494 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string); 1495 var 1496 writer: TFPCustomImageWriter; 1497 format: TBGRAImageFormat; 1498 begin 1499 format := SuggestImageFormat(filenameUTF8); 1500 writer := CreateBGRAImageWriter(Format, HasTransparentPixels); 1501 try 1502 SaveToFileUTF8(filenameUTF8, writer); 1503 finally 1504 writer.free; 1505 end; 1506 end; 1507 1508 procedure TBGRACustomBitmap.SaveToFile(const filename: string; 1509 Handler: TFPCustomImageWriter); 1510 begin 1511 SaveToFileUTF8(SysToUtf8(filename),Handler); 1512 end; 1513 1514 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string; 1515 Handler: TFPCustomImageWriter); 1516 var 1517 stream: TFileStreamUTF8; 1518 begin 1519 stream := TFileStreamUTF8.Create(filenameUTF8,fmCreate); 1520 try 1521 SaveToStream(stream, Handler); 1522 finally 1523 stream.Free; 1524 end; 1525 end; 1526 1527 procedure TBGRACustomBitmap.SaveToStreamAs(Str: TStream; 1528 AFormat: TBGRAImageFormat); 1529 var handler: TFPCustomImageWriter; 1530 begin 1531 handler := CreateBGRAImageWriter(AFormat, HasTransparentPixels); 1532 try 1533 SaveToStream(Str, handler) 1534 finally 1535 handler.Free; 1536 end; 1537 end; 1538 1539 procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel; 1540 ADrawMode: TDrawMode); 1541 begin 1542 case ADrawMode of 1543 dmSet: SetPixel(x,y,c); 1544 dmSetExceptTransparent: if c.alpha = 255 then SetPixel(x,y,c); 1545 dmLinearBlend: FastBlendPixel(x,y,c); 1546 dmDrawWithTransparency: DrawPixel(x,y,c); 1547 dmXor: XorPixel(x,y,c); 1548 end; 1549 end; 1550 1551 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream); 1552 var 1553 format: TBGRAImageFormat; 1554 reader: TFPCustomImageReader; 1555 begin 1556 format := DetectFileFormat(Str); 1557 reader := CreateBGRAImageReader(format); 1558 try 1559 LoadFromStream(Str,reader); 1560 finally 1561 reader.Free; 1562 end; 1563 end; 1564 1565 { LoadFromStream uses TFPCustomImage routine, which uses 1566 Colors property to access pixels. That's why the 1567 FP drawing mode is temporarily changed to load 1568 bitmaps properly } 1569 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream; 1570 Handler: TFPCustomImageReader); 1571 var 1572 OldDrawMode: TDrawMode; 1573 begin 1574 OldDrawMode := CanvasDrawModeFP; 1575 CanvasDrawModeFP := dmSet; 1576 try 1577 inherited LoadFromStream(Str, Handler); 1578 finally 1579 CanvasDrawModeFP := OldDrawMode; 1580 end; 1581 end; 1582 1583 { Look for a pixel considering the bitmap is repeated in both directions } 1584 function TBGRACustomBitmap.GetPixelCycle(x, y: int32or64): TBGRAPixel; 1585 begin 1586 if (Width = 0) or (Height = 0) then 1587 Result := BGRAPixelTransparent 1588 else 1589 Result := (Scanline[PositiveMod(y,Height)] + PositiveMod(x,Width))^; 1590 end; 1591 1592 procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64; 1593 texture: IBGRAScanner); 1594 begin 1595 HorizLine(x,y,x2,texture,dmDrawWithTransparency); 1596 end; 1597 1598 procedure TBGRACustomBitmap.HorizLine(x, y, x2: Int32or64; c: TBGRAPixel; 1599 ADrawMode: TDrawMode); 1600 begin 1601 case ADrawMode of 1602 dmSet: SetHorizLine(x,y,x2,c); 1603 dmSetExceptTransparent: if c.alpha = 255 then SetHorizLine(x,y,x2,c); 1604 dmXor: XorHorizLine(x,y,x2,c); 1605 dmLinearBlend: FastBlendHorizLine(x,y,x2,c); 1606 dmDrawWithTransparency: DrawHorizLine(x,y,x2,c); 1607 end; 1608 end; 1609 1610 procedure TBGRACustomBitmap.VertLine(x, y, y2: Int32or64; c: TBGRAPixel; 1611 ADrawMode: TDrawMode); 1612 begin 1613 case ADrawMode of 1614 dmSet: SetVertLine(x,y,y2,c); 1615 dmSetExceptTransparent: if c.alpha = 255 then SetVertLine(x,y,y2,c); 1616 dmXor: XorVertLine(x,y,y2,c); 1617 dmLinearBlend: FastBlendVertLine(x,y,y2,c); 1618 dmDrawWithTransparency: DrawVertLine(x,y,y2,c); 1619 end; 1620 end; 1621 1622 procedure TBGRACustomBitmap.ArrowStartAsNone; 1623 begin 1624 SetArrowStart(asNone); 1625 end; 1626 1627 procedure TBGRACustomBitmap.ArrowStartAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single); 1628 var join: TPenJoinStyle; 1629 begin 1630 if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter; 1631 if ACut then 1632 begin 1633 if AFlipped then 1634 SetArrowStart(asFlippedCut,join,ARelativePenWidth) 1635 else 1636 SetArrowStart(asCut,join,ARelativePenWidth) 1637 end 1638 else 1639 begin 1640 if AFlipped then 1641 SetArrowStart(asFlipped,join,ARelativePenWidth) 1642 else 1643 SetArrowStart(asNormal,join,ARelativePenWidth) 1644 end; 1645 end; 1646 1647 procedure TBGRACustomBitmap.ArrowStartAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean; 1648 AHollowPenWidth: single); 1649 var join: TPenJoinStyle; 1650 begin 1651 if ARounded then join := pjsRound else join := pjsMiter; 1652 if AHollow then 1653 SetArrowStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset) 1654 else 1655 SetArrowStart(asTriangle, join,1,ABackOffset); 1656 end; 1657 1658 procedure TBGRACustomBitmap.ArrowStartAsTail; 1659 begin 1660 SetArrowStart(asTail); 1661 end; 1662 1663 procedure TBGRACustomBitmap.ArrowEndAsNone; 1664 begin 1665 SetArrowEnd(asNone); 1666 end; 1667 1668 procedure TBGRACustomBitmap.ArrowEndAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single); 1669 var join: TPenJoinStyle; 1670 begin 1671 if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter; 1672 if ACut then 1673 begin 1674 if AFlipped then 1675 SetArrowEnd(asFlippedCut,join,ARelativePenWidth) 1676 else 1677 SetArrowEnd(asCut,join,ARelativePenWidth) 1678 end 1679 else 1680 begin 1681 if AFlipped then 1682 SetArrowEnd(asFlipped,join,ARelativePenWidth) 1683 else 1684 SetArrowEnd(asNormal,join,ARelativePenWidth) 1685 end; 1686 end; 1687 1688 procedure TBGRACustomBitmap.ArrowEndAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean; 1689 AHollowPenWidth: single); 1690 var join: TPenJoinStyle; 1691 begin 1692 if ARounded then join := pjsRound else join := pjsMiter; 1693 if AHollow then 1694 SetArrowEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset) 1695 else 1696 SetArrowEnd(asTriangle, join,1, ABackOffset); 1697 end; 1698 1699 procedure TBGRACustomBitmap.ArrowEndAsTail; 1700 begin 1701 SetArrowEnd(asTail); 1702 end; 1703 1704 procedure TBGRACustomBitmap.DrawPolyLine(const points: array of TPoint; 1705 c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode); 1706 var i: integer; 1707 begin 1708 if length(points) = 1 then 1709 begin 1710 if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c,ADrawMode); 1711 end 1712 else 1713 for i := 0 to high(points)-1 do 1714 DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1),ADrawMode); 1715 end; 1716 1717 { Pixel polylines are constructed by concatenation } 1718 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint; 1719 c: TBGRAPixel; DrawLastPixel: boolean); 1720 var i: integer; 1721 begin 1722 if length(points) = 1 then 1723 begin 1724 if DrawLastPixel then DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true); 1725 end 1726 else 1727 for i := 0 to high(points)-1 do 1728 DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1)); 1729 end; 1730 1731 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint; c1, 1732 c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); 1733 var i: integer; 1734 DashPos: integer; 1735 begin 1736 DashPos := 0; 1737 if length(points) = 1 then 1738 begin 1739 if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c1); 1740 end 1741 else 1742 for i := 0 to high(points)-1 do 1743 DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c1,c2,dashLen,DrawLastPixel and (i=high(points)-1),DashPos); 1744 end; 1745 1746 procedure TBGRACustomBitmap.DrawPolygon(const points: array of TPoint; 1747 c: TBGRAPixel; ADrawMode: TDrawMode); 1748 var i: integer; 1749 begin 1750 if length(points) = 1 then 1751 begin 1752 DrawPixel(points[0].x,points[0].y,c,ADrawMode); 1753 end 1754 else 1755 begin 1756 for i := 0 to high(points)-1 do 1757 DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false,ADrawMode); 1758 DrawLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false,ADrawMode); 1759 end; 1760 end; 1761 1762 procedure TBGRACustomBitmap.DrawPolygonAntialias(const points: array of TPoint; 1763 c: TBGRAPixel); 1764 var i: integer; 1765 begin 1766 if length(points) = 1 then 1767 begin 1768 DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true); 1769 end 1770 else 1771 begin 1772 for i := 0 to high(points)-1 do 1773 DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false); 1774 DrawLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false); 1775 end; 1776 end; 1777 1778 procedure TBGRACustomBitmap.ErasePolyLine(const points: array of TPoint; alpha: byte; 1779 DrawLastPixel: boolean); 1780 var i: integer; 1781 begin 1782 if length(points) = 1 then 1783 begin 1784 if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha); 1785 end 1786 else 1787 for i := 0 to high(points)-1 do 1788 EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1)); 1789 end; 1790 1791 procedure TBGRACustomBitmap.ErasePolyLineAntialias( 1792 const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); 1793 var i: integer; 1794 begin 1795 if length(points) = 1 then 1796 begin 1797 if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha); 1798 end 1799 else 1800 for i := 0 to high(points)-1 do 1801 EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1)); 1802 end; 1803 1804 procedure TBGRACustomBitmap.ErasePolygonOutline(const points: array of TPoint; 1805 alpha: byte); 1806 var i: integer; 1807 begin 1808 if length(points) = 1 then 1809 begin 1810 ErasePixel(points[0].x,points[0].y,alpha); 1811 end 1812 else 1813 begin 1814 for i := 0 to high(points)-1 do 1815 EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false); 1816 EraseLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false); 1817 end; 1818 end; 1819 1820 procedure TBGRACustomBitmap.ErasePolygonOutlineAntialias( 1821 const points: array of TPoint; alpha: byte); 1822 var i: integer; 1823 begin 1824 if length(points) = 1 then 1825 begin 1826 ErasePixel(points[0].x,points[0].y,alpha); 1827 end 1828 else 1829 begin 1830 for i := 0 to high(points)-1 do 1831 EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false); 1832 EraseLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false); 1833 end; 1834 end; 1835 1836 { Following functions are defined for convenience } 1837 procedure TBGRACustomBitmap.Rectangle(x, y, x2, y2: integer; c: TColor); 1838 begin 1839 Rectangle(x, y, x2, y2, ColorToBGRA(c), dmSet); 1840 end; 1841 1842 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode 1843 ); 1844 begin 1845 Rectangle(r.left, r.top, r.right, r.bottom, c, mode); 1846 end; 1847 1848 procedure TBGRACustomBitmap.Rectangle(r: TRect; BorderColor, 1849 FillColor: TBGRAPixel; mode: TDrawMode); 1850 begin 1851 Rectangle(r.left, r.top, r.right, r.bottom, BorderColor, FillColor, mode); 1852 end; 1853 1854 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TColor); 1855 begin 1856 Rectangle(r.left, r.top, r.right, r.bottom, c); 1857 end; 1858 1859 procedure TBGRACustomBitmap.RectangleAntialias(x, y, x2, y2: single; 1860 c: TBGRAPixel; w: single); 1861 begin 1862 RectangleAntialias(x, y, x2, y2, c, w, BGRAPixelTransparent); 1863 end; 1864 1865 procedure TBGRACustomBitmap.FillRoundRect(X1, Y1, X2, Y2: integer; DX, 1866 DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode); 1867 begin 1868 RoundRect(X1,Y1,X2,Y2,DX,DY,FillColor,FillColor,ADrawMode); 1869 end; 1870 1871 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor: TBGRAPixel; 1872 ADrawMode: TDrawMode); 1873 begin 1874 RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,ADrawMode); 1875 end; 1876 1877 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor, 1878 FillColor: TBGRAPixel; ADrawMode: TDrawMode); 1879 begin 1880 RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,FillColor,ADrawMode); 1881 end; 1882 1883 procedure TBGRACustomBitmap.FillEllipseInRect(r: TRect; FillColor: TBGRAPixel; 1884 ADrawMode: TDrawMode); 1885 begin 1886 FillRoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),FillColor,ADrawMode); 1887 end; 1888 1889 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TColor); 1890 begin 1891 FillRect(r.Left, r.top, r.right, r.bottom, c); 1892 end; 1893 1894 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); 1895 begin 1896 FillRect(r.Left, r.top, r.right, r.bottom, c, mode); 1897 end; 1898 1899 procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner; 1900 mode: TDrawMode); 1901 begin 1902 FillRect(r.Left, r.top, r.right, r.bottom, texture, mode); 1903 end; 1904 1905 procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer; c: TColor); 1906 begin 1907 FillRect(x, y, x2, y2, ColorToBGRA(c), dmSet); 1908 end; 1909 1910 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text. 1911 The value of FontOrientation is taken into account, so that the text may be rotated. } 1912 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TBGRAPixel); 1913 begin 1914 TextOut(x, y, sUTF8, c, taLeftJustify); 1915 end; 1916 1917 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text. 1918 The value of FontOrientation is taken into account, so that the text may be rotated. } 1919 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TColor); 1920 begin 1921 TextOut(x, y, sUTF8, ColorToBGRA(c)); 1922 end; 1923 1924 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The texture is used to fill the text. 1925 The value of FontOrientation is taken into account, so that the text may be rotated. } 1926 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; 1927 texture: IBGRAScanner); 1928 begin 1929 TextOut(x, y, sUTF8, texture, taLeftJustify); 1930 end; 1931 1932 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary. 1933 The position depends on the specified horizontal alignment halign and vertical alignement valign. 1934 The color c is used to fill the text. No rotation is applied. } 1935 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string; 1936 halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); 1937 var 1938 style: TTextStyle; 1939 begin 1940 {$hints off} 1941 FillChar(style,sizeof(style),0); 1942 {$hints on} 1943 style.Alignment := halign; 1944 style.Layout := valign; 1945 style.Wordbreak := true; 1946 style.ShowPrefix := false; 1947 style.Clipping := false; 1948 TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,c); 1949 end; 1950 1951 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary. 1952 The position depends on the specified horizontal alignment halign and vertical alignement valign. 1953 The texture is used to fill the text. No rotation is applied. } 1954 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string; 1955 halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); 1956 var 1957 style: TTextStyle; 1958 begin 1959 {$hints off} 1960 FillChar(style,sizeof(style),0); 1961 {$hints on} 1962 style.Alignment := halign; 1963 style.Layout := valign; 1964 style.Wordbreak := true; 1965 style.ShowPrefix := false; 1966 style.Clipping := false; 1967 TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,texture); 1968 end; 1969 1970 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry: single): ArrayOfTPointF; 1971 begin 1972 result := ComputeEllipseContour(x,y,rx,ry); 1973 end; 1974 1975 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry, w: single 1976 ): ArrayOfTPointF; 1977 begin 1978 result := ComputeEllipseBorder(x,y,rx,ry,w); 1979 end; 1980 1981 procedure TBGRACustomBitmap.FillTransparent; 1982 begin 1983 Fill(BGRAPixelTransparent); 1984 end; 1985 1986 procedure TBGRACustomBitmap.Fill(c: TColor); 1987 begin 1988 Fill(ColorToBGRA(c)); 1989 end; 1990 1991 procedure TBGRACustomBitmap.Fill(c: TBGRAPixel); 1992 begin 1993 Fill(c, 0, NbPixels); 1994 end; 1995 1996 procedure TBGRACustomBitmap.AlphaFill(alpha: byte); 1997 begin 1998 AlphaFill(alpha, 0, NbPixels); 1999 end; 2000 2001 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap; 2002 color: TBGRAPixel); 2003 begin 2004 FillMask(x,y, AMask, color, dmDrawWithTransparency); 2005 end; 2006 2007 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap; 2008 texture: IBGRAScanner); 2009 begin 2010 FillMask(x,y, AMask, texture, dmDrawWithTransparency); 2011 end; 2012 2013 procedure TBGRACustomBitmap.FloodFill(X, Y: integer; Color: TBGRAPixel; 2014 mode: TFloodfillMode; Tolerance: byte); 2015 begin 2016 ParallelFloodFill(X,Y,Self,Color,mode,Tolerance); 2017 end; 2018 2019 procedure TBGRACustomBitmap.DrawPart(ARect: TRect; Canvas: TCanvas; x, 2020 y: integer; Opaque: boolean); 2021 var 2022 partial: TBGRACustomBitmap; 2023 begin 2024 partial := GetPart(ARect); 2025 if partial <> nil then 2026 begin 2027 partial.Draw(Canvas, x, y, Opaque); 2028 partial.Free; 2029 end; 2030 end; 2031 2032 procedure TBGRACustomBitmap.PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap); 2033 begin 2034 PutImageAngle(x,y,source,0); 2035 end; 2036 2037 procedure TBGRACustomBitmap.PutImagePart(x, y: integer; 2038 Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte); 2039 var w,h,sourcex,sourcey,nx,ny,xb,yb,destx,desty: integer; 2040 oldClip,newClip: TRect; 2041 begin 2042 if (Source = nil) or (AOpacity = 0) then exit; 2043 w := SourceRect.Right-SourceRect.Left; 2044 h := SourceRect.Bottom-SourceRect.Top; 2045 if (w <= 0) or (h <= 0) or (Source.Width = 0) or (Source.Height = 0) then exit; 2046 sourcex := PositiveMod(SourceRect.Left, Source.Width); 2047 sourcey := PositiveMod(SourceRect.Top, Source.Height); 2048 nx := (sourceX+w + Source.Width-1) div Source.Width; 2049 ny := (sourceY+h + Source.Height-1) div Source.Height; 2050 2051 oldClip := ClipRect; 2052 newClip := rect(x,y,x+w,y+h); 2053 if not IntersectRect(newClip,newClip,oldClip) then exit; 2054 2055 ClipRect := newClip; 2056 2057 desty := y-sourcey; 2058 for yb := 0 to ny-1 do 2059 begin 2060 destx := x-sourcex; 2061 for xb := 0 to nx-1 do 2062 begin 2063 self.PutImage(destx,desty,Source,mode,AOpacity); 2064 inc(destx,Source.Width); 2065 end; 2066 inc(desty,Source.Height); 2067 end; 2068 2069 ClipRect := oldClip; 2070 end; 2071 2072 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF; 2073 Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean); 2074 begin 2075 if ACorrectBlur then 2076 PutImageAffine(Origin,HAxis,VAxis,Source,rfCosine,AOpacity) 2077 else 2078 PutImageAffine(Origin,HAxis,VAxis,Source,rfLinear,AOpacity); 2079 end; 2080 2081 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF; 2082 Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte); 2083 var outputBounds: TRect; 2084 begin 2085 if (Source = nil) or (AOpacity = 0) then exit; 2086 if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and 2087 (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and 2088 (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then 2089 begin 2090 PutImage(round(origin.x),round(origin.y),Source,dmDrawWithTransparency,AOpacity); 2091 exit; 2092 end; 2093 outputBounds := GetImageAffineBounds(Origin,HAxis,VAxis,Source); 2094 PutImageAffine(Origin,HAxis,VAxis,Source,outputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity); 2095 end; 2096 2097 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF; 2098 Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte; 2099 ACorrectBlur: Boolean); 2100 begin 2101 if ACorrectBlur then 2102 PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfCosine,dmDrawWithTransparency, AOpacity) 2103 else 2104 PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfLinear,dmDrawWithTransparency,AOpacity); 2105 end; 2106 2107 { Returns the area that contains the affine transformed image } 2108 function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF; 2109 Source: TBGRACustomBitmap): TRect; 2110 var minx,miny,maxx,maxy: integer; 2111 vx,vy,pt1: TPointF; 2112 sourceBounds: TRect; 2113 2114 //include specified point in the bounds 2115 procedure Include(pt: TPointF); 2116 begin 2117 if floor(pt.X) < minx then minx := floor(pt.X); 2118 if floor(pt.Y) < miny then miny := floor(pt.Y); 2119 if ceil(pt.X) > maxx then maxx := ceil(pt.X); 2120 if ceil(pt.Y) > maxy then maxy := ceil(pt.Y); 2121 end; 2122 2123 begin 2124 result := EmptyRect; 2125 if (Source = nil) then exit; 2126 sourceBounds := source.GetImageBounds; 2127 if IsRectEmpty(sourceBounds) then exit; 2128 2129 if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and 2130 (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and 2131 (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then 2132 begin 2133 result := sourceBounds; 2134 OffsetRect(result,round(origin.x),round(origin.y)); 2135 IntersectRect(result,result,ClipRect); 2136 exit; 2137 end; 2138 2139 { Compute bounds } 2140 vx := (HAxis-Origin)*(1/source.Width); 2141 vy := (VAxis-Origin)*(1/source.Height); 2142 pt1 := Origin+vx*sourceBounds.Left+vy*sourceBounds.Top; 2143 minx := floor(pt1.X); 2144 miny := floor(pt1.Y); 2145 maxx := ceil(pt1.X); 2146 maxy := ceil(pt1.Y); 2147 Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Top); 2148 Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Bottom); 2149 Include(Origin+vx*sourceBounds.Left+vy*sourceBounds.Bottom); 2150 2151 result := rect(minx,miny,maxx+1,maxy+1); 2152 IntersectRect(result,result,ClipRect); 2153 end; 2154 2155 procedure TBGRACustomBitmap.PutImageAngle(x, y: single; 2156 Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; 2157 imageCenterX: single; imageCenterY: single; AOpacity: Byte; 2158 ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean); 2159 begin 2160 if ACorrectBlur then 2161 PutImageAngle(x,y,Source,angle,AOutputBounds,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation) 2162 else 2163 PutImageAngle(x,y,Source,angle,AOutputBounds,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation); 2164 end; 2165 2166 procedure TBGRACustomBitmap.PutImageAngle(x, y: single; 2167 Source: TBGRACustomBitmap; angle: single; imageCenterX: single; 2168 imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean); 2169 begin 2170 if ACorrectBlur then 2171 PutImageAngle(x,y,Source,angle,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation) 2172 else 2173 PutImageAngle(x,y,Source,angle,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation); 2174 end; 2175 2176 procedure TBGRACustomBitmap.PutImageAngle(x, y: single; 2177 Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; 2178 AResampleFilter: TResampleFilter; imageCenterX: single; imageCenterY: single; AOpacity: Byte; 2179 ARestoreOffsetAfterRotation: boolean); 2180 var 2181 Origin,HAxis,VAxis: TPointF; 2182 begin 2183 if (source = nil) or (AOpacity=0) then exit; 2184 ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation, 2185 Origin,HAxis,VAxis); 2186 PutImageAffine(Origin,HAxis,VAxis,source,AOutputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity); 2187 end; 2188 2189 procedure TBGRACustomBitmap.PutImageAngle(x, y: single; 2190 Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter; 2191 imageCenterX: single; imageCenterY: single; AOpacity: Byte; 2192 ARestoreOffsetAfterRotation: boolean); 2193 var 2194 Origin,HAxis,VAxis: TPointF; 2195 begin 2196 if (source = nil) or (AOpacity=0) then exit; 2197 ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation, 2198 Origin,HAxis,VAxis); 2199 PutImageAffine(Origin,HAxis,VAxis,source,AResampleFilter,AOpacity); 2200 end; 2201 2202 procedure TBGRACustomBitmap.ComputeImageAngleAxes(x, y, w, h, 2203 angle: single; imageCenterX, imageCenterY: single; 2204 ARestoreOffsetAfterRotation: boolean; out Origin, HAxis, VAxis: TPointF); 2205 var 2206 cosa,sina: single; 2207 2208 { Compute rotated coordinates } 2209 function Coord(relX,relY: single): TPointF; 2210 begin 2211 relX -= imageCenterX; 2212 relY -= imageCenterY; 2213 result.x := relX*cosa-relY*sina+x; 2214 result.y := relY*cosa+relX*sina+y; 2215 if ARestoreOffsetAfterRotation then 2216 begin 2217 result.x += imageCenterX; 2218 result.y += imageCenterY; 2219 end; 2220 end; 2221 2222 begin 2223 cosa := cos(-angle*Pi/180); 2224 sina := -sin(-angle*Pi/180); 2225 Origin := Coord(0,0); 2226 HAxis := Coord(w,0); 2227 VAxis := Coord(0,h); 2228 end; 2229 2230 function TBGRACustomBitmap.GetImageAngleBounds(x, y: single; 2231 Source: TBGRACustomBitmap; angle: single; imageCenterX: single; 2232 imageCenterY: single; ARestoreOffsetAfterRotation: boolean): TRect; 2233 var 2234 cosa,sina: single; 2235 2236 { Compute rotated coordinates } 2237 function Coord(relX,relY: single): TPointF; 2238 begin 2239 relX -= imageCenterX; 2240 relY -= imageCenterY; 2241 result.x := relX*cosa-relY*sina+x; 2242 result.y := relY*cosa+relX*sina+y; 2243 if ARestoreOffsetAfterRotation then 2244 begin 2245 result.x += imageCenterX; 2246 result.y += imageCenterY; 2247 end; 2248 end; 2249 2250 begin 2251 if (source = nil) then 2252 begin 2253 result := EmptyRect; 2254 exit; 2255 end; 2256 cosa := cos(-angle*Pi/180); 2257 sina := -sin(-angle*Pi/180); 2258 result := GetImageAffineBounds(Coord(0,0),Coord(source.Width,0),Coord(0,source.Height),source); 2259 end; 2260 2261 procedure TBGRACustomBitmap.VerticalFlip; 2262 begin 2263 VerticalFlip(rect(0,0,Width,Height)); 2264 end; 2265 2266 procedure TBGRACustomBitmap.HorizontalFlip; 2267 begin 2268 HorizontalFlip(rect(0,0,Width,Height)); 2269 end; 2270 2271 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap); 2272 begin 2273 ApplyMask(mask, Rect(0,0,Width,Height), Point(0,0)); 2274 end; 2275 2276 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect); 2277 begin 2278 ApplyMask(mask, ARect, ARect.TopLeft); 2279 end; 2280 2281 { Interface gateway } 2282 function TBGRACustomBitmap.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2283 begin 2284 if GetInterface(iid, obj) then 2285 Result := S_OK 2286 else 2287 Result := longint(E_NOINTERFACE); 2288 end; 2289 2290 { There is no automatic reference counting, but it is compulsory to define these functions } 2291 function TBGRACustomBitmap._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2292 begin 2293 result := 0; 2294 end; 2295 2296 function TBGRACustomBitmap._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2297 begin 2298 result := 0; 2299 end; 2300 2301 {$hints off} 2302 procedure TBGRACustomBitmap.ScanPutPixels(pdest: PBGRAPixel; count: integer; 2303 mode: TDrawMode); 2304 begin 2305 //do nothing 2306 end; 2307 {$hints on} 2308 2309 function TBGRACustomBitmap.IsScanPutPixelsDefined: boolean; 2310 begin 2311 result := False; 2312 end; 2313 2314 {********************** End of TBGRACustomBitmap **************************} 2315 2316 { TBGRACustomScanner } 2317 { The abstract class record the position so that a derived class 2318 need only to redefine ScanAt } 2319 2320 function TBGRACustomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel; 2321 begin 2322 result := ScanAt(X,Y); 2323 end; 2324 2325 procedure TBGRACustomScanner.ScanMoveTo(X, Y: Integer); 2326 begin 2327 FCurX := X; 2328 FCurY := Y; 2329 end; 2330 2331 { Call ScanAt to determine pixel value } 2332 function TBGRACustomScanner.ScanNextPixel: TBGRAPixel; 2333 begin 2334 result := ScanAt(FCurX,FCurY); 2335 Inc(FCurX); 2336 end; 2337 2338 {$hints off} 2339 procedure TBGRACustomScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer; 2340 mode: TDrawMode); 2341 begin 2342 //do nothing 2343 end; 2344 {$hints on} 2345 2346 function TBGRACustomScanner.IsScanPutPixelsDefined: boolean; 2347 begin 2348 result := false; 2349 end; 2350 2351 { Interface gateway } 2352 function TBGRACustomScanner.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2353 begin 2354 if GetInterface(iid, obj) then 2355 Result := S_OK 2356 else 2357 Result := longint(E_NOINTERFACE); 2358 end; 2359 2360 { There is no automatic reference counting, but it is compulsory to define these functions } 2361 function TBGRACustomScanner._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2362 begin 2363 result := 0; 2364 end; 2365 2366 function TBGRACustomScanner._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; 2367 begin 2368 result := 0; 2369 end; 2370 2371 {********************** End of TBGRACustomScanner **************************} 2372 2373 { The gamma correction is approximated here by a power function } 2374 const 2375 GammaExpFactor = 1.7; //exponent 2376 redWeightShl10 = 306; // = 0.299 2377 greenWeightShl10 = 601; // = 0.587 2378 blueWeightShl10 = 117; // = 0.114 2379 2380 var 2381 GammaLinearFactor: single; 2382 2383 procedure InitGamma; 2384 var 2385 i: integer; 2386 {$IFDEF WINCE} 2387 j,prevpos,curpos,midpos: integer; 2388 {$ENDIF} 2389 begin 2390 //the linear factor is used to normalize expanded values in the range 0..65535 2391 GammaLinearFactor := 65535 / power(255, GammaExpFactor); 2392 2393 {$IFDEF WINCE} 2394 curpos := 0; 2395 GammaExpansionTab[0] := 0; 2396 GammaCompressionTab[0] := 0; 2397 for i := 0 to 255 do 2398 begin 2399 prevpos := curpos; 2400 curpos := round(power(i, GammaExpFactor) * GammaLinearFactor); 2401 if i = 1 then curpos := 1; //to avoid information loss 2402 GammaExpansionTab[i] := curpos; 2403 midpos := (prevpos+1+curpos) div 2; 2404 for j := prevpos+1 to midpos-1 do 2405 GammaCompressionTab[j] := i-1; 2406 for j := midpos to curpos do 2407 GammaCompressionTab[j] := i; 2408 end; 2409 {$ELSE} 2410 for i := 0 to 255 do 2411 GammaExpansionTab[i] := round(power(i, GammaExpFactor) * GammaLinearFactor); 2412 2413 for i := 0 to 65535 do 2414 GammaCompressionTab[i] := round(power(i / GammaLinearFactor, 1 / GammaExpFactor)); 2415 2416 GammaExpansionTab[1] := 1; //to avoid information loss 2417 GammaCompressionTab[1] := 1; 2418 {$ENDIF} 2419 end; 2420 2421 {************************** Color functions **************************} 570 begin {optional implementation} end; 571 2422 572 2423 573 function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, … … 2460 610 2461 611 result := true; 2462 end;2463 2464 { The intensity is defined here as the maximum value of any color component }2465 function GetIntensity(const c: TExpandedPixel): word; inline;2466 begin2467 Result := c.red;2468 if c.green > Result then2469 Result := c.green;2470 if c.blue > Result then2471 Result := c.blue;2472 end;2473 2474 function GetIntensity(c: TBGRAPixel): word;2475 begin2476 Result := c.red;2477 if c.green > Result then2478 Result := c.green;2479 if c.blue > Result then2480 Result := c.blue;2481 result := GammaExpansionTab[Result];2482 end;2483 2484 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;2485 var2486 curIntensity: word;2487 begin2488 curIntensity := GetIntensity(c);2489 if curIntensity = 0 then //suppose it's gray if there is no color information2490 begin2491 Result.red := intensity;2492 Result.green := intensity;2493 Result.blue := intensity;2494 result.alpha := c.alpha;2495 end2496 else2497 begin2498 //linear interpolation to reached wanted intensity2499 Result.red := (c.red * intensity + (curIntensity shr 1)) div curIntensity;2500 Result.green := (c.green * intensity + (curIntensity shr 1)) div curIntensity;2501 Result.blue := (c.blue * intensity + (curIntensity shr 1)) div curIntensity;2502 Result.alpha := c.alpha;2503 end;2504 end;2505 2506 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;2507 begin2508 result := GammaCompression(SetIntensity(GammaExpansion(c),intensity));2509 end;2510 2511 function GetLightness(c: TBGRAPixel): word;2512 begin2513 result := GetLightness(GammaExpansion(c));2514 end;2515 2516 { The lightness here is defined as the subjective sensation of luminosity, where2517 blue is the darkest component and green the lightest }2518 function GetLightness(const c: TExpandedPixel): word; inline;2519 begin2520 Result := (c.red * redWeightShl10 + c.green * greenWeightShl10 +2521 c.blue * blueWeightShl10 + 512) shr 10;2522 end;2523 2524 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;2525 var2526 curLightness: word;2527 begin2528 curLightness := GetLightness(c);2529 if lightness = curLightness then2530 begin //no change2531 Result := c;2532 exit;2533 end;2534 result := SetLightness(c, lightness, curLightness);2535 end;2536 2537 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;2538 begin2539 result := GammaCompression(SetLightness(GammaExpansion(c),lightness));2540 end;2541 2542 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel;2543 var2544 AddedWhiteness, maxBeforeWhite: word;2545 clip: boolean;2546 begin2547 if lightness = curLightness then2548 begin //no change2549 Result := c;2550 exit;2551 end;2552 if lightness = 65535 then //set to white2553 begin2554 Result.red := 65535;2555 Result.green := 65535;2556 Result.blue := 65535;2557 Result.alpha := c.alpha;2558 exit;2559 end;2560 if lightness = 0 then //set to black2561 begin2562 Result.red := 0;2563 Result.green := 0;2564 Result.blue := 0;2565 Result.alpha := c.alpha;2566 exit;2567 end;2568 if curLightness = 0 then //set from black2569 begin2570 Result.red := lightness;2571 Result.green := lightness;2572 Result.blue := lightness;2573 Result.alpha := c.alpha;2574 exit;2575 end;2576 if lightness < curLightness then //darker is easy2577 begin2578 result.alpha:= c.alpha;2579 result.red := (c.red * lightness + (curLightness shr 1)) div curLightness;2580 result.green := (c.green * lightness + (curLightness shr 1)) div curLightness;2581 result.blue := (c.blue * lightness + (curLightness shr 1)) div curLightness;2582 exit;2583 end;2584 //lighter and grayer2585 Result := c;2586 AddedWhiteness := lightness - curLightness;2587 maxBeforeWhite := 65535 - AddedWhiteness;2588 clip := False;2589 if Result.red <= maxBeforeWhite then2590 Inc(Result.red, AddedWhiteness)2591 else2592 begin2593 Result.red := 65535;2594 clip := True;2595 end;2596 if Result.green <= maxBeforeWhite then2597 Inc(Result.green, AddedWhiteness)2598 else2599 begin2600 Result.green := 65535;2601 clip := True;2602 end;2603 if Result.blue <= maxBeforeWhite then2604 Inc(Result.blue, AddedWhiteness)2605 else2606 begin2607 Result.blue := 65535;2608 clip := True;2609 end;2610 2611 if clip then //light and whiter2612 begin2613 curLightness := GetLightness(Result);2614 addedWhiteness := lightness - curLightness;2615 maxBeforeWhite := 65535 - curlightness;2616 Result.red := Result.red + addedWhiteness * (65535 - Result.red) div2617 maxBeforeWhite;2618 Result.green := Result.green + addedWhiteness * (65535 - Result.green) div2619 maxBeforeWhite;2620 Result.blue := Result.blue + addedWhiteness * (65535 - Result.blue) div2621 maxBeforeWhite;2622 end;2623 end;2624 2625 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel;2626 var2627 r,g,b: word;2628 lightness256: byte;2629 begin2630 if lightness <= 32768 then2631 begin2632 if lightness = 32768 then2633 result := color else2634 begin2635 lightness256 := GammaCompressionTab[lightness shl 1];2636 result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,2637 color.blue * lightness256 shr 8, color.alpha);2638 end;2639 end else2640 begin2641 if lightness = 65535 then2642 result := BGRA(255,255,255,color.alpha) else2643 begin2644 lightness -= 32767;2645 r := GammaExpansionTab[color.red];2646 g := GammaExpansionTab[color.green];2647 b := GammaExpansionTab[color.blue];2648 result := BGRA(GammaCompressionTab[ r + (not r)*lightness shr 15 ],2649 GammaCompressionTab[ g + (not g)*lightness shr 15 ],2650 GammaCompressionTab[ b + (not b)*lightness shr 15 ],2651 color.alpha);2652 end;2653 end;2654 end;2655 2656 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;2657 {$ifdef CPUI386} {$asmmode intel} assembler;2658 asm2659 imul edx2660 shl edx, 172661 shr eax, 152662 or edx, eax2663 mov result, edx2664 end;2665 {$ELSE}2666 begin2667 result := int64(lightness1)*lightness2 shr 15;2668 end;2669 {$ENDIF}2670 2671 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;2672 var2673 maxValue,invMaxValue,r,g,b: longword;2674 lightness256: byte;2675 begin2676 if lightness <= 32768 then2677 begin2678 if lightness = 32768 then2679 result := color else2680 begin2681 lightness256 := GammaCompressionTab[lightness shl 1];2682 result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,2683 color.blue * lightness256 shr 8, color.alpha);2684 end;2685 end else2686 begin2687 r := CombineLightness(GammaExpansionTab[color.red], lightness);2688 g := CombineLightness(GammaExpansionTab[color.green], lightness);2689 b := CombineLightness(GammaExpansionTab[color.blue], lightness);2690 maxValue := r;2691 if g > maxValue then maxValue := g;2692 if b > maxValue then maxValue := b;2693 if maxValue <= 65535 then2694 result := BGRA(GammaCompressionTab[r],2695 GammaCompressionTab[g],2696 GammaCompressionTab[b],2697 color.alpha)2698 else2699 begin2700 invMaxValue := (longword(2147483647)+longword(maxValue-1)) div maxValue;2701 maxValue := (maxValue-65535) shr 1;2702 r := r*invMaxValue shr 15 + maxValue;2703 g := g*invMaxValue shr 15 + maxValue;2704 b := b*invMaxValue shr 15 + maxValue;2705 if r >= 65535 then result.red := 255 else2706 result.red := GammaCompressionTab[r];2707 if g >= 65535 then result.green := 255 else2708 result.green := GammaCompressionTab[g];2709 if b >= 65535 then result.blue := 255 else2710 result.blue := GammaCompressionTab[b];2711 result.alpha := color.alpha;2712 end;2713 end;2714 end;2715 2716 { Conversion from RGB value to HSL colorspace. See : http://en.wikipedia.org/wiki/HSL_color_space }2717 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;2718 begin2719 result := ExpandedToHSLA(GammaExpansion(c));2720 end;2721 2722 procedure ExpandedToHSLAInline(r,g,b: Int32Or64; var dest: THSLAPixel); inline;2723 const2724 deg60 = 10922;2725 deg120 = 21845;2726 deg240 = 43690;2727 var2728 min, max, minMax: Int32or64;2729 UMinMax,UTwiceLightness: UInt32or64;2730 begin2731 if g > r then2732 begin2733 max := g;2734 min := r;2735 end2736 else2737 begin2738 max := r;2739 min := g;2740 end;2741 if b > max then2742 max := b2743 else2744 if b < min then2745 min := b;2746 minMax := max - min;2747 2748 if minMax = 0 then2749 dest.hue := 02750 else2751 if max = r then2752 {$PUSH}{$RANGECHECKS OFF}2753 dest.hue := ((g - b) * deg60) div minMax2754 {$POP}2755 else2756 if max = g then2757 dest.hue := ((b - r) * deg60) div minMax + deg1202758 else2759 {max = b} dest.hue := ((r - g) * deg60) div minMax + deg240;2760 UTwiceLightness := max + min;2761 if min = max then2762 dest.saturation := 02763 else2764 begin2765 UMinMax:= minMax;2766 if UTwiceLightness < 65536 then2767 dest.saturation := (UMinMax shl 16) div (UTwiceLightness + 1)2768 else2769 dest.saturation := (UMinMax shl 16) div (131072 - UTwiceLightness);2770 end;2771 dest.lightness := UTwiceLightness shr 1;2772 end;2773 2774 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;2775 begin2776 result.alpha := ec.alpha;2777 ExpandedToHSLAInline(ec.red,ec.green,ec.blue,result);2778 end;2779 2780 function HtoG(hue: word): word;2781 const2782 segmentDest: array[0..5] of NativeUInt =2783 (13653, 10923, 8192, 13653, 10923, 8192);2784 segmentSrc: array[0..5] of NativeUInt =2785 (10923, 10922, 10923, 10923, 10922, 10923);2786 var2787 h,g: NativeUInt;2788 begin2789 h := hue;2790 if h < segmentSrc[0] then2791 g := h * segmentDest[0] div segmentSrc[0]2792 else2793 begin2794 g := segmentDest[0];2795 h -= segmentSrc[0];2796 if h < segmentSrc[1] then2797 g += h * segmentDest[1] div segmentSrc[1]2798 else2799 begin2800 g += segmentDest[1];2801 h -= segmentSrc[1];2802 if h < segmentSrc[2] then2803 g += h * segmentDest[2] div segmentSrc[2]2804 else2805 begin2806 g += segmentDest[2];2807 h -= segmentSrc[2];2808 if h < segmentSrc[3] then2809 g += h * segmentDest[3] div segmentSrc[3]2810 else2811 begin2812 g += segmentDest[3];2813 h -= segmentSrc[3];2814 if h < segmentSrc[4] then2815 g += h * segmentDest[4] div segmentSrc[4]2816 else2817 begin2818 g += segmentDest[4];2819 h -= segmentSrc[4];2820 g += h * segmentDest[5] div segmentSrc[5];2821 end;2822 end;2823 end;2824 end;2825 end;2826 result := g;2827 end;2828 2829 function GtoH(ghue: word): word;2830 const2831 segment: array[0..5] of NativeUInt =2832 (13653, 10923, 8192, 13653, 10923, 8192);2833 var g: NativeUint;2834 begin2835 g := ghue;2836 if g < segment[0] then2837 result := g * 10923 div segment[0]2838 else2839 begin2840 g -= segment[0];2841 if g < segment[1] then2842 result := g * (21845-10923) div segment[1] + 109232843 else2844 begin2845 g -= segment[1];2846 if g < segment[2] then2847 result := g * (32768-21845) div segment[2] + 218452848 else2849 begin2850 g -= segment[2];2851 if g < segment[3] then2852 result := g * (43691-32768) div segment[3] + 327682853 else2854 begin2855 g -= segment[3];2856 if g < segment[4] then2857 result := g * (54613-43691) div segment[4] + 436912858 else2859 begin2860 g -= segment[4];2861 result := g * (65536-54613) div segment[5] + 54613;2862 end;2863 end;2864 end;2865 end;2866 end;2867 end;2868 2869 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;2870 var lightness: UInt32Or64;2871 red,green,blue: Int32or64;2872 begin2873 red := GammaExpansionTab[c.red];2874 green := GammaExpansionTab[c.green];2875 blue := GammaExpansionTab[c.blue];2876 result.alpha := c.alpha shl 8 + c.alpha;2877 2878 lightness := (red * redWeightShl10 + green * greenWeightShl10 +2879 blue * blueWeightShl10 + 512) shr 10;2880 2881 ExpandedToHSLAInline(red,green,blue,result);2882 if result.lightness > 32768 then2883 result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;2884 result.lightness := lightness;2885 result.hue := HtoG(result.hue);2886 end;2887 2888 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;2889 var lightness: UInt32Or64;2890 red,green,blue: Int32or64;2891 begin2892 red := ec.red;2893 green := ec.green;2894 blue := ec.blue;2895 result.alpha := ec.alpha;2896 2897 lightness := (red * redWeightShl10 + green * greenWeightShl10 +2898 blue * blueWeightShl10 + 512) shr 10;2899 2900 ExpandedToHSLAInline(red,green,blue,result);2901 if result.lightness > 32768 then2902 result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;2903 result.lightness := lightness;2904 result.hue := HtoG(result.hue);2905 end;2906 2907 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;2908 const2909 deg30 = 4096;2910 deg60 = 8192;2911 deg120 = deg60 * 2;2912 deg180 = deg60 * 3;2913 deg240 = deg60 * 4;2914 deg360 = deg60 * 6;2915 2916 function ComputeColor(p, q: Int32or64; h: Int32or64): Int32or64; inline;2917 begin2918 if h < deg180 then2919 begin2920 if h < deg60 then2921 Result := p + ((q - p) * h + deg30) div deg602922 else2923 Result := q2924 end else2925 begin2926 if h < deg240 then2927 Result := p + ((q - p) * (deg240 - h) + deg30) div deg602928 else2929 Result := p;2930 end;2931 end;2932 2933 var2934 q, p, L, S, H: Int32or64;2935 begin2936 L := c.lightness;2937 S := c.saturation;2938 if S = 0 then //gray2939 begin2940 result.red := L;2941 result.green := L;2942 result.blue := L;2943 result.alpha := c.alpha;2944 exit;2945 end;2946 {$hints off}2947 if L < 32768 then2948 q := (L shr 1) * ((65535 + S) shr 1) shr 142949 else2950 q := L + S - ((L shr 1) *2951 (S shr 1) shr 14);2952 {$hints on}2953 if q > 65535 then q := 65535;2954 p := (L shl 1) - q;2955 if p > 65535 then p := 65535;2956 H := c.hue * deg360 shr 16;2957 result.green := ComputeColor(p, q, H);2958 inc(H, deg120);2959 if H > deg360 then Dec(H, deg360);2960 result.red := ComputeColor(p, q, H);2961 inc(H, deg120);2962 if H > deg360 then Dec(H, deg360);2963 result.blue := ComputeColor(p, q, H);2964 result.alpha := c.alpha;2965 end;2966 2967 { Conversion from HSL colorspace to RGB. See : http://en.wikipedia.org/wiki/HSL_color_space }2968 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;2969 var ec: TExpandedPixel;2970 begin2971 ec := HSLAToExpanded(c);2972 Result := GammaCompression(ec);2973 end;2974 2975 function HueDiff(h1, h2: word): word;2976 begin2977 result := abs(integer(h1)-integer(h2));2978 if result > 32768 then result := 65536-result;2979 end;2980 2981 function GetHue(ec: TExpandedPixel): word;2982 const2983 deg60 = 8192;2984 deg120 = deg60 * 2;2985 deg240 = deg60 * 4;2986 deg360 = deg60 * 6;2987 var2988 min, max, minMax: integer;2989 r,g,b: integer;2990 begin2991 r := ec.red;2992 g := ec.green;2993 b := ec.blue;2994 min := r;2995 max := r;2996 if g > max then2997 max := g2998 else2999 if g < min then3000 min := g;3001 if b > max then3002 max := b3003 else3004 if b < min then3005 min := b;3006 minMax := max - min;3007 3008 if minMax = 0 then3009 Result := 03010 else3011 if max = r then3012 Result := (((g - b) * deg60) div3013 minMax + deg360) mod deg3603014 else3015 if max = g then3016 Result := ((b - r) * deg60) div minMax + deg1203017 else3018 {max = b} Result :=3019 ((r - g) * deg60) div minMax + deg240;3020 3021 Result := (Result shl 16) div deg360; //normalize3022 end;3023 3024 function ColorImportance(ec: TExpandedPixel): word;3025 var min,max: word;3026 begin3027 min := ec.red;3028 max := ec.red;3029 if ec.green > max then3030 max := ec.green3031 else3032 if ec.green < min then3033 min := ec.green;3034 if ec.blue > max then3035 max := ec.blue3036 else3037 if ec.blue < min then3038 min := ec.blue;3039 result := max - min;3040 end;3041 3042 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;3043 var ec: TExpandedPixel;3044 lightness: word;3045 begin3046 c.hue := GtoH(c.hue);3047 lightness := c.lightness;3048 c.lightness := 32768;3049 ec := HSLAToExpanded(c);3050 result := GammaCompression(SetLightness(ec, lightness));3051 end;3052 3053 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;3054 var lightness: word;3055 begin3056 c.hue := GtoH(c.hue);3057 lightness := c.lightness;3058 c.lightness := 32768;3059 result := SetLightness(HSLAToExpanded(c),lightness);3060 end;3061 3062 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;3063 begin3064 result := BGRAToHSLA(GSBAToBGRA(c));3065 end;3066 3067 { Apply gamma correction using conversion tables }3068 function GammaExpansion(c: TBGRAPixel): TExpandedPixel;3069 begin3070 Result.red := GammaExpansionTab[c.red];3071 Result.green := GammaExpansionTab[c.green];3072 Result.blue := GammaExpansionTab[c.blue];3073 Result.alpha := c.alpha shl 8 + c.alpha;3074 end;3075 3076 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel;3077 begin3078 Result.red := GammaCompressionTab[ec.red];3079 Result.green := GammaCompressionTab[ec.green];3080 Result.blue := GammaCompressionTab[ec.blue];3081 Result.alpha := ec.alpha shr 8;3082 end;3083 3084 function GammaCompression(red, green, blue, alpha: word): TBGRAPixel;3085 begin3086 Result.red := GammaCompressionTab[red];3087 Result.green := GammaCompressionTab[green];3088 Result.blue := GammaCompressionTab[blue];3089 Result.alpha := alpha shr 8;3090 end;3091 3092 // Conversion to grayscale by taking into account3093 // different color weights3094 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;3095 var3096 ec: TExpandedPixel;3097 gray: word;3098 cgray: byte;3099 begin3100 if c.alpha = 0 then3101 begin3102 result := BGRAPixelTransparent;3103 exit;3104 end;3105 //gamma expansion3106 ec := GammaExpansion(c);3107 //gray composition3108 gray := (ec.red * redWeightShl10 + ec.green * greenWeightShl10 +3109 ec.blue * blueWeightShl10 + 512) shr 10;3110 //gamma compression3111 cgray := GammaCompressionTab[gray];3112 Result.red := cgray;3113 Result.green := cgray;3114 Result.blue := cgray;3115 Result.alpha := c.alpha;3116 end;3117 3118 function GrayscaleToBGRA(lightness: word): TBGRAPixel;3119 begin3120 result.red := GammaCompressionTab[lightness];3121 result.green := result.red;3122 result.blue := result.red;3123 result.alpha := $ff;3124 end;3125 3126 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel;3127 var3128 sumR,sumG,sumB,sumA: NativeUInt;3129 i: integer;3130 begin3131 if length(colors)<=0 then3132 begin3133 result := BGRAPixelTransparent;3134 exit;3135 end;3136 sumR := 0;3137 sumG := 0;3138 sumB := 0;3139 sumA := 0;3140 for i := 0 to high(colors) do3141 with colors[i] do3142 begin3143 sumR += red*alpha;3144 sumG += green*alpha;3145 sumB += blue*alpha;3146 sumA += alpha;3147 end;3148 if sumA > 0 then3149 begin3150 result.red := (sumR + sumA shr 1) div sumA;3151 result.green := (sumG + sumA shr 1) div sumA;3152 result.blue := (sumB + sumA shr 1) div sumA;3153 result.alpha := sumA div longword(length(colors));3154 end3155 else3156 result := BGRAPixelTransparent;3157 end;3158 3159 { Merge linearly two colors of same importance }3160 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel;3161 var c12: cardinal;3162 begin3163 if (c1.alpha = 0) then3164 Result := c23165 else3166 if (c2.alpha = 0) then3167 Result := c13168 else3169 begin3170 c12 := c1.alpha + c2.alpha;3171 Result.red := (c1.red * c1.alpha + c2.red * c2.alpha + c12 shr 1) div c12;3172 Result.green := (c1.green * c1.alpha + c2.green * c2.alpha + c12 shr 1) div c12;3173 Result.blue := (c1.blue * c1.alpha + c2.blue * c2.alpha + c12 shr 1) div c12;3174 Result.alpha := (c12 + 1) shr 1;3175 end;3176 end;3177 3178 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel;3179 weight2: integer): TBGRAPixel;3180 var3181 f1,f2,f12: int64;3182 begin3183 if (weight1 = 0) then3184 begin3185 if (weight2 = 0) then3186 result := BGRAPixelTransparent3187 else3188 Result := c23189 end3190 else3191 if (weight2 = 0) then3192 Result := c13193 else3194 if (weight1+weight2 = 0) then3195 Result := BGRAPixelTransparent3196 else3197 begin3198 f1 := int64(c1.alpha)*weight1;3199 f2 := int64(c2.alpha)*weight2;3200 f12 := f1+f2;3201 if f12 = 0 then3202 result := BGRAPixelTransparent3203 else3204 begin3205 Result.red := (c1.red * f1 + c2.red * f2 + f12 shr 1) div f12;3206 Result.green := (c1.green * f1 + c2.green * f2 + f12 shr 1) div f12;3207 Result.blue := (c1.blue * f1 + c2.blue * f2 + f12 shr 1) div f12;3208 {$hints off}3209 Result.alpha := (f12 + ((weight1+weight2) shr 1)) div (weight1+weight2);3210 {$hints on}3211 end;3212 end;3213 end;3214 3215 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel;3216 weight2: byte): TBGRAPixel;3217 var3218 w1,w2,f1,f2,f12,a: UInt32or64;3219 begin3220 w1 := weight1;3221 w2 := weight2;3222 if (w1 = 0) then3223 begin3224 if (w2 = 0) then3225 result := BGRAPixelTransparent3226 else3227 Result := c23228 end3229 else3230 if (w2 = 0) then3231 Result := c13232 else3233 begin3234 f1 := c1.alpha*w1;3235 f2 := c2.alpha*w2;3236 a := (f1+f2 + ((w1+w2) shr 1)) div (w1+w2);3237 if a = 0 then3238 begin3239 result := BGRAPixelTransparent;3240 exit;3241 end else3242 Result.alpha := a;3243 {$IFNDEF CPU64}3244 if (f1 >= 32768) or (f2 >= 32768) then3245 begin3246 f1 := f1 shr 1;3247 f2 := f2 shr 1;3248 end;3249 {$ENDIF}3250 f12 := f1+f2;3251 Result.red := GammaCompressionTab[(GammaExpansionTab[c1.red] * f1 + GammaExpansionTab[c2.red] * f2) div f12];3252 Result.green := GammaCompressionTab[(GammaExpansionTab[c1.green] * f1 + GammaExpansionTab[c2.green] * f2) div f12];3253 Result.blue := GammaCompressionTab[(GammaExpansionTab[c1.blue] * f1 + GammaExpansionTab[c2.blue] * f2) div f12];3254 end;3255 end;3256 3257 { Merge two colors of same importance }3258 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel;3259 var c12: cardinal;3260 begin3261 if (ec1.alpha = 0) then3262 Result := ec23263 else3264 if (ec2.alpha = 0) then3265 Result := ec13266 else3267 begin3268 c12 := ec1.alpha + ec2.alpha;3269 Result.red := (int64(ec1.red) * ec1.alpha + int64(ec2.red) * ec2.alpha + c12 shr 1) div c12;3270 Result.green := (int64(ec1.green) * ec1.alpha + int64(ec2.green) * ec2.alpha + c12 shr 1) div c12;3271 Result.blue := (int64(ec1.blue) * ec1.alpha + int64(ec2.blue) * ec2.alpha + c12 shr 1) div c12;3272 Result.alpha := (c12 + 1) shr 1;3273 end;3274 end;3275 3276 function BGRA(red, green, blue, alpha: byte): TBGRAPixel;3277 begin3278 Result.red := red;3279 Result.green := green;3280 Result.blue := blue;3281 Result.alpha := alpha;3282 end;3283 3284 function BGRA(red, green, blue: byte): TBGRAPixel; overload;3285 begin3286 Result.red := red;3287 Result.green := green;3288 Result.blue := blue;3289 Result.alpha := 255;3290 end;3291 3292 { Convert a TColor value to a TBGRAPixel value. Note that3293 you need to call ColorToRGB first if you use a system3294 color identifier like clWindow. }3295 {$PUSH}{$R-}3296 3297 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel;3298 begin3299 Result.hue := hue;3300 Result.saturation := saturation;3301 Result.lightness := lightness;3302 Result.alpha := alpha;3303 end;3304 3305 function HSLA(hue, saturation, lightness: word): THSLAPixel;3306 begin3307 Result.hue := hue;3308 Result.saturation := saturation;3309 Result.lightness := lightness;3310 Result.alpha := $ffff;3311 end;3312 3313 function ColorToBGRA(color: TColor): TBGRAPixel; overload;3314 begin3315 Result.red := color;3316 Result.green := color shr 8;3317 Result.blue := color shr 16;3318 Result.alpha := 255;3319 end;3320 3321 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;3322 begin3323 Result.red := color;3324 Result.green := color shr 8;3325 Result.blue := color shr 16;3326 Result.alpha := opacity;3327 end;3328 {$POP}3329 3330 { Conversion from TFPColor to TBGRAPixel assuming TFPColor3331 is already gamma compressed }3332 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;3333 begin3334 with AValue do3335 Result := BGRA(red shr 8, green shr 8, blue shr 8, alpha shr 8);3336 end;3337 3338 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;3339 begin3340 result.red := AValue.red shl 8 + AValue.red;3341 result.green := AValue.green shl 8 + AValue.green;3342 result.blue := AValue.blue shl 8 + AValue.blue;3343 result.alpha := AValue.alpha shl 8 + AValue.alpha;3344 end;3345 3346 function BGRAToColor(c: TBGRAPixel): TColor;3347 begin3348 Result := c.red + (c.green shl 8) + (c.blue shl 16);3349 end;3350 3351 operator = (const c1, c2: TBGRAPixel): boolean;3352 begin3353 if (c1.alpha = 0) and (c2.alpha = 0) then3354 Result := True3355 else3356 Result := (c1.alpha = c2.alpha) and (c1.red = c2.red) and3357 (c1.green = c2.green) and (c1.blue = c2.blue);3358 end;3359 3360 function LessStartSlope65535(value: word): word;3361 var factor: word;3362 begin3363 factor := 4096 - (not value)*3 shr 7;3364 result := value*factor shr 12;3365 end;3366 3367 function ExpandedDiff(ec1, ec2: TExpandedPixel): word;3368 var3369 CompRedAlpha1, CompGreenAlpha1, CompBlueAlpha1, CompRedAlpha2,3370 CompGreenAlpha2, CompBlueAlpha2: integer;3371 DiffAlpha: word;3372 ColorDiff: word;3373 TempHueDiff: word;3374 begin3375 CompRedAlpha1 := ec1.red * ec1.alpha shr 16; //gives 0..655353376 CompGreenAlpha1 := ec1.green * ec1.alpha shr 16;3377 CompBlueAlpha1 := ec1.blue * ec1.alpha shr 16;3378 CompRedAlpha2 := ec2.red * ec2.alpha shr 16;3379 CompGreenAlpha2 := ec2.green * ec2.alpha shr 16;3380 CompBlueAlpha2 := ec2.blue * ec2.alpha shr 16;3381 Result := (Abs(CompRedAlpha2 - CompRedAlpha1)*redWeightShl10 +3382 Abs(CompBlueAlpha2 - CompBlueAlpha1)*blueWeightShl10 +3383 Abs(CompGreenAlpha2 - CompGreenAlpha1)*greenWeightShl10) shr 10;3384 ColorDiff := min(ColorImportance(ec1),ColorImportance(ec2));3385 if ColorDiff > 0 then3386 begin3387 TempHueDiff := HueDiff(HtoG(GetHue(ec1)),HtoG(GetHue(ec2)));3388 if TempHueDiff < 32768 then3389 TempHueDiff := LessStartSlope65535(TempHueDiff shl 1) shr 43390 else3391 TempHueDiff := TempHueDiff shr 3;3392 Result := ((Result shr 4)* (not ColorDiff) + TempHueDiff*ColorDiff) shr 12;3393 end;3394 DiffAlpha := Abs(integer(ec2.Alpha) - integer(ec1.Alpha));3395 if DiffAlpha > Result then3396 Result := DiffAlpha;3397 end;3398 3399 function BGRAWordDiff(c1, c2: TBGRAPixel): word;3400 begin3401 result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2));3402 end;3403 3404 function BGRADiff(c1,c2: TBGRAPixel): byte;3405 begin3406 result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2)) shr 8;3407 end;3408 3409 operator-(const c1, c2: TColorF): TColorF;3410 begin3411 result[1] := c1[1]-c2[1];3412 result[2] := c1[2]-c2[2];3413 result[3] := c1[3]-c2[3];3414 result[4] := c1[4]-c2[4];3415 end;3416 3417 operator+(const c1, c2: TColorF): TColorF;3418 begin3419 result[1] := c1[1]+c2[1];3420 result[2] := c1[2]+c2[2];3421 result[3] := c1[3]+c2[3];3422 result[4] := c1[4]+c2[4];3423 end;3424 3425 operator*(const c1, c2: TColorF): TColorF;3426 begin3427 result[1] := c1[1]*c2[1];3428 result[2] := c1[2]*c2[2];3429 result[3] := c1[3]*c2[3];3430 result[4] := c1[4]*c2[4];3431 end;3432 3433 operator*(const c1: TColorF; factor: single): TColorF;3434 begin3435 result[1] := c1[1]*factor;3436 result[2] := c1[2]*factor;3437 result[3] := c1[3]*factor;3438 result[4] := c1[4]*factor;3439 end;3440 3441 function ColorF(red, green, blue, alpha: single): TColorF;3442 begin3443 result[1] := red;3444 result[2] := green;3445 result[3] := blue;3446 result[4] := alpha;3447 end;3448 3449 { Write a color in hexadecimal format RRGGBBAA or using the name in a color list }3450 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;3451 var idx: integer;3452 begin3453 if Assigned(AColorList) then3454 begin3455 idx := AColorList.IndexOfColor(c, AMaxDiff);3456 if idx<> -1 then3457 begin3458 result := AColorList.Name[idx];3459 exit;3460 end;3461 end;3462 result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2);3463 end;3464 3465 type3466 arrayOfString = array of string;3467 3468 function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString;3469 var idxOpen,start,cur: integer;3470 begin3471 result := nil;3472 idxOpen := pos('(',str);3473 if idxOpen = 0 then3474 begin3475 start := 1;3476 //find first space3477 while (start <= length(str)) and (str[start]<>' ') do inc(start);3478 end else3479 start := idxOpen+1;3480 cur := start;3481 while cur <= length(str) do3482 begin3483 if str[cur] in[',',')'] then3484 begin3485 setlength(result,length(result)+1);3486 result[high(result)] := trim(copy(str,start,cur-start));3487 start := cur+1;3488 if str[cur] = ')' then exit;3489 end;3490 inc(cur);3491 end;3492 if idxOpen <> 0 then flagError := true; //should exit on ')'3493 if start <= length(str) then3494 begin3495 setlength(result,length(result)+1);3496 result[high(result)] := copy(str,start,length(str)-start+1);3497 end;3498 end;3499 3500 function ParseColorValue(str: string; var flagError: boolean): byte;3501 var pourcent,unclipped,{%H-}errPos: integer;3502 begin3503 if str = '' then result := 0 else3504 begin3505 if str[length(str)]='%' then3506 begin3507 val(copy(str,1,length(str)-1),pourcent,errPos);3508 if errPos <> 0 then flagError := true;3509 if pourcent < 0 then result := 0 else3510 if pourcent > 100 then result := 255 else3511 result := pourcent*255 div 100;3512 end else3513 begin3514 val(str,unclipped,errPos);3515 if errPos <> 0 then flagError := true;3516 if unclipped < 0 then result := 0 else3517 if unclipped > 255 then result := 255 else3518 result := unclipped;3519 end;3520 end;3521 end;3522 3523 //this function returns the parsed value only if it contains no error nor missing values, otherwise3524 //it returns BGRAPixelTransparent3525 function StrToBGRA(str: string): TBGRAPixel;3526 var missingValues, error: boolean;3527 begin3528 result := BGRABlack;3529 TryStrToBGRA(str, result, missingValues, error);3530 if missingValues or error then result := BGRAPixelTransparent;3531 end;3532 3533 //this function changes the content of parsedValue depending on available and parsable information.3534 //set parsedValue to the fallback values before calling this function.3535 //missing values are expressed by empty string or by '?', for example 'rgb(255,?,?,?)' will change only the red value.3536 //note that if alpha is not expressed by the string format, it will be opaque. So 'rgb(255,?,?)' will change the red value and the alpha value.3537 //the last parameter of rgba() is a floating point number where 1 is opaque and 0 is transparent.3538 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);3539 var errPos: integer;3540 values: array of string;3541 alphaF: single;3542 idx: integer;3543 begin3544 str := Trim(str);3545 error := false;3546 if (str = '') or (str = '?') then3547 begin3548 missingValues := true;3549 exit;3550 end else3551 missingValues := false;3552 str := StringReplace(lowerCase(str),'grey','gray',[]);3553 3554 //VGA color names3555 idx := VGAColors.IndexOf(str);3556 if idx <> -1 then3557 begin3558 parsedValue := VGAColors[idx];3559 exit;3560 end;3561 if str='transparent' then parsedValue := BGRAPixelTransparent else3562 begin3563 //check CSS color3564 idx := CSSColors.IndexOf(str);3565 if idx <> -1 then3566 begin3567 parsedValue := CSSColors[idx];3568 exit;3569 end;3570 3571 //CSS RGB notation3572 if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') or3573 (copy(str,1,4)='rgb ') or (copy(str,1,5)='rgba ') then3574 begin3575 values := SimpleParseFuncParam(str,error);3576 if (length(values)=3) or (length(values)=4) then3577 begin3578 if (values[0] <> '') and (values[0] <> '?') then3579 parsedValue.red := ParseColorValue(values[0], error)3580 else3581 missingValues := true;3582 if (values[1] <> '') and (values[1] <> '?') then3583 parsedValue.green := ParseColorValue(values[1], error)3584 else3585 missingValues := true;3586 if (values[2] <> '') and (values[2] <> '?') then3587 parsedValue.blue := ParseColorValue(values[2], error)3588 else3589 missingValues := true;3590 if length(values)=4 then3591 begin3592 if (values[3] <> '') and (values[3] <> '?') then3593 begin3594 val(values[3],alphaF,errPos);3595 if errPos <> 0 then3596 begin3597 parsedValue.alpha := 255;3598 error := true;3599 end3600 else3601 begin3602 if alphaF < 0 then3603 parsedValue.alpha := 0 else3604 if alphaF > 1 then3605 parsedValue.alpha := 2553606 else3607 parsedValue.alpha := round(alphaF*255);3608 end;3609 end else3610 missingValues := true;3611 end else3612 parsedValue.alpha := 255;3613 end else3614 error := true;3615 exit;3616 end;3617 3618 //remove HTML notation header3619 if str[1]='#' then delete(str,1,1);3620 3621 //add alpha if missing (if you want an undefined alpha use '??' or '?')3622 if length(str)=6 then str += 'FF';3623 if length(str)=3 then str += 'F';3624 3625 //hex notation3626 if length(str)=8 then3627 begin3628 if copy(str,1,2) <> '??' then3629 begin3630 val('$'+copy(str,1,2),parsedValue.red,errPos);3631 if errPos <> 0 then error := true;3632 end else missingValues := true;3633 if copy(str,3,2) <> '??' then3634 begin3635 val('$'+copy(str,3,2),parsedValue.green,errPos);3636 if errPos <> 0 then error := true;3637 end else missingValues := true;3638 if copy(str,5,2) <> '??' then3639 begin3640 val('$'+copy(str,5,2),parsedValue.blue,errPos);3641 if errPos <> 0 then error := true;3642 end else missingValues := true;3643 if copy(str,7,2) <> '??' then3644 begin3645 val('$'+copy(str,7,2),parsedValue.alpha,errPos);3646 if errPos <> 0 then3647 begin3648 error := true;3649 parsedValue.alpha := 255;3650 end;3651 end else missingValues := true;3652 end else3653 if length(str)=4 then3654 begin3655 if str[1] <> '?' then3656 begin3657 val('$'+str[1],parsedValue.red,errPos);3658 if errPos <> 0 then error := true;3659 parsedValue.red *= $11;3660 end else missingValues := true;3661 if str[2] <> '?' then3662 begin3663 val('$'+str[2],parsedValue.green,errPos);3664 if errPos <> 0 then error := true;3665 parsedValue.green *= $11;3666 end else missingValues := true;3667 if str[3] <> '?' then3668 begin3669 val('$'+str[3],parsedValue.blue,errPos);3670 if errPos <> 0 then error := true;3671 parsedValue.blue *= $11;3672 end else missingValues := true;3673 if str[4] <> '?' then3674 begin3675 val('$'+str[4],parsedValue.alpha,errPos);3676 if errPos <> 0 then3677 begin3678 error := true;3679 parsedValue.alpha := 255;3680 end else3681 parsedValue.alpha *= $11;3682 end else missingValues := true;3683 end else3684 error := true; //string format not recognised3685 end;3686 3687 end;3688 3689 //this function returns the values that can be read from the string, otherwise3690 //it fills the gaps with the fallback values. The error boolean is True only3691 //if there was invalid values, it is not set to True if there was missing values.3692 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out3693 error: boolean): TBGRAPixel;3694 var missingValues: boolean;3695 begin3696 result := fallbackValues;3697 TryStrToBGRA(str, result, missingValues, error);3698 end;3699 3700 { Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. }3701 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;3702 var missingValues, error: boolean;3703 begin3704 result := BGRABlack;3705 TryStrToBGRA(str, result, missingValues, error);3706 if missingValues or error then result := DefaultColor;3707 end;3708 3709 function MapHeight(Color: TBGRAPixel): Single;3710 var intval: integer;3711 begin3712 intval := color.Green shl 16 + color.red shl 8 + color.blue;3713 result := intval*5.960464832810452e-8;3714 end;3715 3716 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;3717 var intval: integer;3718 begin3719 if Height >= 1 then result := BGRA(255,255,255,alpha) else3720 if Height <= 0 then result := BGRA(0,0,0,alpha) else3721 begin3722 intval := round(Height*16777215);3723 result := BGRA(intval shr 8,intval shr 16,intval,alpha);3724 end;3725 end;3726 3727 {********************** Point functions **************************}3728 3729 function PointF(x, y: single): TPointF;3730 begin3731 Result.x := x;3732 Result.y := y;3733 end;3734 3735 function PointsF(const pts: array of TPointF): ArrayOfTPointF;3736 var3737 i: Integer;3738 begin3739 setlength(result, length(pts));3740 for i := 0 to high(pts) do result[i] := pts[i];3741 end;3742 3743 operator =(const pt1, pt2: TPointF): boolean;3744 begin3745 result := (pt1.x = pt2.x) and (pt1.y = pt2.y);3746 end;3747 3748 operator-(const pt1, pt2: TPointF): TPointF;3749 begin3750 result.x := pt1.x-pt2.x;3751 result.y := pt1.y-pt2.y;3752 end;3753 3754 operator-(const pt2: TPointF): TPointF;3755 begin3756 result.x := -pt2.x;3757 result.y := -pt2.y;3758 end;3759 3760 operator+(const pt1, pt2: TPointF): TPointF;3761 begin3762 result.x := pt1.x+pt2.x;3763 result.y := pt1.y+pt2.y;3764 end;3765 3766 operator*(const pt1, pt2: TPointF): single;3767 begin3768 result := pt1.x*pt2.x + pt1.y*pt2.y;3769 end;3770 3771 operator*(const pt1: TPointF; factor: single): TPointF;3772 begin3773 result.x := pt1.x*factor;3774 result.y := pt1.y*factor;3775 end;3776 3777 operator*(factor: single; const pt1: TPointF): TPointF;3778 begin3779 result.x := pt1.x*factor;3780 result.y := pt1.y*factor;3781 end;3782 3783 function PtInRect(const pt: TPoint; r: TRect): boolean;3784 var3785 temp: integer;3786 begin3787 if r.right < r.left then3788 begin3789 temp := r.left;3790 r.left := r.right;3791 r.Right := temp;3792 end;3793 if r.bottom < r.top then3794 begin3795 temp := r.top;3796 r.top := r.bottom;3797 r.bottom := temp;3798 end;3799 Result := (pt.X >= r.left) and (pt.Y >= r.top) and (pt.X < r.right) and3800 (pt.y < r.bottom);3801 end;3802 3803 function RectWithSize(left, top, width, height: integer): TRect;3804 begin3805 result.left := left;3806 result.top := top;3807 result.right := left+width;3808 result.bottom := top+height;3809 end;3810 3811 function VectLen(dx, dy: single): single;3812 begin3813 result := sqrt(dx*dx+dy*dy);3814 end;3815 3816 function VectLen(v: TPointF): single;3817 begin3818 result := sqrt(v.x*v.x+v.y*v.y);3819 end;3820 {$OPTIMIZATION OFF} // Modif J.P 5/20133821 function IntersectLine(line1, line2: TLineDef): TPointF;3822 var parallel: boolean;3823 begin3824 result := IntersectLine(line1,line2,parallel);3825 end;3826 {$OPTIMIZATION ON}3827 3828 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;3829 var divFactor: double;3830 begin3831 parallel := false;3832 //if lines are parallel3833 if ((line1.dir.x = line2.dir.x) and (line1.dir.y = line2.dir.y)) or3834 ((abs(line1.dir.y) < 1e-6) and (abs(line2.dir.y) < 1e-6)) then3835 begin3836 parallel := true;3837 //return the center of the segment between line origins3838 result.x := (line1.origin.x+line2.origin.x)/2;3839 result.y := (line1.origin.y+line2.origin.y)/2;3840 end else3841 if abs(line1.dir.y) < 1e-6 then //line1 is horizontal3842 begin3843 result.y := line1.origin.y;3844 result.x := line2.origin.x + (result.y - line2.origin.y)3845 /line2.dir.y*line2.dir.x;3846 end else3847 if abs(line2.dir.y) < 1e-6 then //line2 is horizontal3848 begin3849 result.y := line2.origin.y;3850 result.x := line1.origin.x + (result.y - line1.origin.y)3851 /line1.dir.y*line1.dir.x;3852 end else3853 begin3854 divFactor := line1.dir.x/line1.dir.y - line2.dir.x/line2.dir.y;3855 if abs(divFactor) < 1e-6 then //almost parallel3856 begin3857 parallel := true;3858 //return the center of the segment between line origins3859 result.x := (line1.origin.x+line2.origin.x)/2;3860 result.y := (line1.origin.y+line2.origin.y)/2;3861 end else3862 begin3863 result.y := (line2.origin.x - line1.origin.x +3864 line1.origin.y*line1.dir.x/line1.dir.y -3865 line2.origin.y*line2.dir.x/line2.dir.y)3866 / divFactor;3867 result.x := line1.origin.x + (result.y - line1.origin.y)3868 /line1.dir.y*line1.dir.x;3869 end;3870 end;3871 end;3872 3873 { Check if a polygon is convex, i.e. it always turns in the same direction }3874 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean;3875 var3876 positive,negative,zero: boolean;3877 product: single;3878 i: Integer;3879 begin3880 positive := false;3881 negative := false;3882 zero := false;3883 for i := 0 to high(pts) do3884 begin3885 product := (pts[(i+1) mod length(pts)].x-pts[i].x)*(pts[(i+2) mod length(pts)].y-pts[i].y) -3886 (pts[(i+1) mod length(pts)].y-pts[i].y)*(pts[(i+2) mod length(pts)].x-pts[i].x);3887 if product > 0 then3888 begin3889 if negative then3890 begin3891 result := false;3892 exit;3893 end;3894 positive := true;3895 end else3896 if product < 0 then3897 begin3898 if positive then3899 begin3900 result := false;3901 exit;3902 end;3903 negative := true;3904 end else3905 zero := true;3906 end;3907 if not IgnoreAlign and zero then3908 result := false3909 else3910 result := true;3911 end;3912 3913 { Check if two segments intersect }3914 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;3915 var3916 seg1: TLineDef;3917 seg1len: single;3918 seg2: TLineDef;3919 seg2len: single;3920 inter: TPointF;3921 pos1,pos2: single;3922 para: boolean;3923 3924 begin3925 { Determine line definitions }3926 seg1.origin := pt1;3927 seg1.dir := pt2-pt1;3928 seg1len := sqrt(sqr(seg1.dir.X)+sqr(seg1.dir.Y));3929 if seg1len = 0 then3930 begin3931 result := false;3932 exit;3933 end;3934 seg1.dir *= 1/seg1len;3935 3936 seg2.origin := pt3;3937 seg2.dir := pt4-pt3;3938 seg2len := sqrt(sqr(seg2.dir.X)+sqr(seg2.dir.Y));3939 if seg2len = 0 then3940 begin3941 result := false;3942 exit;3943 end;3944 seg2.dir *= 1/seg2len;3945 3946 //obviously parallel3947 if seg1.dir = seg2.dir then3948 result := false3949 else3950 begin3951 //try to compute intersection3952 inter := IntersectLine(seg1,seg2,para);3953 if para then3954 result := false3955 else3956 begin3957 //check if intersections are inside the segments3958 pos1 := (inter-seg1.origin)*seg1.dir;3959 pos2 := (inter-seg2.origin)*seg2.dir;3960 if (pos1 >= 0) and (pos1 <= seg1len) and3961 (pos2 >= 0) and (pos2 <= seg2len) then3962 result := true3963 else3964 result := false;3965 end;3966 end;3967 end;3968 3969 { Check if a quaduadrilateral intersects itself }3970 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;3971 begin3972 result := DoesSegmentIntersect(pt1,pt2,pt3,pt4) or DoesSegmentIntersect(pt2,pt3,pt4,pt1);3973 612 end; 3974 613 … … 4110 749 begin 4111 750 for i := 0 to 6 do dwords[i] := LEtoN(dwords[i]); 4112 if (dwords[0] = 0) and (dwords[1] <= expectedFileSize) and (dwords[5] <= expectedFileSize) and4113 (dwords[9] <= expectedFileSize) and751 if (dwords[0] = 0) and (dwords[1] <= maxFileSize) and (dwords[5] <= maxFileSize) and 752 (dwords[9] <= maxFileSize) and 4114 753 (dwords[6] = 0) then inc(scores[ifLazPaint],2); 4115 754 end; … … 4199 838 end; 4200 839 840 if (copy(magicAsText,1,4) = 'oXo ') then 841 begin 842 inc(scores[ifPhoxo],1); 843 if (magic[4] = 1) and (magic[5] = 0) and (magic[6] = 0) and (magic[7] = 0) then 844 inc(scores[ifPhoxo],1); 845 end; 846 4201 847 DetectLazPaint; 4202 848 … … 4233 879 4234 880 ASuggestedExtensionUTF8:= UTF8LowerCase(ASuggestedExtensionUTF8); 4235 if (ASuggestedExtensionUTF8 <> '') and ( UTF8Copy(ASuggestedExtensionUTF8,1,1) <> '.') then881 if (ASuggestedExtensionUTF8 <> '') and (ASuggestedExtensionUTF8[1] <> '.') then //first UTF8 char is in first pos 4236 882 ASuggestedExtensionUTF8 := '.'+ASuggestedExtensionUTF8; 4237 883 … … 4254 900 function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat; 4255 901 var ext: string; 902 posDot: integer; 4256 903 begin 4257 904 result := ifUnknown; 4258 905 4259 906 ext := ExtractFileName(AFilenameOrExtensionUTF8); 4260 if pos('.', ext) <> 0 then ext := ExtractFileExt(ext) else ext := '.'+ext; 907 posDot := LastDelimiter('.', ext); 908 if posDot <> 0 then ext := copy(ext,posDot,length(ext)-posDot+1) 909 else ext := '.'+ext; 4261 910 ext := UTF8LowerCase(ext); 4262 911 … … 4274 923 if (ext = '.tif') or (ext = '.tiff') then result := ifTiff else 4275 924 if (ext = '.xwd') then result := ifXwd else 4276 if (ext = '.xpm') then result := ifXPixMap; 925 if (ext = '.xpm') then result := ifXPixMap else 926 if (ext = '.oxo') then result := ifPhoxo; 927 end; 928 929 function SuggestImageExtension(AFormat: TBGRAImageFormat): string; 930 begin 931 case AFormat of 932 ifJpeg: result := 'jpg'; 933 ifPng: result := 'png'; 934 ifGif: result := 'gif'; 935 ifBmp: result := 'bmp'; 936 ifIco: result := 'ico'; 937 ifPcx: result := 'pcx'; 938 ifPaintDotNet: result := 'pdn'; 939 ifLazPaint: result := 'lzp'; 940 ifOpenRaster: result := 'ora'; 941 ifPsd: result := 'psd'; 942 ifTarga: result := 'tga'; 943 ifTiff: result := 'tif'; 944 ifXwd: result := 'xwd'; 945 ifXPixMap: result := 'xpm'; 946 ifBmpMioMap: result := 'bmp'; 947 else result := '?'; 948 end; 4277 949 end; 4278 950 … … 4306 978 if AFormat = ifPng then 4307 979 begin 4308 result := TFPWriterPNG.Create; 4309 TFPWriterPNG(result).Indexed := false; 4310 TFPWriterPNG(result).WordSized := false; 4311 TFPWriterPNG(result).UseAlpha := AHasTransparentPixels; 980 result := TBGRAWriterPNG.Create; 981 TBGRAWriterPNG(result).UseAlpha := AHasTransparentPixels; 4312 982 end else 4313 983 if AFormat = ifBmp then … … 4328 998 initialization 4329 999 4330 InitGamma; 4331 {$DEFINE INCLUDE_COLOR_LIST} 1000 {$DEFINE INCLUDE_INIT} 1001 {$I bgrapixel.inc} 1002 1003 {$DEFINE INCLUDE_INIT} 4332 1004 {$I csscolorconst.inc} 1005 4333 1006 DefaultBGRAImageWriter[ifJpeg] := TFPWriterJPEG; 4334 DefaultBGRAImageWriter[ifPng] := T FPWriterPNG;1007 DefaultBGRAImageWriter[ifPng] := TBGRAWriterPNG; 4335 1008 DefaultBGRAImageWriter[ifBmp] := TFPWriterBMP; 4336 1009 DefaultBGRAImageWriter[ifPcx] := TFPWriterPCX; … … 4346 1019 finalization 4347 1020 4348 CSSColors.Free; 4349 VGAColors.Free; 4350 1021 {$DEFINE INCLUDE_FINAL} 1022 {$I csscolorconst.inc} 1023 1024 {$DEFINE INCLUDE_FINAL} 1025 {$I bgrapixel.inc} 4351 1026 end. -
GraphicTest/Packages/bgrabitmap/bgrablend.pas
r472 r494 20 20 procedure DrawExpandedPixelInlineNoAlphaCheck(dest: PBGRAPixel; const ec: TExpandedPixel; calpha: byte); inline; overload; 21 21 procedure ClearTypeDrawPixel(pdest: PBGRAPixel; Cr, Cg, Cb: byte; Color: TBGRAPixel); inline; 22 procedure InterpolateBilinear(pUpLeft,pUpRight,pDownLeft,pDownRight: PBGRAPixel; 23 iFactX,iFactY: Integer; ADest: PBGRAPixel); 22 24 23 25 procedure CopyPixelsWithOpacity(dest,src: PBGRAPixel; opacity: byte; Count: integer); inline; … … 100 102 procedure ScreenPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 101 103 procedure SoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 104 procedure SvgSoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 102 105 procedure HardLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 103 106 procedure BlendXorPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 107 procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean); 108 procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x, y: integer; 109 mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; 110 KeepRGBOrder: boolean); 111 procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean); 104 112 105 113 implementation 114 115 procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean); 116 var 117 pdest: PBGRAPixel; 118 ClearTypePixel: array[0..2] of byte; 119 curThird: integer; 120 121 procedure OutputPixel; inline; 122 begin 123 if texture <> nil then 124 color := texture.ScanNextPixel; 125 if RGBOrder then 126 ClearTypeDrawPixel(pdest, ClearTypePixel[0],ClearTypePixel[1],ClearTypePixel[2], color) 127 else 128 ClearTypeDrawPixel(pdest, ClearTypePixel[2],ClearTypePixel[1],ClearTypePixel[0], color); 129 end; 130 131 procedure NextAlpha(alphaValue: byte); inline; 132 begin 133 ClearTypePixel[curThird] := alphaValue; 134 inc(curThird); 135 if curThird = 3 then 136 begin 137 OutputPixel; 138 curThird := 0; 139 Fillchar(ClearTypePixel, sizeof(ClearTypePixel),0); 140 inc(pdest); 141 end; 142 end; 143 144 procedure EndRow; inline; 145 begin 146 if curThird > 0 then OutputPixel; 147 end; 148 149 var 150 yMask,n: integer; 151 a: byte; 152 pmask: PByte; 153 dx:integer; 154 miny,maxy,minx,minxThird,maxx,alphaMinX,alphaMaxX,alphaLineLen: integer; 155 leftOnSide, rightOnSide: boolean; 156 countBetween: integer; 157 v1,v2,v3: byte; 158 159 procedure StartRow; inline; 160 begin 161 pdest := dest.Scanline[yMask+y]+minx; 162 if texture <> nil then 163 texture.ScanMoveTo(minx,yMask+y); 164 165 curThird := minxThird; 166 ClearTypePixel[0] := 0; 167 ClearTypePixel[1] := 0; 168 ClearTypePixel[2] := 0; 169 end; 170 171 begin 172 alphaLineLen := maskWidth+2; 173 174 xThird -= 1; //for first subpixel 175 176 if xThird >= 0 then dx := xThird div 3 177 else dx := -((-xThird+2) div 3); 178 x += dx; 179 xThird -= dx*3; 180 181 if y >= dest.ClipRect.Top then miny := 0 182 else miny := dest.ClipRect.Top-y; 183 if y+maskHeight-1 < dest.ClipRect.Bottom then 184 maxy := maskHeight-1 else 185 maxy := dest.ClipRect.Bottom-1-y; 186 187 if x >= dest.ClipRect.Left then 188 begin 189 minx := x; 190 minxThird := xThird; 191 alphaMinX := 0; 192 leftOnSide := false; 193 end else 194 begin 195 minx := dest.ClipRect.Left; 196 minxThird := 0; 197 alphaMinX := (dest.ClipRect.Left-x)*3 - xThird; 198 leftOnSide := true; 199 end; 200 201 if x*3+xThird+maskWidth-1 < dest.ClipRect.Right*3 then 202 begin 203 maxx := (x*3+xThird+maskWidth-1) div 3; 204 alphaMaxX := alphaLineLen-1; 205 rightOnSide := false; 206 end else 207 begin 208 maxx := dest.ClipRect.Right-1; 209 alphaMaxX := maxx*3+2 - (x*3+xThird); 210 rightOnSide := true; 211 end; 212 213 countBetween := alphaMaxX-alphaMinX-1; 214 215 if (alphaMinX <= alphaMaxX) then 216 begin 217 for yMask := miny to maxy do 218 begin 219 StartRow; 220 221 if leftOnSide then 222 begin 223 pmask := maskData + (yMask*maskRowSize)+ (alphaMinX-1)*maskPixelSize; 224 a := pmask^ div 3; 225 v1 := a+a; 226 v2 := a; 227 v3 := 0; 228 inc(pmask, maskPixelSize); 229 end else 230 begin 231 pmask := maskData + (yMask*maskRowSize); 232 v1 := 0; 233 v2 := 0; 234 v3 := 0; 235 end; 236 237 for n := countBetween-1 downto 0 do 238 begin 239 a := pmask^ div 3; 240 v1 += a; 241 v2 += a; 242 v3 += a; 243 inc(pmask, maskPixelSize); 244 245 NextAlpha(v1); 246 v1 := v2; 247 v2 := v3; 248 v3 := 0; 249 end; 250 251 if rightOnSide then 252 begin 253 a := pmask^ div 3; 254 v1 += a; 255 v2 += a+a; 256 end; 257 258 NextAlpha(v1); 259 NextAlpha(v2); 260 261 EndRow; 262 end; 263 end; 264 end; 265 266 procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean); 267 var delta: NativeInt; 268 begin 269 delta := mask.Width*sizeof(TBGRAPixel); 270 if mask.LineOrder = riloBottomToTop then 271 delta := -delta; 272 BGRAFillClearTypeMaskPtr(dest,x,y,xThird,pbyte(mask.ScanLine[0])+1,sizeof(TBGRAPixel),delta,mask.Width,mask.Height,color,texture,RGBOrder); 273 end; 274 275 procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x, y: integer; 276 mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; 277 KeepRGBOrder: boolean); 278 var 279 minx,miny,maxx,maxy,countx,n,yb: integer; 280 pdest,psrc: PBGRAPixel; 281 begin 282 if y >= dest.ClipRect.Top then miny := 0 283 else miny := dest.ClipRect.Top-y; 284 if y+mask.Height-1 < dest.ClipRect.Bottom then 285 maxy := mask.Height-1 else 286 maxy := dest.ClipRect.Bottom-1-y; 287 288 if x >= dest.ClipRect.Left then minx := 0 289 else minx := dest.ClipRect.Left-x; 290 if x+mask.Width-1 < dest.ClipRect.Right then 291 maxx := mask.Width-1 else 292 maxx := dest.ClipRect.Right-1-x; 293 294 countx := maxx-minx+1; 295 if countx <= 0 then exit; 296 297 for yb := miny to maxy do 298 begin 299 pdest := dest.ScanLine[y+yb]+(x+minx); 300 psrc := mask.ScanLine[yb]+minx; 301 if texture <> nil then 302 texture.ScanMoveTo(x+minx, y+yb); 303 if KeepRGBOrder then 304 begin 305 for n := countx-1 downto 0 do 306 begin 307 if texture <> nil then color := texture.ScanNextPixel; 308 ClearTypeDrawPixel(pdest, psrc^.red, psrc^.green, psrc^.blue, color); 309 inc(pdest); 310 inc(psrc); 311 end; 312 end else 313 begin 314 for n := countx-1 downto 0 do 315 begin 316 if texture <> nil then color := texture.ScanNextPixel; 317 ClearTypeDrawPixel(pdest, psrc^.blue, psrc^.green, psrc^.red, color); 318 inc(pdest); 319 inc(psrc); 320 end; 321 end; 322 end; 323 end; 106 324 107 325 procedure ClearTypeDrawPixel(pdest: PBGRAPixel; Cr, Cg, Cb: byte; Color: TBGRAPixel); … … 141 359 end; 142 360 pdest^ := merge; 361 end; 362 end; 363 364 procedure InterpolateBilinear(pUpLeft, pUpRight, pDownLeft, 365 pDownRight: PBGRAPixel; iFactX,iFactY: Integer; ADest: PBGRAPixel); 366 var 367 w1,w2,w3,w4,alphaW: cardinal; 368 rSum, gSum, bSum: cardinal; //rgbDiv = aSum 369 aSum, aDiv: cardinal; 370 begin 371 rSum := 0; 372 gSum := 0; 373 bSum := 0; 374 aSum := 0; 375 aDiv := 0; 376 377 w4 := (iFactX*iFactY+127) shr 8; 378 w3 := iFactY-w4; 379 {$PUSH}{$HINTS OFF} 380 w1 := (256-iFactX)-w3; 381 {$POP} 382 w2 := iFactX-w4; 383 384 { For each pixel around the coordinate, compute 385 the weight for it and multiply values by it before 386 adding to the sum } 387 if pUpLeft <> nil then 388 with pUpLeft^ do 389 begin 390 alphaW := alpha * w1; 391 aDiv += w1; 392 aSum += alphaW; 393 rSum += red * alphaW; 394 gSum += green * alphaW; 395 bSum += blue * alphaW; 396 end; 397 if pUpRight <> nil then 398 with pUpRight^ do 399 begin 400 alphaW := alpha * w2; 401 aDiv += w2; 402 aSum += alphaW; 403 rSum += red * alphaW; 404 gSum += green * alphaW; 405 bSum += blue * alphaW; 406 end; 407 if pDownLeft <> nil then 408 with pDownLeft^ do 409 begin 410 alphaW := alpha * w3; 411 aDiv += w3; 412 aSum += alphaW; 413 rSum += red * alphaW; 414 gSum += green * alphaW; 415 bSum += blue * alphaW; 416 end; 417 if pDownRight <> nil then 418 with pDownRight^ do 419 begin 420 alphaW := alpha * w4; 421 aDiv += w4; 422 aSum += alphaW; 423 rSum += red * alphaW; 424 gSum += green * alphaW; 425 bSum += blue * alphaW; 426 end; 427 428 if aSum < 128 then //if there is no alpha 429 ADest^ := BGRAPixelTransparent 430 else 431 with ADest^ do 432 begin 433 red := (rSum + aSum shr 1) div aSum; 434 green := (gSum + aSum shr 1) div aSum; 435 blue := (bSum + aSum shr 1) div aSum; 436 if aDiv = 256 then 437 alpha := (aSum + 128) shr 8 438 else 439 alpha := (aSum + aDiv shr 1) div aDiv; 143 440 end; 144 441 end; … … 475 772 procedure DrawPixelInlineNoAlphaCheck(dest: PBGRAPixel; const c: TBGRAPixel); 476 773 var 477 p: PByte;478 774 a1f, a2f, a12, a12m: cardinal; 479 775 begin … … 486 782 a2f := (c.alpha shl 8) - c.alpha; 487 783 488 p := PByte(dest); 489 490 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f + 491 GammaExpansionTab[c.blue] * a2f + a12m) div a12]; 492 Inc(p); 493 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f + 494 GammaExpansionTab[c.green] * a2f + a12m) div a12]; 495 Inc(p); 496 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f + 497 GammaExpansionTab[c.red] * a2f + a12m) div a12]; 498 Inc(p); 499 500 p^ := (a12 + a12 shr 7) shr 8; 784 PDWord(dest)^ := ((GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f + 785 GammaExpansionTab[c.red] * a2f + a12m) div a12]) shl TBGRAPixel_RedShift) or 786 ((GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f + 787 GammaExpansionTab[c.green] * a2f + a12m) div a12]) shl TBGRAPixel_GreenShift) or 788 ((GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f + 789 GammaExpansionTab[c.blue] * a2f + a12m) div a12]) shl TBGRAPixel_BlueShift) or 790 (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift); 501 791 end; 502 792 … … 504 794 const ec: TExpandedPixel; calpha: byte); 505 795 var 506 p: PByte;507 796 a1f, a2f, a12, a12m: cardinal; 508 797 begin … … 515 804 a2f := (calpha shl 8) - calpha; 516 805 517 p := PByte(dest); 518 519 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f + 520 ec.blue * a2f + a12m) div a12]; 521 Inc(p); 522 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f + 523 ec.green * a2f + a12m) div a12]; 524 Inc(p); 525 p^ := GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f + 526 ec.red * a2f + a12m) div a12]; 527 Inc(p); 528 529 p^ := (a12 + a12 shr 7) shr 8; 806 PDWord(dest)^ := ((GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f + 807 ec.red * a2f + a12m) div a12]) shl TBGRAPixel_RedShift) or 808 ((GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f + 809 ec.green * a2f + a12m) div a12]) shl TBGRAPixel_GreenShift) or 810 ((GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f + 811 ec.blue * a2f + a12m) div a12]) shl TBGRAPixel_BlueShift) or 812 (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift); 530 813 end; 531 814 532 815 procedure FastBlendPixelInline(dest: PBGRAPixel; const c: TBGRAPixel); 533 816 var 534 p: PByte;535 817 a1f, a2f, a12, a12m: cardinal; 536 818 begin … … 551 833 a2f := (c.alpha shl 8) - c.alpha; 552 834 553 p := PByte(dest); 554 555 p^ := (dest^.blue * a1f + c.blue * a2f + a12m) div a12; 556 Inc(p); 557 p^ := (dest^.green * a1f + c.green * a2f + a12m) div a12; 558 Inc(p); 559 p^ := (dest^.red * a1f + c.red * a2f + a12m) div a12; 560 Inc(p); 561 562 p^ := (a12 + a12 shr 7) shr 8; 835 PDWord(dest)^ := (((dest^.red * a1f + c.red * a2f + a12m) div a12) shl TBGRAPixel_RedShift) or 836 (((dest^.green * a1f + c.green * a2f + a12m) div a12) shl TBGRAPixel_GreenShift) or 837 (((dest^.blue * a1f + c.blue * a2f + a12m) div a12) shl TBGRAPixel_BlueShift) or 838 (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift); 563 839 end; 564 840 … … 572 848 procedure DrawPixelInlineDiff(dest: PBGRAPixel; c, compare: TBGRAPixel; 573 849 maxDiff: byte); inline; 574 begin 575 DrawPixelInlineWithAlphaCheck(dest, BGRA(c.red, c.green, c.blue, 576 (c.alpha * (maxDiff + 1 - BGRADiff(dest^, compare)) + (maxDiff + 1) shr 1) div 577 (maxDiff + 1))); 850 var alpha: NativeInt; 851 begin 852 alpha := (c.alpha * (maxDiff + 1 - BGRADiff(dest^, compare)) + (maxDiff + 1) shr 1) div 853 (maxDiff + 1); 854 if alpha > 0 then 855 DrawPixelInlineWithAlphaCheck(dest, BGRA(c.red, c.green, c.blue, alpha)); 578 856 end; 579 857 -
GraphicTest/Packages/bgrabitmap/bgracanvas.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, FPCanvas, Graphics, GraphType, Types, FPImage, BGRABitmapTypes;8 Classes, SysUtils, FPCanvas, BGRAGraphics, Types, FPImage, BGRABitmapTypes; 9 9 10 10 type … … 1099 1099 Style: TGraphicsBevelCut); 1100 1100 begin 1101 Frame3D(bounds,width,style,ColorToBGRA( ColorToRGB(clBtnHighlight)),ColorToBGRA(ColorToRGB(clBtnShadow)));1101 Frame3D(bounds,width,style,ColorToBGRA(clRgbBtnHighlight),ColorToBGRA(clRgbBtnShadow)); 1102 1102 end; 1103 1103 … … 1150 1150 RStop,RStart: Byte; 1151 1151 begin 1152 RedGreenBlue(ColorToRGB(AStart), RStart, GStart, BStart);1152 RedGreenBlue(ColorToRGB(AStart), RStart, GStart, BStart); 1153 1153 RedGreenBlue(ColorToRGB(AStop), RStop, GStop, BStop); 1154 1154 -
GraphicTest/Packages/bgrabitmap/bgracanvas2d.pas
r472 r494 18 18 19 19 uses 20 Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATransform, BGRAGradientScanner, BGRAPath; 20 Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform, 21 BGRAGradientScanner, BGRAPath, BGRAPen; 21 22 22 23 type … … 42 43 43 44 TBGRACanvasState2D = class 45 private 46 FClipMask: TBGRACustomBitmap; 47 FClipMaskOwned: boolean; 48 function GetClipMaskReadWrite: TBGRACustomBitmap; 49 public 44 50 strokeColor: TBGRAPixel; 45 51 strokeTextureProvider: IBGRACanvasTextureProvider2D; … … 55 61 56 62 lineWidth: single; 57 lineCap: TPenEndCap; 58 lineJoin: TPenJoinStyle; 59 lineStyle: TBGRAPenStyle; 60 miterLimit: single; 63 penStroker: TBGRAPenStroker; 61 64 62 65 shadowOffsetX,shadowOffsetY,shadowBlur: single; … … 65 68 66 69 matrix: TAffineMatrix; 67 clipMask: TBGRACustomBitmap; 68 constructor Create(AMatrix: TAffineMatrix; AClipMask: TBGRACustomBitmap); 70 constructor Create(AMatrix: TAffineMatrix; AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean); 69 71 function Duplicate: TBGRACanvasState2D; 70 72 destructor Destroy; override; 73 procedure SetClipMask(AClipMask: TBGRACustomBitmap; AOwned: boolean); 74 property clipMaskReadOnly: TBGRACustomBitmap read FClipMask; 75 property clipMaskReadWrite: TBGRACustomBitmap read GetClipMaskReadWrite; 71 76 end; 72 77 … … 88 93 FFontRenderer: TBGRACustomFontRenderer; 89 94 FLastCoord, FStartCoord: TPointF; 90 function GetCurrentPath : ArrayOfTPointF;95 function GetCurrentPathAsPoints: ArrayOfTPointF; 91 96 function GetFontName: string; 92 97 function GetFontRenderer: TBGRACustomFontRenderer; … … 110 115 function GetShadowOffsetX: single; 111 116 function GetShadowOffsetY: single; 117 function GetStrokeMatrix: TAffineMatrix; 112 118 function GetTextAlign: string; 113 119 function GetTextAlignLCL: TAlignment; … … 135 141 procedure SetShadowOffsetX(const AValue: single); 136 142 procedure SetShadowOffsetY(const AValue: single); 143 procedure SetStrokeMatrix(AValue: TAffineMatrix); 137 144 procedure SetTextAlign(AValue: string); 138 145 procedure SetTextAlignLCL(AValue: TAlignment); … … 152 159 function GetDrawMode: TDrawMode; 153 160 procedure copyTo({%H-}dest: IBGRAPath); //IBGRAPath 161 function getPoints: ArrayOfTPointF; //IBGRAPath 162 function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; //IBGRAPath 163 function getCursor: TBGRACustomPathCursor; //IBGRAPath 154 164 public 155 165 antialiasing, linearBlend: boolean; … … 165 175 procedure rotate(angleRadCW: single); 166 176 procedure translate(x,y: single); 167 procedure transform(a,b,c,d,e,f: single); overload; 177 procedure skewx(angleRadCW: single); 178 procedure skewy(angleRadCW: single); 179 procedure transform(m11,m21, m12,m22, m13,m23: single); overload; 168 180 procedure transform(AMatrix: TAffineMatrix); overload; 169 procedure setTransform( a,b,c,d,e,f: single);181 procedure setTransform(m11,m21, m12,m22, m13,m23: single); 170 182 procedure resetTransform; 183 184 procedure strokeScale(x,y: single); 185 procedure strokeSkewx(angleRadCW: single); 186 procedure strokeSkewy(angleRadCW: single); 187 procedure strokeResetTransform; 188 171 189 procedure strokeStyle(color: TBGRAPixel); overload; 172 190 procedure strokeStyle(color: TColor); overload; … … 214 232 procedure roundRect(x,y,w,h,radius: single); overload; 215 233 procedure roundRect(x,y,w,h,rx,ry: single); overload; 234 procedure openedSpline(const pts: array of TPointF; style: TSplineStyle); 235 procedure closedSpline(const pts: array of TPointF; style: TSplineStyle); 216 236 procedure spline(const pts: array of TPointF; style: TSplineStyle= ssOutside); 217 237 procedure splineTo(const pts: array of TPointF; style: TSplineStyle= ssOutside); … … 254 274 property globalAlpha: single read GetGlobalAlpha write SetGlobalAlpha; 255 275 property matrix: TAffineMatrix read GetMatrix write SetMatrix; 276 property strokeMatrix: TAffineMatrix read GetStrokeMatrix write SetStrokeMatrix; 256 277 257 278 property lineWidth: single read GetLineWidth write SetLineWidth; … … 277 298 property textBaseline: string read GetTextBaseline write SetTextBaseine; 278 299 279 property currentPath: ArrayOfTPointF read GetCurrentPath ;300 property currentPath: ArrayOfTPointF read GetCurrentPathAsPoints; 280 301 property fontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer; 281 302 … … 288 309 implementation 289 310 290 uses Types, Math, BGRA Pen, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;311 uses Types, Math, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64; 291 312 292 313 type … … 522 543 { TBGRACanvasState2D } 523 544 545 function TBGRACanvasState2D.GetClipMaskReadWrite: TBGRACustomBitmap; 546 begin 547 if not FClipMaskOwned then 548 begin 549 if FClipMask <> nil then 550 FClipMask := FClipMask.Duplicate; 551 FClipMaskOwned := true; 552 end; 553 result := FClipMask; 554 end; 555 524 556 constructor TBGRACanvasState2D.Create(AMatrix: TAffineMatrix; 525 AClipMask: TBGRACustomBitmap );557 AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean); 526 558 begin 527 559 strokeColor := BGRABlack; … … 536 568 537 569 lineWidth := 1; 538 lineCap := pecFlat; 539 lineJoin := pjsMiter; 540 lineStyle := DuplicatePenStyle(SolidPenStyle); 541 miterLimit := 10; 570 penStroker := TBGRAPenStroker.Create; 571 penStroker.LineCap := pecFlat; 572 penStroker.JoinStyle := pjsMiter; 573 penStroker.CustomPenStyle := DuplicatePenStyle(SolidPenStyle); 574 penStroker.MiterLimit := 10; 575 penStroker.StrokeMatrix := AffineMatrixIdentity; 542 576 543 577 shadowOffsetX := 0; … … 548 582 549 583 matrix := AMatrix; 550 if AClipMask = nil then 551 clipMask := nil 552 else 553 clipMask := AClipMask.Duplicate; 584 FClipMask := nil; 585 FClipMaskOwned := true; 586 SetClipMask(AClipMask,AClipMaskOwned); 554 587 end; 555 588 556 589 function TBGRACanvasState2D.Duplicate: TBGRACanvasState2D; 557 590 begin 558 result := TBGRACanvasState2D.Create(matrix,clipMask );591 result := TBGRACanvasState2D.Create(matrix,clipMaskReadOnly,false); 559 592 result.strokeColor := strokeColor; 560 593 result.strokeTextureProvider := strokeTextureProvider; … … 568 601 569 602 result.lineWidth := lineWidth; 570 result.lineCap := lineCap; 571 result.lineJoin := lineJoin; 572 result.lineStyle := DuplicatePenStyle(lineStyle); 573 result.miterLimit := miterLimit; 603 result.penStroker.LineCap := penStroker.LineCap; 604 result.penStroker.JoinStyle := penStroker.JoinStyle; 605 result.penStroker.CustomPenStyle := DuplicatePenStyle(penStroker.CustomPenStyle); 606 result.penStroker.MiterLimit := penStroker.MiterLimit; 607 result.penStroker.StrokeMatrix := penStroker.StrokeMatrix; 574 608 575 609 result.shadowOffsetX := shadowOffsetX; … … 582 616 destructor TBGRACanvasState2D.Destroy; 583 617 begin 584 clipMask.Free; 618 if FClipMaskOwned and Assigned(FClipMask) then 619 FClipMask.Free; 620 penStroker.Free; 585 621 inherited Destroy; 622 end; 623 624 procedure TBGRACanvasState2D.SetClipMask(AClipMask: TBGRACustomBitmap; 625 AOwned: boolean); 626 begin 627 if FClipMaskOwned and Assigned(FClipMask) then FreeAndNil(FClipMask); 628 FClipMask := AClipMask; 629 FClipMaskOwned := AOwned; 586 630 end; 587 631 … … 598 642 function TBGRACanvas2D.GetLineCap: string; 599 643 begin 600 case currentState. lineCap of644 case currentState.penStroker.LineCap of 601 645 pecRound: result := 'round'; 602 646 pecSquare: result := 'square'; … … 607 651 function TBGRACanvas2D.GetLineCapLCL: TPenEndCap; 608 652 begin 609 result := currentState. lineCap;653 result := currentState.penStroker.LineCap; 610 654 end; 611 655 612 656 function TBGRACanvas2D.GetlineJoin: string; 613 657 begin 614 case currentState. lineJoinof658 case currentState.penStroker.JoinStyle of 615 659 pjsBevel: result := 'bevel'; 616 660 pjsRound: result := 'round'; … … 621 665 function TBGRACanvas2D.GetlineJoinLCL: TPenJoinStyle; 622 666 begin 623 result := currentState. lineJoin;667 result := currentState.penStroker.JoinStyle; 624 668 end; 625 669 626 670 function TBGRACanvas2D.getLineStyle: TBGRAPenStyle; 627 671 begin 628 result := DuplicatePenStyle(currentState. lineStyle);672 result := DuplicatePenStyle(currentState.penStroker.CustomPenStyle); 629 673 end; 630 674 … … 641 685 function TBGRACanvas2D.GetMiterLimit: single; 642 686 begin 643 result := currentState. miterLimit;687 result := currentState.penStroker.MiterLimit; 644 688 end; 645 689 … … 672 716 begin 673 717 result := currentState.shadowOffsetY; 718 end; 719 720 function TBGRACanvas2D.GetStrokeMatrix: TAffineMatrix; 721 begin 722 result := currentState.penStroker.StrokeMatrix; 674 723 end; 675 724 … … 699 748 end; 700 749 701 function TBGRACanvas2D.GetCurrentPath : ArrayOfTPointF;750 function TBGRACanvas2D.GetCurrentPathAsPoints: ArrayOfTPointF; 702 751 var i: integer; 703 752 begin … … 872 921 begin 873 922 if CompareText(AValue,'round')=0 then 874 currentState. lineCap := pecRound else923 currentState.penStroker.LineCap := pecRound else 875 924 if CompareText(AValue,'square')=0 then 876 currentState. lineCap := pecSquare925 currentState.penStroker.LineCap := pecSquare 877 926 else 878 currentState. lineCap := pecFlat;927 currentState.penStroker.LineCap := pecFlat; 879 928 end; 880 929 881 930 procedure TBGRACanvas2D.SetLineCapLCL(AValue: TPenEndCap); 882 931 begin 883 currentState. lineCap := AValue;932 currentState.penStroker.LineCap := AValue; 884 933 end; 885 934 … … 887 936 begin 888 937 if CompareText(AValue,'round')=0 then 889 currentState. lineJoin:= pjsRound else938 currentState.penStroker.JoinStyle := pjsRound else 890 939 if CompareText(AValue,'bevel')=0 then 891 currentState. lineJoin:= pjsBevel940 currentState.penStroker.JoinStyle := pjsBevel 892 941 else 893 currentState. lineJoin:= pjsMiter;942 currentState.penStroker.JoinStyle := pjsMiter; 894 943 end; 895 944 … … 900 949 if (length(points) = 0) or (surface = nil) then exit; 901 950 If hasShadow then DrawShadow(points,[]); 902 if currentState.clipMask <> nil then951 if currentState.clipMaskReadOnly <> nil then 903 952 begin 904 953 if currentState.fillTextureProvider <> nil then 905 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask ,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)954 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha) 906 955 else 907 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask ,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));956 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor)); 908 957 if self.antialiasing then 909 958 BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true, linearBlend) … … 954 1003 multi := TBGRAMultishapeFiller.Create; 955 1004 multi.FillMode := fmWinding; 956 if currentState.clipMask <> nil then1005 if currentState.clipMaskReadOnly <> nil then 957 1006 begin 958 1007 if currentState.fillTextureProvider <> nil then 959 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask ,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)1008 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha) 960 1009 else 961 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask ,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));1010 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor)); 962 1011 multi.AddPolygon(points, tempScan); 963 1012 end else … … 978 1027 if currentState.lineWidth > 0 then 979 1028 begin 980 contour := ComputeWidePolylinePoints(points,currentState.lineWidth,BGRAPixelTransparent, 981 currentState.lineCap,currentState.lineJoin,currentState.lineStyle,[plAutoCycle],miterLimit); 982 983 if currentState.clipMask <> nil then 1029 contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth); 1030 1031 if currentState.clipMaskReadOnly <> nil then 984 1032 begin 985 1033 if currentState.strokeTextureProvider <> nil then 986 tempScan2 := TBGRATextureMaskScanner.Create(currentState.clipMask ,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)1034 tempScan2 := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha) 987 1035 else 988 tempScan2 := TBGRASolidColorMaskScanner.Create(currentState.clipMask ,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));1036 tempScan2 := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor)); 989 1037 multi.AddPolygon(contour,tempScan); 990 1038 end else … … 1012 1060 procedure TBGRACanvas2D.SetLineJoinLCL(AValue: TPenJoinStyle); 1013 1061 begin 1014 currentState. lineJoin:= AValue;1062 currentState.penStroker.JoinStyle := AValue; 1015 1063 end; 1016 1064 1017 1065 procedure TBGRACanvas2D.lineStyle(const AValue: array of single); 1018 1066 begin 1019 currentState. lineStyle := DuplicatePenStyle(AValue);1067 currentState.penStroker.CustomPenStyle := DuplicatePenStyle(AValue); 1020 1068 end; 1021 1069 … … 1063 1111 procedure TBGRACanvas2D.SetMiterLimit(const AValue: single); 1064 1112 begin 1065 currentState. miterLimit := AValue;1113 currentState.penStroker.MiterLimit := AValue; 1066 1114 end; 1067 1115 … … 1099 1147 begin 1100 1148 currentState.shadowOffsetY := AValue; 1149 end; 1150 1151 procedure TBGRACanvas2D.SetStrokeMatrix(AValue: TAffineMatrix); 1152 begin 1153 currentState.penStroker.strokeMatrix := AValue; 1101 1154 end; 1102 1155 … … 1129 1182 begin 1130 1183 if (length(points)= 0) or (currentState.lineWidth = 0) or (surface = nil) then exit; 1131 contour := ComputeWidePolylinePoints(points,currentState.lineWidth,BGRAPixelTransparent, 1132 currentState.lineCap,currentState.lineJoin,currentState.lineStyle,[plAutoCycle],miterLimit); 1184 contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth); 1133 1185 1134 1186 If hasShadow then DrawShadow(contour,[]); 1135 if currentState.clipMask <> nil then1187 if currentState.clipMaskReadOnly <> nil then 1136 1188 begin 1137 1189 if currentState.strokeTextureProvider <> nil then 1138 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask ,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)1190 tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha) 1139 1191 else 1140 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask ,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));1192 tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor)); 1141 1193 if self.antialiasing then 1142 1194 BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,tempScan,True, linearBlend) … … 1206 1258 1207 1259 maxRect := Types.Rect(0,0,width,height); 1208 if currentState.clipMask <> nil then1260 if currentState.clipMaskReadOnly <> nil then 1209 1261 foundRect := maxRect 1210 1262 else 1211 1263 begin 1212 1264 firstFound := true; 1265 foundRect := EmptyRect; 1213 1266 for i := 0 to high(ofsPts) do 1214 1267 AddPt(ofsPts[i]); … … 1250 1303 end; 1251 1304 end; 1252 if currentState.clipMask <> nil then1253 tempBmp.ApplyMask(currentState.clipMask );1305 if currentState.clipMaskReadOnly <> nil then 1306 tempBmp.ApplyMask(currentState.clipMaskReadOnly); 1254 1307 surface.PutImage(foundRect.Left,foundRect.Top,tempBmp,GetDrawMode,currentState.globalAlpha); 1255 1308 tempBmp.Free; … … 1356 1409 end; 1357 1410 1411 function TBGRACanvas2D.getPoints: ArrayOfTPointF; 1412 begin 1413 result := GetCurrentPathAsPoints; 1414 end; 1415 1416 function TBGRACanvas2D.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; 1417 begin 1418 result := GetCurrentPathAsPoints; 1419 if not IsAffineMatrixIdentity(AMatrix) then 1420 result := AMatrix*result; 1421 end; 1422 1423 function TBGRACanvas2D.getCursor: TBGRACustomPathCursor; 1424 begin 1425 result := nil; 1426 end; 1427 1358 1428 constructor TBGRACanvas2D.Create(ASurface: TBGRACustomBitmap); 1359 1429 begin … … 1363 1433 FLastCoord := EmptyPointF; 1364 1434 FStartCoord := EmptyPointF; 1365 currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil );1435 currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil,true); 1366 1436 pixelCenteredCoordinates := false; 1367 1437 antialiasing := true; … … 1451 1521 procedure TBGRACanvas2D.translate(x, y: single); 1452 1522 begin 1523 if (x = 0) and (y = 0) then exit; 1453 1524 currentState.matrix *= AffineMatrixTranslation(x,y); 1454 1525 end; 1455 1526 1456 procedure TBGRACanvas2D.transform(a, b, c, d, e, f: single); 1457 begin 1458 currentState.matrix *= AffineMatrix(a,c,e,b,d,f); 1527 procedure TBGRACanvas2D.skewx(angleRadCW: single); 1528 begin 1529 currentState.matrix *= AffineMatrixSkewXRad(-angleRadCW); 1530 end; 1531 1532 procedure TBGRACanvas2D.skewy(angleRadCW: single); 1533 begin 1534 currentState.matrix *= AffineMatrixSkewYRad(-angleRadCW); 1535 end; 1536 1537 procedure TBGRACanvas2D.transform(m11,m21, m12,m22, m13,m23: single); 1538 begin 1539 currentState.matrix *= AffineMatrix(m11,m12,m13, 1540 m21,m22,m23); 1459 1541 end; 1460 1542 … … 1464 1546 end; 1465 1547 1466 procedure TBGRACanvas2D.setTransform(a, b, c, d, e, f: single); 1467 begin 1468 currentState.matrix := AffineMatrix(a,c,e,b,d,f); 1548 procedure TBGRACanvas2D.setTransform(m11,m21, m12,m22, m13,m23: single); 1549 begin 1550 currentState.matrix := AffineMatrix(m11,m12,m13, 1551 m21,m22,m23); 1469 1552 end; 1470 1553 … … 1472 1555 begin 1473 1556 currentState.matrix := AffineMatrixIdentity; 1557 end; 1558 1559 procedure TBGRACanvas2D.strokeScale(x, y: single); 1560 begin 1561 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixScale(x,y); 1562 end; 1563 1564 procedure TBGRACanvas2D.strokeSkewx(angleRadCW: single); 1565 begin 1566 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewXRad(-angleRadCW); 1567 end; 1568 1569 procedure TBGRACanvas2D.strokeSkewy(angleRadCW: single); 1570 begin 1571 currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewYRad(-angleRadCW); 1572 end; 1573 1574 procedure TBGRACanvas2D.strokeResetTransform; 1575 begin 1576 currentState.penStroker.strokeMatrix := AffineMatrixIdentity; 1474 1577 end; 1475 1578 … … 1820 1923 arcTo(rx,ry,0,false,false,x+rx,y); 1821 1924 closePath; 1925 end; 1926 1927 procedure TBGRACanvas2D.openedSpline(const pts: array of TPointF; 1928 style: TSplineStyle); 1929 var transf: array of TPointF; 1930 begin 1931 if length(pts)=0 then exit; 1932 transf := ApplyTransform(pts); 1933 transf := BGRAPath.ComputeOpenedSpline(transf,style); 1934 AddPoints(transf); 1935 FLastCoord := pts[high(pts)]; 1936 end; 1937 1938 procedure TBGRACanvas2D.closedSpline(const pts: array of TPointF; 1939 style: TSplineStyle); 1940 var transf: array of TPointF; 1941 begin 1942 if length(pts)=0 then exit; 1943 transf := ApplyTransform(pts); 1944 transf := BGRAPath.ComputeClosedSpline(slice(transf, length(transf)-1),style); 1945 AddPoints(transf); 1946 FLastCoord := pts[high(pts)]; 1822 1947 end; 1823 1948 … … 2056 2181 if FPathPointCount = 0 then 2057 2182 begin 2058 currentState.clipMask .Fill(BGRABlack);2183 currentState.clipMaskReadWrite.Fill(BGRABlack); 2059 2184 exit; 2060 2185 end; 2061 if currentState.clipMask = nil then2062 currentState. clipMask := surface.NewBitmap(width,height,BGRAWhite);2186 if currentState.clipMaskReadOnly = nil then 2187 currentState.SetClipMask(surface.NewBitmap(width,height,BGRAWhite),True); 2063 2188 tempBmp := surface.NewBitmap(width,height,BGRABlack); 2064 2189 if antialiasing then … … 2066 2191 else 2067 2192 tempBmp.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet); 2068 currentState.clipMask .BlendImage(0,0,tempBmp,boDarken);2193 currentState.clipMaskReadWrite.BlendImage(0,0,tempBmp,boDarken); 2069 2194 tempBmp.Free; 2070 2195 end; … … 2073 2198 begin 2074 2199 if FPathPointCount = 0 then exit; 2075 if currentState.clipMask = nil then exit;2200 if currentState.clipMaskReadOnly = nil then exit; 2076 2201 if antialiasing then 2077 currentState.clipMask .FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)2202 currentState.clipMaskReadWrite.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite) 2078 2203 else 2079 currentState.clipMask .FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);2080 if currentState.clipMask .Equals(BGRAWhite) then2081 FreeAndNil(currentState.clipMask);2204 currentState.clipMaskReadWrite.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet); 2205 if currentState.clipMaskReadOnly.Equals(BGRAWhite) then 2206 currentState.SetClipMask(nil,true); 2082 2207 end; 2083 2208 -
GraphicTest/Packages/bgrabitmap/bgracolorint.pas
r472 r494 26 26 operator *(const color1,color2: TColorInt65536): TColorInt65536; 27 27 operator *(const color1: TColorInt65536; factor65536: integer): TColorInt65536; 28 function ColorIntToBGRA(const AColor: TColorInt65536 ): TBGRAPixel;29 function BGRAToColorInt(const AColor: TBGRAPixel ): TColorInt65536;28 function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean = false): TBGRAPixel; 29 function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean = false): TColorInt65536; 30 30 function BGRAToColorIntMultiply(const color1: TBGRAPixel; const color2: TColorInt65536): TColorInt65536; 31 31 … … 184 184 {$endif} 185 185 186 function BGRAToColorInt(const AColor: TBGRAPixel): TColorInt65536; 187 begin 188 result.r := AColor.red shl 8 + AColor.red + (AColor.red shr 7); 189 result.g := AColor.green shl 8 + AColor.green + (AColor.green shr 7); 190 result.b := AColor.blue shl 8 + AColor.blue + (AColor.blue shr 7); 186 function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean): TColorInt65536; 187 begin 188 if AGammaExpansion then 189 begin 190 result.r := GammaExpansionTab[AColor.red] + (AColor.red shr 7); 191 result.g := GammaExpansionTab[AColor.green] + (AColor.green shr 7); 192 result.b := GammaExpansionTab[AColor.blue] + (AColor.blue shr 7); 193 end else 194 begin 195 result.r := AColor.red shl 8 + AColor.red + (AColor.red shr 7); 196 result.g := AColor.green shl 8 + AColor.green + (AColor.green shr 7); 197 result.b := AColor.blue shl 8 + AColor.blue + (AColor.blue shr 7); 198 end; 191 199 result.a := AColor.alpha shl 8 + AColor.alpha+ (AColor.alpha shr 7); 192 200 end; … … 203 211 mov ecx, [Color1] 204 212 205 movzx eax, cl //b 213 mov eax, ecx 214 shr eax, TBGRAPixel_RedShift 215 and eax, 255 216 mov edx, eax 217 shr edx, 7 218 add eax, edx 219 imul [esi] 220 shl edx, 24 221 shr eax, 8 222 or edx, eax 223 mov [ebx], edx 224 225 mov eax, ecx 226 shr eax, TBGRAPixel_GreenShift 227 and eax, 255 228 mov edx, eax 229 shr edx, 7 230 add eax, edx 231 imul [esi+4] 232 shl edx, 24 233 shr eax, 8 234 or edx, eax 235 mov [ebx+4], edx 236 237 mov eax, ecx 238 shr eax, TBGRAPixel_BlueShift 239 and eax, 255 206 240 mov edx, eax 207 241 shr edx, 7 … … 212 246 or edx, eax 213 247 mov [ebx+8], edx 214 shr ecx, 8 215 216 movzx eax, cl //g 217 mov edx, eax 218 shr edx, 7 219 add eax, edx 220 imul [esi+4] 221 shl edx, 24 222 shr eax, 8 223 or edx, eax 224 mov [ebx+4], edx 225 shr ecx, 8 226 227 movzx eax, cl //r 228 mov edx, eax 229 shr edx, 7 230 add eax, edx 231 imul [esi] 232 shl edx, 24 233 shr eax, 8 234 or edx, eax 235 mov [ebx], edx 236 shr ecx, 8 237 238 movzx eax, cl //a 248 249 mov eax, ecx 250 shr eax, TBGRAPixel_AlphaShift 251 and eax, 255 239 252 mov edx, eax 240 253 shr edx, 7 … … 258 271 {$ENDIF} 259 272 260 function ColorIntToBGRA(const AColor: TColorInt65536 ): TBGRAPixel;273 function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean): TBGRAPixel; 261 274 var maxValue,invMaxValue,r,g,b: integer; 262 275 begin … … 280 293 end; 281 294 282 if maxValue <= 65535 then 283 begin 284 if AColor.r <= 0 then result.red := 0 else 285 result.red := AColor.r shr 8 - (AColor.r shr 15); 286 287 if AColor.g <= 0 then result.green := 0 else 288 result.green := AColor.g shr 8 - (AColor.g shr 15); 289 290 if AColor.b <= 0 then result.blue := 0 else 291 result.blue := AColor.b shr 8 - (AColor.b shr 15); 292 exit; 293 end; 294 295 invMaxValue := (1073741824+maxValue-1) div maxValue; 296 maxValue := (maxValue-65535) shr 9; 297 if AColor.r < 0 then r := 0 else 298 r := AColor.r*invMaxValue shr 22 + maxValue; 299 if AColor.g < 0 then g := 0 else 300 g := AColor.g*invMaxValue shr 22 + maxValue; 301 if AColor.b < 0 then b := 0 else 302 b := AColor.b*invMaxValue shr 22 + maxValue; 303 304 if r >= 255 then result.red := 255 else 305 result.red := r; 306 if g >= 255 then result.green := 255 else 307 result.green := g; 308 if b >= 255 then result.blue := 255 else 309 result.blue := b; 295 if AGammaCompression then 296 begin 297 if maxValue <= 65535 then 298 begin 299 if AColor.r <= 0 then result.red := 0 else 300 result.red := GammaCompressionTab[AColor.r - (AColor.r shr 15)]; 301 302 if AColor.g <= 0 then result.green := 0 else 303 result.green :=GammaCompressionTab[AColor.g - (AColor.g shr 15)]; 304 305 if AColor.b <= 0 then result.blue := 0 else 306 result.blue := GammaCompressionTab[AColor.b - (AColor.b shr 15)]; 307 exit; 308 end; 309 310 invMaxValue := (1073741824+maxValue-1) div maxValue; 311 312 maxValue := (maxValue-65535) shr 1; 313 if AColor.r < 0 then r := maxValue else 314 r := AColor.r*invMaxValue shr 14 + maxValue; 315 if AColor.g < 0 then g := maxValue else 316 g := AColor.g*invMaxValue shr 14 + maxValue; 317 if AColor.b < 0 then b := maxValue else 318 b := AColor.b*invMaxValue shr 14 + maxValue; 319 320 if r >= 65535 then result.red := 255 else 321 result.red := GammaCompressionTab[r]; 322 if g >= 65535 then result.green := 255 else 323 result.green := GammaCompressionTab[g]; 324 if b >= 65535 then result.blue := 255 else 325 result.blue := GammaCompressionTab[b]; 326 end else 327 begin 328 if maxValue <= 65535 then 329 begin 330 if AColor.r <= 0 then result.red := 0 else 331 result.red := AColor.r shr 8 - (AColor.r shr 15); 332 333 if AColor.g <= 0 then result.green := 0 else 334 result.green := AColor.g shr 8 - (AColor.g shr 15); 335 336 if AColor.b <= 0 then result.blue := 0 else 337 result.blue := AColor.b shr 8 - (AColor.b shr 15); 338 exit; 339 end; 340 341 invMaxValue := (1073741824+maxValue-1) div maxValue; 342 343 maxValue := (maxValue-65535) shr 9; 344 if AColor.r < 0 then r := maxValue else 345 r := AColor.r*invMaxValue shr 22 + maxValue; 346 if AColor.g < 0 then g := maxValue else 347 g := AColor.g*invMaxValue shr 22 + maxValue; 348 if AColor.b < 0 then b := maxValue else 349 b := AColor.b*invMaxValue shr 22 + maxValue; 350 351 if r >= 255 then result.red := 255 else 352 result.red := r; 353 if g >= 255 then result.green := 255 else 354 result.green := g; 355 if b >= 255 then result.blue := 255 else 356 result.blue := b; 357 end; 310 358 end; 311 359 -
GraphicTest/Packages/bgrabitmap/bgracolorquantization.pas
r472 r494 33 33 { TBGRAColorQuantizer } 34 34 35 TBGRAColorQuantizer = class 35 TBGRAColorQuantizer = class(TBGRACustomColorQuantizer) 36 36 private 37 37 FColors: ArrayOfWeightedColor; … … 40 40 FReductionKeepContrast: boolean; 41 41 FSeparateAlphaChannel: boolean; 42 function GetPalette: TBGRAApproxPalette;43 function GetSourceColor(AIndex: integer): TBGRAPixel;44 function GetSourceColorCount: Integer;45 42 procedure Init(ABox: TBGRAColorBox); 46 procedure SetReductionColorCount(AValue: Integer);47 43 procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; ARedBounds, AGreenBounds, ABlueBounds, AAlphaBounds: TDimensionMinMax; AUniform: boolean); 48 44 procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; AColorBounds, AAlphaBounds: TDimensionMinMax); 45 protected 46 function GetPalette: TBGRACustomApproxPalette; override; 47 function GetSourceColor(AIndex: integer): TBGRAPixel; override; 48 function GetSourceColorCount: Integer; override; 49 function GetReductionColorCount: integer; override; 50 procedure SetReductionColorCount(AValue: Integer); override; 49 51 public 50 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); over load;51 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); over load;52 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); over load;53 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); over load;52 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); override; 53 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); override; 54 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); override; 55 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); override; 54 56 destructor Destroy; override; 55 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); 56 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap); 57 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; 58 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; 59 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string); 60 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string; AFormat: TBGRAImageFormat); 61 procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); 62 property SourceColorCount: Integer read GetSourceColorCount; 63 property SourceColor[AIndex: integer]: TBGRAPixel read GetSourceColor; 64 property ReductionColorCount: Integer read FReductionColorCount write SetReductionColorCount; 65 property ReducedPalette: TBGRAApproxPalette read GetPalette; 57 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); override; 58 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; override; 59 function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm; 60 ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; override; 61 procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; 62 ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); override; 66 63 end; 67 64 … … 71 68 private 72 69 FTree: TBGRAColorTree; 73 FColors: ArrayOf TBGRAPixel;70 FColors: ArrayOfWeightedColor; 74 71 protected 75 72 function GetCount: integer; override; 76 73 function GetColorByIndex(AIndex: integer): TBGRAPixel; override; 74 function GetWeightByIndex(AIndex: Integer): UInt32; override; 77 75 procedure Init(const AColors: ArrayOfTBGRAPixel); 78 76 public … … 99 97 end; 100 98 FLargerOwned: boolean; 99 FTransparentColorIndex: integer; 101 100 protected 102 101 function FindNearestLargerColorIndex(AValue: TBGRAPixel): integer; virtual; … … 107 106 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override; 108 107 function FindNearestColorIndex(AValue: TBGRAPixel): integer; override; 109 end; 110 111 TIsChannelStrictlyGreaterFunc = function (p1,p2 : PBGRAPixel): boolean; 108 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override; 109 end; 110 111 TIsChannelStrictlyGreaterFunc = TBGRAPixelComparer; 112 112 TIsChannelGreaterThanOrEqualToValueFunc = function (p : PBGRAPixel; v: UInt32): boolean; 113 113 … … 122 122 FColors: ArrayOfWeightedColor; 123 123 FDimensions: TColorDimensions; 124 F HasPureTransparentColor: boolean;124 FPureTransparentColorCount: integer; 125 125 function GetApparentInterval(ADimension: TColorDimension): UInt32; 126 126 function GetAverageColor: TBGRAPixel; … … 128 128 function GetBounds(ADimension: TColorDimension): TDimensionMinMax; 129 129 function GetColorCount(ACountPureTransparent: boolean): integer; 130 function GetHasPureTransparentColor: boolean; 130 131 function GetInferiorColor: TBGRAPixel; 131 132 function GetLargestApparentDimension: TColorDimension; … … 135 136 procedure Init(AColors: ArrayOfWeightedColor; AOwner: boolean); 136 137 procedure SortBy(ADimension: TColorDimension); 137 procedure InsertionSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, AMaxIndex: NativeInt);138 procedure QuickSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, AMaxIndex: NativeInt);139 138 function GetMedianIndex(ADimension : TColorDimension; AMinValue, AMaxValue: UInt32): integer; 140 139 public 141 140 constructor Create(ADimensions: TColorDimensions; AColors: ArrayOfWeightedColor; AOwner: boolean); overload; 142 constructor Create(ADimensions: TColorDimensions; AColors: ArrayOfTBGRAPixel); overload;141 constructor Create(ADimensions: TColorDimensions; const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette); overload; 143 142 constructor Create(ADimensions: TColorDimensions; ABounds: TColorBoxBounds); overload; 144 143 constructor Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette); overload; 145 144 constructor Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload; 145 constructor Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption); overload; 146 146 function BoundsContain(AColor: TBGRAPixel): boolean; 147 147 function MedianCut(ADimension: TColorDimension; out SuperiorMiddle: UInt32): TBGRAColorBox; … … 159 159 property TotalWeight: UInt32 read FTotalWeight; 160 160 property ColorCount[ACountPureTransparent: boolean]: integer read GetColorCount; 161 property HasPureTransparentColor: boolean read FHasPureTransparentColor; 161 property HasPureTransparentColor: boolean read GetHasPureTransparentColor; 162 property PureTransparentColorCount: integer read FPureTransparentColorCount; 162 163 end; 163 164 … … 180 181 FAverageColor: TBGRAPixel; 181 182 182 F HasPureTransparentColor: boolean;183 FPureTransparentColorCount: integer; 183 184 FPureTransparentColorIndex: integer; 184 185 FDimension: TColorDimension; … … 187 188 FInferiorBranch, FSuperiorBranch: TBGRAColorTree; 188 189 function GetApproximatedColorCount: integer; 190 function GetHasPureTransparentColor: boolean; 189 191 function GetLeafCount: integer; 190 192 procedure Init(ALeaf: TBGRAColorBox; AOwned: boolean); … … 206 208 function ApproximateColorIndex(AColor: TBGRAPixel): integer; 207 209 function GetAsArrayOfApproximatedColors: ArrayOfTBGRAPixel; 210 function GetAsArrayOfWeightedColors: ArrayOfWeightedColor; 208 211 procedure SplitIntoPalette(ACount: integer; AMethod: TBiggestLeafMethod; 209 212 ALeafColor: TBGRALeafColorMode); … … 212 215 property LeafCount: integer read GetLeafCount; 213 216 property ApproximatedColorCount: integer read GetApproximatedColorCount; 214 property HasPureTransparentColor: boolean read FHasPureTransparentColor; 217 property HasPureTransparentColor: boolean read GetHasPureTransparentColor; 218 property PureTransparentColorCount: integer read FPureTransparentColorCount; 215 219 end; 216 220 … … 224 228 implementation 225 229 226 uses BGRADithering, lazutf8classes, FPimage, FPWriteBMP, FPWritePNG;230 uses BGRADithering, FPimage, FPWriteBMP, BGRAWritePNG; 227 231 228 232 const MedianMinPercentage = 0.2; … … 533 537 534 538 const 535 InsertionSortLimit = 10;536 539 ApproxPaletteDimensions = [cdAlpha,cdRInvG,cdGInvB,cdRInvB,cdRInvGB,cdGInvRB,cdBInvRG,cdRGB]; 537 540 … … 551 554 if AValue.alpha = 0 then 552 555 begin 553 result := -1;556 result := FTransparentColorIndex; 554 557 exit; 555 558 end; 556 diff := BGRAWordDiff(AValue, FColors[0] );559 diff := BGRAWordDiff(AValue, FColors[0].Color); 557 560 result := 0; 558 561 for i := 0 to high(FColors) do 559 562 begin 560 curDiff := BGRAWordDiff(AValue, FColors[i] );563 curDiff := BGRAWordDiff(AValue, FColors[i].Color); 561 564 if curDiff < diff then 562 565 begin … … 570 573 const AColors: ArrayOfTBGRAPixel; ALarger: TBGRACustomApproxPalette; ALargerOwned: boolean); 571 574 var i: integer; 575 largeWeighted: ArrayOfWeightedColor; 572 576 begin 573 577 inherited Create(AColors); 578 FTransparentColorIndex:= -1; 579 for i := 0 to high(FColors) do 580 begin 581 FColors[i].Weight := 0; 582 if FColors[i].Color.alpha = 0 then FTransparentColorIndex:= i; 583 end; 574 584 FLarger := ALarger; 575 585 FLargerOwned := ALargerOwned; 576 setlength(FLargerColors, FLarger.Count); 586 largeWeighted := FLarger.GetAsArrayOfWeightedColor; 587 setlength(FLargerColors, length(largeWeighted)); 577 588 for i := 0 to high(FLargerColors) do 578 589 with FLargerColors[i] do 579 590 begin 580 approxColorIndex := SlowFindNearestColorIndex( FLarger.Color[i]);591 approxColorIndex := SlowFindNearestColorIndex(largeWeighted[i].Color); 581 592 if approxColorIndex = -1 then 582 593 approxColor := BGRAPixelTransparent 583 594 else 584 approxColor := FColors[approxColorIndex]; 595 begin 596 approxColor := FColors[approxColorIndex].Color; 597 inc(FColors[approxColorIndex].Weight, largeWeighted[i].Weight); 598 end; 585 599 end; 586 600 end; … … 614 628 end; 615 629 630 function TBGRAApproxPaletteViaLargerPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor; 631 var 632 i: Integer; 633 begin 634 setlength(result, length(FColors)); 635 for i := 0 to high(FColors) do 636 result[i] := FColors[i]; 637 end; 638 616 639 { TBGRAApproxPalette } 617 640 … … 625 648 if (AIndex < 0) or (AIndex >= length(FColors)) then 626 649 raise ERangeError.Create('Index out of bounds'); 627 result := FColors[AIndex]; 650 result := FColors[AIndex].Color; 651 end; 652 653 function TBGRAApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32; 654 begin 655 if (AIndex < 0) or (AIndex >= length(FColors)) then 656 raise ERangeError.Create('Index out of bounds'); 657 result := FColors[AIndex].Weight; 628 658 end; 629 659 … … 643 673 FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage); 644 674 645 FColors := FTree.GetAsArrayOf ApproximatedColors;675 FColors := FTree.GetAsArrayOfWeightedColors; 646 676 end; 647 677 … … 656 686 FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage); 657 687 658 FColors := FTree.GetAsArrayOf ApproximatedColors;688 FColors := FTree.GetAsArrayOfWeightedColors; 659 689 end; 660 690 … … 662 692 begin 663 693 FTree := AOwnedSplitTree; 664 FColors := FTree.GetAsArrayOf ApproximatedColors;694 FColors := FTree.GetAsArrayOfWeightedColors; 665 695 end; 666 696 … … 679 709 begin 680 710 result := FTree.ApproximateColorIndex(AValue); 681 if (result <> -1) and not (DWord(FColors[result] ) = DWord(AValue)) then result := -1;711 if (result <> -1) and not (DWord(FColors[result].Color) = DWord(AValue)) then result := -1; 682 712 end; 683 713 … … 698 728 setlength(result, length(FColors)); 699 729 for i := 0 to high(result) do 700 result[i] := FColors[i] ;730 result[i] := FColors[i].Color; 701 731 end; 702 732 … … 705 735 i: NativeInt; 706 736 begin 707 setlength(result, length(FColors)); 708 for i := 0 to high(result) do 709 with result[i] do 710 begin 711 Color := FColors[i]; 712 Weight:= 1; 737 if Assigned(FTree) then 738 result := FTree.GetAsArrayOfWeightedColors 739 else 740 begin 741 setlength(result, length(FColors)); 742 for i := 0 to high(result) do 743 result[i] := FColors[i]; 713 744 end; 714 745 end; … … 719 750 begin 720 751 FColors := ABox.FColors; 721 if ABox. FHasPureTransparentColor then752 if ABox.HasPureTransparentColor then 722 753 begin 723 754 setlength(FColors,length(FColors)+1); … … 725 756 begin 726 757 Color := BGRAPixelTransparent; 727 Weight:= 1;758 Weight:= ABox.PureTransparentColorCount; 728 759 end; 729 760 end; … … 853 884 end; 854 885 855 function TBGRAColorQuantizer.GetPalette: TBGRAApproxPalette; 886 function TBGRAColorQuantizer.GetReductionColorCount: integer; 887 begin 888 result := FReductionColorCount; 889 end; 890 891 function TBGRAColorQuantizer.GetPalette: TBGRACustomApproxPalette; 856 892 var 857 893 tree: TBGRAColorTree; 858 894 859 895 procedure MakeTreeErrorDiffusionFriendly; 860 var moreColors: ArrayOf TBGRAPixel;896 var moreColors: ArrayOfWeightedColor; 861 897 box: TBGRAColorBox; 862 898 begin 863 moreColors := tree.GetAsArrayOf ApproximatedColors;899 moreColors := tree.GetAsArrayOfWeightedColors; 864 900 tree.free; 865 box := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],moreColors );901 box := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],moreColors,True); 866 902 tree := TBGRAColorTree.Create(box,True); 867 903 tree.SplitIntoPalette(box.ColorCount[true], blApparentInterval, lcAverage); … … 889 925 bounds[cdBlue] := originalBox.Bounds[cdBlue]; 890 926 bounds[cdAlpha] := originalBox.Bounds[cdAlpha]; 891 if originalBox. FHasPureTransparentColor then bounds[cdAlpha].Minimum := 0;927 if originalBox.HasPureTransparentColor then bounds[cdAlpha].Minimum := 0; 892 928 if FReductionColorCount = 1 then 893 929 begin … … 971 1007 end; 972 1008 973 procedure TBGRAColorQuantizer.ApplyDitheringInplace(974 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap);975 begin976 ApplyDitheringInplace(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));977 end;978 979 1009 function TBGRAColorQuantizer.GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; 980 1010 ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; … … 986 1016 end; 987 1017 988 function TBGRAColorQuantizer.GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; 989 ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; 990 var task: TDitheringTask; 991 begin 992 task := CreateDitheringTask(AAlgorithm, ABitmap, ReducedPalette, FSeparateAlphaChannel); 993 result := task.Execute; 994 task.Free; 995 end; 996 997 procedure TBGRAColorQuantizer.SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 998 AFilenameUTF8: string); 999 begin 1000 SaveBitmapToFile(AAlgorithm, ABitmap, AFilenameUTF8, SuggestImageFormat(AFilenameUTF8)); 1001 end; 1002 1003 procedure TBGRAColorQuantizer.SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 1004 AFilenameUTF8: string; AFormat: TBGRAImageFormat); 1018 function TBGRAColorQuantizer.GetDitheredBitmapIndexedData( 1019 ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 1020 out AScanlineSize: PtrInt): Pointer; 1005 1021 var 1006 stream: TFileStreamUTF8; 1007 begin 1008 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmCreate); 1009 try 1010 SaveBitmapToStream(AAlgorithm, ABitmap, stream, AFormat); 1011 finally 1012 stream.Free; 1013 end; 1022 indexer: TDitheringToIndexedImage; 1023 begin 1024 indexer := TDitheringToIndexedImage.Create(ReducedPalette, FSeparateAlphaChannel, ABitDepth, AByteOrder); 1025 indexer.DefaultTransparentColorIndex := ReducedPalette.IndexOfColor(BGRAPixelTransparent); 1026 AScanlineSize:= indexer.ComputeMinimumScanlineSize(ABitmap.Width); 1027 result := indexer.DitherImage(AAlgorithm, ABitmap, AScanlineSize); 1028 indexer.Free; 1014 1029 end; 1015 1030 … … 1028 1043 writer := CreateBGRAImageWriter(AFormat, hasTransp); 1029 1044 try 1030 if writer is T FPWriterPNG then TFPWriterPNG(writer).Indexed := true else1045 if writer is TBGRAWriterPNG then TBGRAWriterPNG(writer).Indexed := true else 1031 1046 if writer is TFPWriterBMP then 1032 1047 begin … … 1092 1107 begin 1093 1108 index := 0; 1094 if FHasPureTransparentColor then1109 if HasPureTransparentColor then 1095 1110 begin 1096 1111 FPureTransparentColorIndex:= index; … … 1229 1244 begin 1230 1245 CheckColorComputed; 1231 setlength(result,1+byte( FHasPureTransparentColor));1246 setlength(result,1+byte(HasPureTransparentColor)); 1232 1247 idx := 0; 1233 if FHasPureTransparentColor then1248 if HasPureTransparentColor then 1234 1249 begin 1235 1250 result[idx] := BGRAPixelTransparent; … … 1241 1256 a := FInferiorBranch.GetAsArrayOfApproximatedColors; 1242 1257 b := FSuperiorBranch.GetAsArrayOfApproximatedColors; 1243 setlength(result, length(a)+length(b)+byte( FHasPureTransparentColor));1258 setlength(result, length(a)+length(b)+byte(HasPureTransparentColor)); 1244 1259 idx := 0; 1245 if FHasPureTransparentColor then1260 if HasPureTransparentColor then 1246 1261 begin 1247 1262 result[idx] := BGRAPixelTransparent; 1263 inc(idx); 1264 end; 1265 for i := 0 to high(a) do 1266 begin 1267 result[idx] := a[i]; 1268 inc(idx); 1269 end; 1270 for i := 0 to high(b) do 1271 begin 1272 result[idx] := b[i]; 1273 inc(idx); 1274 end; 1275 end; 1276 end; 1277 1278 function TBGRAColorTree.GetAsArrayOfWeightedColors: ArrayOfWeightedColor; 1279 var a,b: ArrayOfWeightedColor; 1280 idx,i: integer; 1281 begin 1282 if IsLeaf then 1283 begin 1284 CheckColorComputed; 1285 setlength(result,1+byte(HasPureTransparentColor)); 1286 idx := 0; 1287 if HasPureTransparentColor then 1288 begin 1289 result[idx].Color := BGRAPixelTransparent; 1290 result[idx].Weight := PureTransparentColorCount; 1291 inc(idx); 1292 end; 1293 result[idx].Color := FLeafColor; 1294 result[idx].Weight := Weight; 1295 end else 1296 begin 1297 a := FInferiorBranch.GetAsArrayOfWeightedColors; 1298 b := FSuperiorBranch.GetAsArrayOfWeightedColors; 1299 setlength(result, length(a)+length(b)+byte(HasPureTransparentColor)); 1300 idx := 0; 1301 if HasPureTransparentColor then 1302 begin 1303 result[idx].Color := BGRAPixelTransparent; 1304 result[idx].Weight := PureTransparentColorCount; 1248 1305 inc(idx); 1249 1306 end; … … 1328 1385 if Assigned(FSuperiorBranch) then result += FSuperiorBranch.ApproximatedColorCount; 1329 1386 end; 1330 if FHasPureTransparentColor then inc(result); 1387 if HasPureTransparentColor then inc(result); 1388 end; 1389 1390 function TBGRAColorTree.GetHasPureTransparentColor: boolean; 1391 begin 1392 result := FPureTransparentColorCount > 0; 1331 1393 end; 1332 1394 … … 1347 1409 FMaxBorder[c] := true; 1348 1410 end; 1349 F HasPureTransparentColor:= FLeaf.HasPureTransparentColor;1411 FPureTransparentColorCount:= FLeaf.PureTransparentColorCount; 1350 1412 FPureTransparentColorIndex:= -1; 1351 1413 end; … … 1406 1468 else 1407 1469 result := supLeaf; 1408 blMix:1470 else{blMix:} 1409 1471 if (sqrt(infLeaf.Weight/FWeight)*(infLeaf.LargestApparentInterval/LargestApparentInterval) >= 1410 1472 sqrt(supLeaf.Weight/FWeight)*(supLeaf.LargestApparentInterval/LargestApparentInterval) ) then … … 1533 1595 begin 1534 1596 result := length(FColors); 1535 if ACountPureTransparent and FHasPureTransparentColor then inc(result); 1597 if ACountPureTransparent and HasPureTransparentColor then inc(result); 1598 end; 1599 1600 function TBGRAColorBox.GetHasPureTransparentColor: boolean; 1601 begin 1602 result := FPureTransparentColorCount > 0; 1536 1603 end; 1537 1604 … … 1667 1734 c: TColorDimension; 1668 1735 begin 1669 F HasPureTransparentColor:= false;1736 FPureTransparentColorCount:= 0; 1670 1737 FTotalWeight:= 0; 1671 1738 for c := low(TColorDimension) to high(TColorDimension) do … … 1697 1764 inc(idx); 1698 1765 end else 1699 FHasPureTransparentColor:= true;1766 inc(FPureTransparentColorCount, Weight); 1700 1767 end; 1701 1768 setlength(FColors,idx); … … 1707 1774 comparer := GetPixelStrictComparer(ADimension); 1708 1775 if comparer = nil then exit; 1709 if Length(FColors) > InsertionSortLimit then 1710 QuickSort(comparer,0,high(FColors)) 1711 else 1712 InsertionSort(comparer,0,high(FColors)); 1713 end; 1714 1715 procedure TBGRAColorBox.InsertionSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, 1716 AMaxIndex: NativeInt); 1717 var i,j,insertPos: NativeInt; 1718 compared: TBGRAWeightedPaletteEntry; 1719 begin 1720 for i := AMinIndex+1 to AMaxIndex do 1721 begin 1722 insertPos := i; 1723 compared := FColors[i]; 1724 while (insertPos > AMinIndex) and AComparer(@FColors[insertPos-1].Color,@compared.Color) do 1725 dec(insertPos); 1726 if insertPos <> i then 1727 begin 1728 for j := i downto insertPos+1 do 1729 FColors[j] := FColors[j-1]; 1730 FColors[insertPos] := compared; 1731 end; 1732 end; 1733 end; 1734 1735 procedure TBGRAColorBox.QuickSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, 1736 AMaxIndex: NativeInt); 1737 var Pivot: TBGRAPixel; 1738 CurMin,CurMax,i : NativeInt; 1739 1740 procedure Swap(a,b: NativeInt); 1741 var Temp: TBGRAWeightedPaletteEntry; 1742 begin 1743 if a = b then exit; 1744 Temp := FColors[a]; 1745 FColors[a] := FColors[b]; 1746 FColors[b] := Temp; 1747 end; 1748 begin 1749 if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then 1750 begin 1751 InsertionSort(AComparer,AMinIndex,AMaxIndex); 1752 exit; 1753 end; 1754 Pivot := FColors[(AMinIndex+AMaxIndex) shr 1].Color; 1755 CurMin := AMinIndex; 1756 CurMax := AMaxIndex; 1757 i := CurMin; 1758 while i < CurMax do 1759 begin 1760 if AComparer(@FColors[i].Color, @Pivot) then 1761 begin 1762 Swap(i, CurMax); 1763 dec(CurMax); 1764 end else 1765 begin 1766 if AComparer(@Pivot, @FColors[i].Color) then 1767 begin 1768 Swap(i, CurMin); 1769 inc(CurMin); 1770 end; 1771 inc(i); 1772 end; 1773 end; 1774 if AComparer(@Pivot, @FColors[i].Color) then 1775 begin 1776 Swap(i, CurMin); 1777 inc(CurMin); 1778 end; 1779 if CurMin > AMinIndex then QuickSort(AComparer,AMinIndex,CurMin); 1780 if CurMax < AMaxIndex then QuickSort(AComparer,CurMax,AMaxIndex); 1776 ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),comparer) 1781 1777 end; 1782 1778 … … 1842 1838 1843 1839 constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; 1844 AColors: ArrayOfTBGRAPixel);1840 const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette); 1845 1841 var weightedColors: ArrayOfWeightedColor; 1846 1842 i: Integer; 1847 1843 begin 1848 FDimensions:= ADimensions; 1849 setlength(weightedColors, length(AColors)); 1850 for i := 0 to high(weightedColors) do 1851 with weightedColors[i] do 1852 begin 1853 color := AColors[i]; 1854 Weight:= 1; 1855 end; 1856 Init(weightedColors,True); 1844 if AAlpha = acFullChannelInPalette then 1845 begin 1846 FDimensions:= ADimensions; 1847 setlength(weightedColors, length(AColors)); 1848 for i := 0 to high(weightedColors) do 1849 with weightedColors[i] do 1850 begin 1851 color := AColors[i]; 1852 Weight:= 1; 1853 end; 1854 Init(weightedColors,True); 1855 end else 1856 Create(ADimensions, @AColors[0], length(AColors), AAlpha); 1857 1857 end; 1858 1858 … … 1862 1862 FBounds := ABounds; 1863 1863 FTotalWeight:= 0; 1864 F HasPureTransparentColor:= false;1864 FPureTransparentColorCount:= 0; 1865 1865 end; 1866 1866 … … 1871 1871 end; 1872 1872 1873 constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); 1873 constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; 1874 ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); 1875 begin 1876 Create(ADimensions, ABitmap.Data, ABitmap.NbPixels, AAlpha); 1877 end; 1878 1879 constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption); 1874 1880 var i,j,prev,idx: integer; 1875 1881 p: PBGRAPixel; 1876 1882 skip: boolean; 1877 1883 alphaMask: DWord; 1878 transp : boolean;1884 transpIndex: integer; 1879 1885 begin 1880 1886 if AAlpha <> acFullChannelInPalette then … … 1883 1889 alphaMask := 0; 1884 1890 FDimensions:= ADimensions; 1885 transp := false;1886 SetLength(FColors,A Bitmap.NbPixels);1891 transpIndex := -1; 1892 SetLength(FColors,ANbPixels); 1887 1893 if length(FColors)>0 then 1888 1894 begin 1889 p := A Bitmap.Data;1895 p := AColors; 1890 1896 idx := 0; 1891 for i := 0 to A Bitmap.NbPixels-1 do1897 for i := 0 to ANbPixels-1 do 1892 1898 begin 1893 1899 if (p^.alpha = 0) or ((AAlpha = acTransparentEntry) and (p^.alpha < 128)) then 1894 1900 begin 1895 1901 skip := true; 1896 if not transp and not(AAlpha = acIgnore) then1902 if not (AAlpha = acIgnore) then 1897 1903 begin 1898 with FColors[idx] do1904 if (transpIndex=-1) then 1899 1905 begin 1900 Color := BGRAPixelTransparent; 1901 Weight:= 1; 1902 end; 1903 transp := true; 1904 inc(idx); 1906 transpIndex := idx; 1907 with FColors[idx] do 1908 begin 1909 Color := BGRAPixelTransparent; 1910 Weight:= 1; 1911 end; 1912 inc(idx); 1913 end else 1914 inc(FColors[transpIndex].Weight); 1905 1915 end; 1906 1916 if (p^.alpha = 0) then … … 1938 1948 setLength(FColors, idx); 1939 1949 1940 QuickSort(@IsDWordGreater,0,high(FColors));1950 ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),@IsDWordGreater); 1941 1951 prev := 0; 1942 1952 for i := 1 to high(FColors) do … … 2006 2016 var i,idx: integer; 2007 2017 begin 2008 if AIncludePureTransparent and FHasPureTransparentColor then2018 if AIncludePureTransparent and HasPureTransparentColor then 2009 2019 begin 2010 2020 setlength(result, length(FColors)+1); -
GraphicTest/Packages/bgrabitmap/bgracompressablebitmap.pas
r472 r494 27 27 28 28 uses 29 Classes, SysUtils, BGRABitmap , GraphType, zstream;29 Classes, SysUtils, BGRABitmapTypes, BGRABitmap, zstream; 30 30 31 31 type … … 69 69 implementation 70 70 71 uses BGRABitmapTypes;72 73 71 // size of each chunk treated by Compress function 74 72 const maxPartSize = 524288; … … 113 111 FUncompressedData.Read(UsedPart.Data^,UsedPart.NbPixels*Sizeof(TBGRAPixel)); 114 112 if UsedPart.LineOrder <> FLineOrder then UsedPart.VerticalFlip; 113 If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue; 115 114 result.PutImage(FBounds.Left,FBounds.Top,UsedPart,dmSet); 116 115 UsedPart.Free; 117 116 end; 118 117 end else 118 begin 119 119 FUncompressedData.Read(result.Data^,result.NbPixels*Sizeof(TBGRAPixel)); 120 If TBGRAPixel_RGBAOrder then result.SwapRedBlue; 121 end; 120 122 end; 121 123 … … 305 307 begin 306 308 UsedPart := Source.GetPart(FBounds) as TBGRABitmap; 309 If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue; 307 310 FUncompressedData.Write(UsedPart.Data^,NbUsedPixels*Sizeof(TBGRAPixel)); 308 311 FLineOrder := UsedPart.LineOrder; … … 310 313 end else 311 314 begin 315 If TBGRAPixel_RGBAOrder then Source.SwapRedBlue; 312 316 FUncompressedData.Write(Source.Data^,Source.NbPixels*Sizeof(TBGRAPixel)); 317 If TBGRAPixel_RGBAOrder then Source.SwapRedBlue; 313 318 FLineOrder := Source.LineOrder; 314 319 end; -
GraphicTest/Packages/bgrabitmap/bgracoordpool3d.pas
r472 r494 212 212 P := PBGRACoordData3D(FPoolData.Data); 213 213 {$IFDEF CPUI386} 214 {$IFDEF BGRASSE_AVAILABLE} 214 215 {$asmmode intel} 215 216 if UseSSE then … … 304 305 else 305 306 {$ENDIF} 307 {$ENDIF} 306 308 begin 307 309 i := UsedCapacity; … … 385 387 P := PBGRANormalData3D(FPoolData.Data); 386 388 {$IFDEF CPUI386} 389 {$IFDEF BGRASSE_AVAILABLE} 387 390 {$asmmode intel} 388 391 if UseSSE then … … 414 417 else 415 418 {$ENDIF} 419 {$ENDIF} 416 420 begin 417 421 i := UsedCapacity; -
GraphicTest/Packages/bgrabitmap/bgradefaultbitmap.pas
r472 r494 33 33 34 34 uses 35 Classes, SysUtils, Types, FPImage, Graphics, BGRABitmapTypes, GraphType, FPImgCanv,36 BGRACanvas, BGRACanvas2D, FPWritePng, BGRAArrow, BGRAPen;35 SysUtils, Classes, Types, FPImage, BGRAGraphics, BGRABitmapTypes, FPImgCanv, 36 BGRACanvas, BGRACanvas2D, BGRAArrow, BGRAPen, BGRATransform; 37 37 38 38 type 39 TBGRAPtrBitmap = class; 40 {=== TBGRABitmap reference ===} 39 41 { TBGRADefaultBitmap } 40 42 {* This class is the base for all ''TBGRABitmap'' classes. It implements most 43 function to the exception from implementations specific to the 44 widgetset }{ in the doc, it is presented as 45 TBGRABitmap = class(TBGRACustomBitmap) 46 } 41 47 TBGRADefaultBitmap = class(TBGRACustomBitmap) 42 48 private … … 47 53 function CheckVertLineBounds(var x, y, y2: int32or64; out delta: int32or64): boolean; inline; 48 54 function CheckRectBounds(var x,y,x2,y2: integer; minsize: integer): boolean; inline; 49 function CheckClippedRectBounds(var x,y,x2,y2: integer): boolean; inline;50 55 function CheckAntialiasRectBounds(var x,y,x2,y2: single; w: single): boolean; 51 56 function GetCanvasBGRA: TBGRACanvas; 52 57 function GetCanvas2D: TBGRACanvas2D; 58 procedure GradientFillDithered(x, y, x2, y2: integer; c1, c2: TBGRAPixel; 59 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 60 gammaColorCorrection: boolean = True; Sinus: Boolean=False; 61 ditherAlgo: TDitheringAlgorithm = daFloydSteinberg); 62 procedure GradientFillDithered(x, y, x2, y2: integer; gradient: TBGRACustomGradient; 63 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 64 Sinus: Boolean=False; 65 ditherAlgo: TDitheringAlgorithm = daFloydSteinberg); 53 66 protected 54 67 FRefCount: integer; //reference counter (not related to interface reference counter) … … 57 70 FData: PBGRAPixel; //pointer to pixels 58 71 FWidth, FHeight, FNbPixels: integer; //dimensions 72 FScanWidth, FScanHeight: integer; //possibility to reduce the zone being scanned 59 73 FDataModified: boolean; //if data image has changed so TBitmap should be updated 60 74 FLineOrder: TRawImageLineOrder; … … 65 79 FScanCurX,FScanCurY: integer; //current scan coordinates 66 80 67 // LCLbitmap object81 //GUI bitmap object 68 82 FBitmap: TBitmap; 69 83 FBitmapModified: boolean; //if TBitmap has changed so pixel data should be updated … … 86 100 FFontRenderer: TBGRACustomFontRenderer; 87 101 88 { Pen style can be defined by PenStyle property of by CustomPenStyle property. 89 When PenStyle property is assigned, CustomPenStyle property is assigned the actual 90 pen pattern. } 91 FCustomPenStyle: TBGRAPenStyle; 92 FPenStyle: TPenStyle; 93 FArrow: TBGRAArrow; 94 FLineCap: TPenEndCap; 102 FPenStroker: TBGRAPenStroker; 95 103 96 104 //Pixel data … … 98 106 function GetScanLine(y: integer): PBGRAPixel; override; //don't forget to call InvalidateBitmap after modifications 99 107 function LoadFromRawImage(ARawImage: TRawImage; DefaultOpacity: byte; 100 AlwaysReplaceAlpha: boolean = False; RaiseErrorOnInvalidPixelFormat: boolean = True): boolean; 108 AlwaysReplaceAlpha: boolean = False; RaiseErrorOnInvalidPixelFormat: boolean = True): boolean; virtual; abstract; 101 109 function GetDataPtr: PBGRAPixel; override; 102 110 procedure ClearTransparentPixels; override; 103 111 function GetScanlineFast(y: integer): PBGRAPixel; inline; 104 112 function GetLineOrder: TRawImageLineOrder; override; 113 procedure SetLineOrder(AValue: TRawImageLineOrder); virtual; 105 114 function GetNbPixels: integer; override; 106 115 function GetWidth: integer; override; 107 116 function GetHeight: integer; override; 108 117 109 // LCLbitmap object118 //GUI bitmap object 110 119 function GetBitmap: TBitmap; override; 111 120 function GetCanvas: TCanvas; override; … … 116 125 function GetCanvasAlphaCorrection: boolean; override; 117 126 procedure SetCanvasAlphaCorrection(const AValue: boolean); override; 127 procedure DoLoadFromBitmap; virtual; 118 128 119 129 //FreePascal drawing routines … … 125 135 procedure ReallocData; virtual; 126 136 procedure FreeData; virtual; 127 128 procedure RebuildBitmap; virtual; 137 function CreatePtrBitmap(AWidth,AHeight: integer; AData: PBGRAPixel): TBGRAPtrBitmap; virtual; 138 139 procedure RebuildBitmap; virtual; abstract; 129 140 procedure FreeBitmap; virtual; 130 141 … … 144 155 function GetAverageColor: TColor; override; 145 156 function GetAveragePixel: TBGRAPixel; override; 146 function CreateAdaptedPngWriter: TFPWriterPNG;147 157 148 158 //drawing 159 function GetPenJoinStyle: TPenJoinStyle; override; 160 procedure SetPenJoinStyle(const AValue: TPenJoinStyle); override; 161 function GetPenMiterLimit: single; override; 162 procedure SetPenMiterLimit(const AValue: single); override; 149 163 function GetCustomPenStyle: TBGRAPenStyle; override; 150 164 procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); override; … … 153 167 function GetLineCap: TPenEndCap; override; 154 168 procedure SetLineCap(AValue: TPenEndCap); override; 169 function GetPenStroker: TBGRACustomPenStroker; override; 170 155 171 function GetArrowEndSize: TPointF; override; 156 172 function GetArrowStartSize: TPointF; override; … … 173 189 function GetFontRenderer: TBGRACustomFontRenderer; override; 174 190 procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); override; 191 function CreateDefaultFontRenderer: TBGRACustomFontRenderer; virtual; abstract; 192 function GetFontAnchorVerticalOffset: single; 193 function GetFontAnchorRotatedOffset: TPointF; 194 function GetFontAnchorRotatedOffset(ACustomOrientation: integer): TPointF; 175 195 176 196 function GetClipRect: TRect; override; … … 179 199 function InternalGetPixelCycle256(ix,iy: int32or64; iFactX,iFactY: int32or64): TBGRAPixel; 180 200 function InternalGetPixel256(ix,iy: int32or64; iFactX,iFactY: int32or64; smoothBorder: boolean): TBGRAPixel; 181 function GetPolyLineOption: TBGRAPolyLineOptions;182 201 function GetArrow: TBGRAArrow; 183 procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override; 184 procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override; 202 procedure InternalTextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); 203 204 function CheckClippedRectBounds(var x,y,x2,y2: integer): boolean; 205 procedure InternalArc(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single; 206 AFillColor: TBGRAPixel; AOptions: TArcOptions; ADrawChord: boolean = false; ATexture: IBGRAScanner = nil); override; 185 207 186 208 public 187 {Reference counter functions} 209 {** Provides a canvas with opacity and antialiasing } 210 property CanvasBGRA: TBGRACanvas read GetCanvasBGRA; 211 {** Provides a canvas with 2d transformation and similar to HTML5. } 212 property Canvas2D: TBGRACanvas2D read GetCanvas2D; 213 {** For more properties, see parent class [[TBGRACustomBitmap and IBGRAScanner#TBGRACustomBitmap|TBGRACustomBitmap]] } 214 215 {==== Reference counting ====} 216 217 {** Adds a reference (this reference count is not the same as 218 the reference count of an interface, it changes only by 219 explicit calls } 188 220 function NewReference: TBGRACustomBitmap; 221 {** Free a reference. When the resulting reference count gets 222 to zero, the image is freed. The initial reference count 223 is equal to 1 } 189 224 procedure FreeReference; 225 {** Returns an object with a reference count equal to 1. Duplicate 226 this bitmap if necessary } 190 227 function GetUnique: TBGRACustomBitmap; 191 228 229 {==== Constructors ====} 230 192 231 {------------------------- Constructors from TFPCustomImage----------------} 193 constructor Create(AWidth, AHeight: integer); override; //Creates a new bitmap, initialize properties and bitmap data 194 procedure SetSize(AWidth, AHeight: integer); override; //Can only be called with an existing instance of TBGRABitmap. 195 //Sets the dimensions of an existing TBGRABitmap instance. 232 {** Creates a new bitmap, initialize properties and bitmap data } 233 constructor Create(AWidth, AHeight: integer); override; 234 {** Can only be called with an existing instance of ''TBGRABitmap''. 235 Sets the dimensions of an existing ''TBGRABitmap'' instance. } 236 procedure SetSize(AWidth, AHeight: integer); override; 196 237 197 238 {------------------------- Constructors from TBGRACustomBitmap-------------} 198 constructor Create; override; //Creates an image of width and height equal to zero. 199 constructor Create(ABitmap: TBitmap); override; //Creates an image of dimensions AWidth and AHeight and filled with transparent pixels. 200 constructor Create(AWidth, AHeight: integer; Color: TColor); override; //Creates an image of dimensions AWidth and AHeight and fills it with the opaque color Color. 201 constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override; //Creates an image of dimensions AWidth and AHeight and fills it with Color. 202 203 constructor Create(AFilename: string); override; // Creates an image by loading its content from the file AFilename. 204 // The encoding of the string is the default one for the operating system. 205 // It is recommended to use the next constructor and UTF8 encoding. 206 207 constructor Create(AFilename: string; AIsUtf8: boolean); override; //Creates an image by loading its content from the file AFilename. 208 //The boolean AIsUtf8Filename specifies if UTF8 encoding is assumed for the filename. 209 210 constructor Create(AStream: TStream); override; // Creates an image by loading its content from the stream AStream. 211 destructor Destroy; override; // Free the object and all its resources 239 {** Creates an image of width and height equal to zero. In this case, 240 ''Data'' = '''nil''' } 241 constructor Create; override; 242 {** Creates an image by copying the content of a ''TFPCustomImage'' } 243 constructor Create(AFPImage: TFPCustomImage); override; 244 {** Creates an image by copying the content of a ''TBitmap'' } 245 constructor Create(ABitmap: TBitmap; AUseTransparent: boolean = true); override; 246 {** Creates an image of dimensions ''AWidth'' and ''AHeight'' and fills it with the opaque color ''Color'' } 247 constructor Create(AWidth, AHeight: integer; Color: TColor); override; 248 {** Creates an image of dimensions ''AWidth'' and ''AHeight'' and fills it with ''Color'' } 249 constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override; 250 251 {** Creates an image by loading its content from the file ''AFilename''. 252 The encoding of the string is the default one for the operating system. 253 It is recommended to use the next constructor and UTF8 encoding } 254 constructor Create(AFilename: string); override; 255 256 {** Creates an image by loading its content from the file ''AFilename''. 257 The boolean ''AIsUtf8Filename'' specifies if UTF8 encoding is assumed 258 for the filename } 259 constructor Create(AFilename: string; AIsUtf8: boolean); override; 260 constructor Create(AFilename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions); override; 261 262 {** Creates an image by loading its content from the stream ''AStream'' } 263 constructor Create(AStream: TStream); override; 264 {** Free the object and all its resources } 265 destructor Destroy; override; 212 266 213 267 {------------------------- Quasi-constructors -----------------------------} 214 function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap. 215 //Creates a new instance with dimensions AWidth and AHeight, 216 //containing transparent pixels. 217 218 function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap. 219 //Creates a new instance with dimensions AWidth and AHeight, 220 //and fills it with Color. 221 222 function NewBitmap(Filename: string): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap. 223 //Creates a new instance with by loading its content 224 //from the file Filename. The encoding of the string 225 //is the default one for the operating system. 226 227 function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap. 228 //Creates a new instance with by loading its content 229 //from the file Filename. 230 231 procedure SaveToFile(const filename: string); override; 232 procedure SaveToStreamAsPng(Str: TStream); override; 233 procedure Assign(ARaster: TRasterImage); override; overload; 234 procedure Assign(MemBitmap: TBGRACustomBitmap);override; overload; 268 {** Can only be called from an existing instance of ''TBGRABitmap''. 269 Creates a new instance with dimensions ''AWidth'' and ''AHeight'', 270 containing transparent pixels. } 271 function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override; 272 273 {** Can only be called from an existing instance of ''TBGRABitmap''. 274 Creates a new instance with dimensions ''AWidth'' and ''AHeight'', 275 and fills it with Color } 276 function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override; 277 278 {** Can only be called from an existing instance of ''TBGRABitmap''. 279 Creates a new instance with by loading its content 280 from the file ''Filename''. The encoding of the string 281 is the default one for the operating system } 282 function NewBitmap(Filename: string): TBGRACustomBitmap; override; 283 284 {** Can only be called from an existing instance of ''TBGRABitmap''. 285 Creates a new instance with by loading its content 286 from the file ''Filename'' } 287 function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; override; 288 function NewBitmap(Filename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions): TBGRACustomBitmap; override; 289 290 {** Can only be called from an existing instance of ''TBGRABitmap''. 291 Creates an image by copying the content of a ''TFPCustomImage'' } 292 function NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap; override; 293 294 {** Load image from a stream. The specified image reader is used } 295 procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader; AOptions: TBGRALoadingOptions); override; 296 297 {** Assign the content of the specified ''Source''. It can be a ''TBGRACustomBitmap'' or 298 a ''TFPCustomImage'' } 299 procedure Assign(Source: TPersistent); override; 300 procedure Assign(Source: TBitmap; AUseTransparent: boolean); overload; 301 {** Stores the image in the stream without compression nor header } 235 302 procedure Serialize(AStream: TStream); override; 303 {** Reads the image in a stream that was previously serialized } 236 304 procedure Deserialize(AStream: TStream); override; 305 {** Stores an empty image (of size zero) } 237 306 class procedure SerializeEmpty(AStream: TStream); 238 307 239 {Pixel functions} 308 {* Example: 309 <syntaxhighlight> 310 * var bmp1, bmp2: TBGRABitmap; 311 * begin 312 * bmp1 := TBGRABitmap.Create(100,100); 313 * bmp2 := bmp1.NewBitmap(100,100) as TBGRABitmap; 314 * ... 315 * end;</syntaxhighlight> 316 See tutorial 2 on [[BGRABitmap_tutorial_2|how to load and display an image]]. 317 * See reference on [[TBGRACustomBitmap_and_IBGRAScanner#Load_and_save_files|loading and saving files]] } 318 319 {==== Pixel functions ====} 320 {** Checks if the specified point is in the clipping rectangle ''ClipRect'' } 240 321 function PtInClipRect(x, y: int32or64): boolean; inline; 322 {** Sets the pixel by replacing the content at (''x'',''y'') with the specified color. 323 Alpha value is set to 255 (opaque) } 241 324 procedure SetPixel(x, y: int32or64; c: TColor); override; 325 {** Sets the pixel at (''x'',''y'') with the specified content } 242 326 procedure SetPixel(x, y: int32or64; c: TBGRAPixel); override; 327 {** Applies a logical '''xor''' to the content of the pixel with the specified value. 328 This includes the alpha channel, so if you want to preserve the opacity, provide 329 a color ''c'' with alpha channel equal to zero } 243 330 procedure XorPixel(x, y: int32or64; c: TBGRAPixel); override; 331 {** Draws a pixel with gamma correction at (''x'',''y''). Pixel is supplied 332 in sRGB colorspace } 244 333 procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); override; 334 {** Draws a pixel with the specified ''ADrawMode'' at (''x'',''y''). 335 Pixel is supplied in sRGB colorspace. Gamma correction may be applied 336 depending on the draw mode }{inherited 337 procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload; 338 }{** Draws a pixel with gamma correction at (''x'',''y''). Pixel is supplied 339 in gamma expanded colorspace } 245 340 procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); override; 341 {** Draws a pixel without gamma correction at (''x'',''y''). Pixel is supplied 342 in sRGB colorspace } 246 343 procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); override; 344 {** Erase the content of the pixel by reducing the value of the 345 alpha channel. ''alpha'' specifies how much to decrease. 346 If the resulting alpha reaches zero, the content 347 is replaced by ''BGRAPixelTransparent'' } 247 348 procedure ErasePixel(x, y: int32or64; alpha: byte); override; 349 {** Sets the alpha value at (''x'',''y''). If ''alpha'' = 0, the 350 pixel is replaced by ''BGRAPixelTransparent'' } 248 351 procedure AlphaPixel(x, y: int32or64; alpha: byte); override; 352 {** Returns the content of the specified pixel. If it is out of the 353 bounds of the picture, the result is ''BGRAPixelTransparent'' } 249 354 function GetPixel(x, y: int32or64): TBGRAPixel; override; 355 {** Computes the value of the pixel at a floating point coordiante 356 by interpolating the values of the pixels around it. 357 * There is a one pixel wide margin around the pixel where the pixels are 358 still considered inside. If ''smoothBorder'' is set to true, pixel fade 359 to transparent. 360 * If it is more out of the bounds, the result is ''BGRAPixelTransparent''. 361 * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted 362 values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' } 363 function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override; 364 {** Similar to previous ''GetPixel'' function, but the fractional part of 365 the coordinate is supplied with a number from 0 to 255. The actual 366 coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) } 250 367 function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override; 251 function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override; 368 {** Computes the value of the pixel at a floating point coordiante 369 by interpolating the values of the pixels around it. If the pixel 370 is out of bounds, the image is repeated. 371 * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted 372 values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' } 252 373 function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override; 374 {** Similar to previous ''GetPixel'' function, but the fractional part of 375 the coordinate is supplied with a number from 0 to 255. The actual 376 coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) } 377 function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override; 378 {** Computes the value of the pixel at a floating point coordiante 379 by interpolating the values of the pixels around it. ''repeatX'' and 380 ''repeatY'' specifies if the image is to be repeated or not. 381 * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted 382 values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' } 253 383 function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override; 254 function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override; 384 {** Similar to previous ''GetPixel'' function, but the fractional part of 385 the coordinate is supplied with a number from 0 to 255. The actual 386 coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) } 255 387 function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override; 256 388 257 {Line primitives} 389 {==== Drawing lines and polylines (integer coordinates) ====} 390 {* These functions do not take into account current pen style/cap/join. 391 See [[BGRABitmap tutorial 13|coordinate system]]. } 392 393 {** Replaces the content of the pixels at line ''y'' and 394 at columns ''x'' to ''x2'' included, using specified color } 258 395 procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override; 396 {** Applies xor to the pixels at line ''y'' and 397 at columns ''x'' to ''x2'' included, using specified color. 398 This includes the alpha channel, so if you want to preserve the 399 opacity, provide a color ''c'' with alpha channel equal to zero } 259 400 procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override; 401 {** Draws an horizontal line with gamma correction at line ''y'' and 402 at columns ''x'' to ''x2'' included, using specified color } 260 403 procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override; 404 {** Draws an horizontal line with gamma correction at line ''y'' and 405 at columns ''x'' to ''x2'' included, using specified color } 261 406 procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); override; 407 {** Draws an horizontal line with gamma correction at line ''y'' and 408 at columns ''x'' to ''x2'' included, using specified scanner 409 to get the source colors }{inherited 410 procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload; 411 }{** Draws an horizontal line without gamma correction at line ''y'' and 412 at columns ''x'' to ''x2'' included, using specified color } 413 procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override; 414 {** Draws an horizontal line at line ''y'' and 415 at columns ''x'' to ''x2'' included, using specified scanner 416 and the specified ''ADrawMode'' } 262 417 procedure HorizLine(x, y, x2: int32or64; texture: IBGRAScanner; ADrawMode : TDrawMode); override; 263 264 procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override; 418 {** Draws an horizontal line at line ''y'' and 419 at columns ''x'' to ''x2'' included, using specified color 420 and the specified ''ADrawMode'' }{inherited 421 procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload; 422 } 423 {** Replaces the alpha value of the pixels at line ''y'' and 424 at columns ''x'' to ''x2'' included } 265 425 procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); override; 266 procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;267 procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;268 procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;269 procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); override;270 procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;426 {** Draws an horizontal line with gamma correction at line ''y'' and 427 at columns ''x'' to ''x2'' included, using specified color, 428 and with a transparency that increases with the color difference 429 with ''compare''. If the difference is greater than ''maxDiff'', 430 pixels are not changed } 271 431 procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; 272 432 maxDiff: byte); override; 273 433 274 {Shapes} 275 procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); override; 276 procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); override; 277 434 {** Replaces a vertical line at column ''x'' and at row ''y'' to ''y2'' } 435 procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); override; 436 {** Xors a vertical line at column ''x'' and at row ''y'' to ''y2'' } 437 procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); override; 438 {** Draws a vertical line with gamma correction at column ''x'' and at row ''y'' to ''y2'' } 439 procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); override; 440 {** Draws a vertical line without gamma correction at column ''x'' and at row ''y'' to ''y2'' } 441 procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); override; 442 {** Replace alpha values in a vertical line at column ''x'' and at row ''y'' to ''y2'' } 443 procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); override; 444 {** Draws a vertical line with the specified draw mode at column ''x'' and at row ''y'' to ''y2'' }{inherited 445 procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); 446 } 447 448 {** Draws an aliased line from (x1,y1) to (x2,y2) using Bresenham's algorithm 449 ''c'' specifies the color. ''DrawLastPixel'' specifies if (x2,y2) must be drawn. 450 ''ADrawMode'' specifies the mode to use when drawing the pixels } 278 451 procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 452 {** Draws an antialiased line from (x1,y1) to (x2,y2) using an improved version of Bresenham's algorithm 453 ''c'' specifies the color. ''DrawLastPixel'' specifies if (x2,y2) must be drawn } 279 454 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); override; 455 {** Draws an antialiased line with two colors ''c1'' and ''c2'' as dashes of lenght ''dashLen'' } 280 456 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); override; 457 {** Draws an antialiased line with two colors ''c1'' and ''c2'' as dashes of lenght ''dashLen''. 458 ''DashPos'' can be used to specify the start dash position and to retrieve the dash position at the end 459 of the line, in order to draw a polyline with consistent dashes } 281 460 procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); override; 461 462 {** Erases the line from (x1,y1) to (x2,y2) using Bresenham's algorithm. 463 ''alpha'' specifies how much to decrease. If ''alpha'' = 0, nothing 464 is changed and if ''alpha'' = 255, all pixels become transparent. 465 ''DrawListPixel'' specifies if (x2,y2) must be changed } 466 procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override; 467 {** Erases the line from (x1,y1) to (x2,y2) width antialiasing. 468 ''alpha'' specifies how much to decrease. If ''alpha'' = 0, nothing 469 is changed and if ''alpha'' = 255, all pixels become transparent. 470 ''DrawListPixel'' specifies if (x2,y2) must be changed } 471 procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override; 472 473 {==== Drawing lines and polylines (floating point coordinates) ====} 474 {* These functions use the current pen style/cap/join. The parameter ''w'' 475 specifies the width of the line and the base unit for dashes. 476 See [[BGRABitmap tutorial 13|coordinate system]]. } 477 478 {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join } 282 479 procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); override; 480 {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join. 481 ''texture'' specifies the source color to use when filling the line } 283 482 procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); override; 284 procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); override; 285 procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); override; 286 483 {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join. 484 ''Closed'' specifies if the end of the line is closed. If it is not closed, 485 a space is left so that the next line can fit } 486 procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; ClosedCap: boolean); override; 487 {** Same as above with ''texture'' specifying the source color to use when filling the line } 488 procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; ClosedCap: boolean); override; 489 490 {** Draws a polyline using current pen style/cap/join } 287 491 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override; 492 {** Draws a polyline using current pen style/cap/join. 493 ''texture'' specifies the source color to use when filling the line } 288 494 procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override; 289 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); override; 495 {** Draws a polyline using current pen style/cap/join. 496 ''Closed'' specifies if the end of the line is closed. If it is not closed, 497 a space is left so that the next line can fit } 498 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; ClosedCap: boolean); override; 499 procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single; ClosedCap: boolean); override; 500 {** Draws a polyline using current pen style/cap/join. 501 ''fillcolor'' specifies a color to fill the polygon formed by the points } 290 502 procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override; 503 {** Draws a polyline using current pen style/cap/join. 504 The last point considered as a join with the first point if it has 505 the same coordinate } 506 procedure DrawPolyLineAntialiasAutocycle(const points: array of TPointF; c: TBGRAPixel; w: single); override; 507 procedure DrawPolyLineAntialiasAutocycle(const points: array of TPointF; texture: IBGRAScanner; w: single); override; 508 {** Draws a polygon using current pen style/cap/join. 509 The polygon is always closed. You don't need to set the last point 510 to be the same as the first point } 291 511 procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override; 512 {** Draws a polygon using current pen style/cap/join. 513 The polygon is always closed. You don't need to set the last point 514 to be the same as the first point } 292 515 procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override; 516 {** Draws a filled polygon using current pen style/cap/join. 517 The polygon is always closed. You don't need to set the last point 518 to be the same as the first point. } 293 519 procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override; 294 520 295 procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override; 296 procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override; 521 {** Erases a line from (x1,y1) to (x2,y2) using current pen style/cap/join } 297 522 procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); override; 523 {** Erases a line from (x1,y1) to (x2,y2) using current pen style/cap/join. 524 ''Closed'' specifies if the end of the line is closed. If it is not closed, 525 a space is left so that the next line can fit } 298 526 procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); override; 527 {** Erases a polyline using current pen style/cap/join } 299 528 procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); override; 300 529 301 procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); override; 302 procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); override; 303 304 procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override; 305 procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override; 306 procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); override; 307 procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); override; 308 procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); override; 309 310 procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override; 311 procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override; 312 procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True); override; 313 procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); override; 314 procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override; 315 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override; 316 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override; 317 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override; 318 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override; 319 320 procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); override; 321 procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); override; 322 procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel); override; 323 procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); override; 324 procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); override; 325 530 {==== Rectangles (integer coordinates) ====} 531 {* The integer coordinates of rectangles interpreted such that 532 that the bottom/right pixels are not drawn. The width is equal 533 to x2-x, and pixels are drawn from x to x2-1. If x = x2, then nothing 534 is drawn. See [[BGRABitmap tutorial 13|coordinate system]]. 535 * These functions do not take into account current pen style/cap/join. 536 They draw a continuous 1-pixel width border } 537 538 {** Draw a size border of a rectangle, 539 using the specified ''mode'' } 540 procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; 541 {** Draw a filled rectangle with a border of color ''BorderColor'', 542 using the specified ''mode'' } 543 procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); override; 544 {** Fills completely a rectangle, without any border, with the specified ''mode'' } 545 procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; overload; 546 {** Fills completely a rectangle, without any border, with the specified ''texture'' and 547 with the specified ''mode'' } 548 procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint); override; overload; 549 procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm); override; overload; 550 {** Sets the alpha value within the specified rectangle } 551 procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); override; 552 {** Draws a filled round rectangle, with corners having an elliptical diameter of ''DX'' and ''DY'' } 553 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 554 {** Draws a round rectangle, with corners having an elliptical diameter of ''DX'' and ''DY'' } 555 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 556 557 {==== Rectangles and ellipses (floating point coordinates) ====} 558 {* These functions use the current pen style/cap/join. The parameter ''w'' 559 specifies the width of the line and the base unit for dashes 560 * The coordinates are pixel-centered, so that when filling a rectangle, 561 if the supplied values are integers, the border will be half transparent. 562 If you want the border to be completely filled, you can subtract/add 563 0.5 to the coordinates to include the remaining thin border. 564 See [[BGRABitmap tutorial 13|coordinate system]]. } 565 566 {** Draws a rectangle with antialiasing and fills it with color ''back''. 567 Note that the pixel (x2,y2) is included contrary to integer coordinates } 568 procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override; 569 {** Draws a rectangle with antialiasing. Note that the pixel (x2,y2) is 570 included contrary to integer coordinates } 571 procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); override; 572 {** Fills a rectangle with antialiasing. For example (-0.5,-0.5,0.5,0.5) 573 fills one pixel } 574 procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel; pixelCenteredCoordinates: boolean = true); override; 575 {** Fills a rectangle with a texture } 576 procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner; pixelCenteredCoordinates: boolean = true); override; 577 {** Erases the content of a rectangle with antialiasing } 578 procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte; pixelCenteredCoordinates: boolean = true); override; 579 580 {** Draws a rounded rectangle border with antialiasing. The corners have an 581 elliptical radius of ''rx'' and ''ry''. ''options'' specifies how to 582 draw the corners. See [[BGRABitmap Geometry types|geometry types]] } 583 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); override; 584 {** Draws a rounded rectangle border with the specified texture. 585 The corners have an elliptical radius of ''rx'' and ''ry''. 586 ''options'' specifies how to draw the corners. 587 See [[BGRABitmap Geometry types|geometry types]] } 588 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); override; 589 {** Draws and fills a round rectangle } 590 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); override; 591 {** Draws and fills a round rectangle with textures } 592 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); override; 593 594 {** Fills a rounded rectangle with antialiasing. The corners have an 595 elliptical radius of ''rx'' and ''ry''. ''options'' specifies how to 596 draw the corners. See [[BGRABitmap Geometry types|geometry types]] } 597 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override; 598 {** Fills a rounded rectangle with a texture } 599 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override; 600 {** Erases the content of a rounded rectangle with a texture } 601 procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override; 602 603 {** Draws an ellipse with antialising. ''rx'' is the horizontal radius and 604 ''ry'' the vertical radius } 605 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); override; 606 {** Draws an ellipse border with a ''texture'' } 607 procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); override; 608 {** Draws and fills an ellipse } 609 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override; 610 {** Fills an ellipse } 611 procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); override; 612 {** Fills an ellipse with a ''texture'' } 613 procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); override; 614 {** Fills an ellipse with a gradient of color. ''outercolor'' specifies 615 the end color of the gradient on the border of the ellipse and 616 ''innercolor'' the end color of the gradient at the center of the 617 ellipse } 618 procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); override; 619 {** Erases the content of an ellipse } 620 procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); override; 621 622 {==== Polygons and path ====} 326 623 procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); override; 327 624 procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); override; … … 331 628 procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); override; 332 629 630 procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override; 631 procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override; 632 procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); override; 633 procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); override; 634 procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); override; 635 636 procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override; 637 procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override; 638 procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True; ACulling: TFaceCulling = fcNone); override; 639 procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); override; 640 procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACulling: TFaceCulling = fcNone); override; 641 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 642 procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 643 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override; 644 procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override; 645 procedure FillQuadAffineMapping(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; ADrawMode: TDrawMode = dmDrawWithTransparency; AOpacity: byte = 255); override; 646 procedure FillQuadAffineMappingAntialias(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; AOpacity: byte = 255); override; 647 648 procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); override; 649 procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); override; 650 procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel); override; 651 procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); override; 652 procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); override; 653 333 654 procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); override; 334 655 procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); override; … … 338 659 procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); override; 339 660 340 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); override; 341 procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); override; 342 procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override; 343 procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); override; 344 procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); override; 345 procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); override; 346 procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); override; 347 348 procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; 349 procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); override; 350 procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override; 351 procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); override; 352 353 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); override; 354 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); override; 355 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); override; 356 procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); override; 357 358 procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; overload; 359 procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); override; overload; 360 procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); override; 361 procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); override; 362 procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); override; 363 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); override; 364 procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); override; 365 procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); override; 366 procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); override; 367 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; 368 BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 369 procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; 370 BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override; 661 procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); override; 662 procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); override; 663 procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); override; 664 procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); override; 665 procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single); override; 666 procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single); override; 667 procedure FillPath(APath: IBGRAPath; AFillColor: TBGRAPixel); override; 668 procedure FillPath(APath: IBGRAPath; AFillTexture: IBGRAScanner); override; 669 procedure ErasePath(APath: IBGRAPath; alpha: byte); override; 670 671 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); override; 672 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); override; 673 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); override; 674 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); override; 675 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single); override; 676 procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single); override; 677 procedure FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AFillColor: TBGRAPixel); override; 678 procedure FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AFillTexture: IBGRAScanner); override; 679 procedure ErasePath(APath: IBGRAPath; AMatrix: TAffineMatrix; alpha: byte); override; 680 681 procedure ArrowStartAsNone; override; 682 procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override; 683 procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override; 684 procedure ArrowStartAsTail; override; 685 686 procedure ArrowEndAsNone; override; 687 procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override; 688 procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override; 689 procedure ArrowEndAsTail; override; 371 690 372 691 { Draws the UTF8 encoded string, with color c. … … 385 704 procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override; overload; 386 705 706 procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); override; overload; 707 procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); override; overload; 708 387 709 { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect. 388 710 Additional style information is provided by the style parameter. … … 405 727 406 728 function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; override; 407 function ComputeWidePolyline(const points: array of TPointF; w: single; Closed : boolean): ArrayOfTPointF; override;729 function ComputeWidePolyline(const points: array of TPointF; w: single; ClosedCap: boolean): ArrayOfTPointF; override; 408 730 function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; override; 409 731 … … 425 747 procedure AlphaFill(alpha: byte; start, Count: integer); override; 426 748 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); override; 427 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode ); override;749 procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode; AOpacity: byte = 255); override; 428 750 procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); override; 429 751 procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); override; 430 752 procedure ReplaceColor(before, after: TColor); override; 431 753 procedure ReplaceColor(before, after: TBGRAPixel); override; 754 procedure ReplaceColor(ABounds: TRect; before, after: TColor); override; 755 procedure ReplaceColor(ABounds: TRect; before, after: TBGRAPixel); override; 432 756 procedure ReplaceTransparent(after: TBGRAPixel); override; 757 procedure ReplaceTransparent(ABounds: TRect; after: TBGRAPixel); override; 433 758 procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel; 434 759 mode: TFloodfillMode; Tolerance: byte = 0); override; 435 760 procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel; 436 761 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 437 gammaColorCorrection: boolean = True; Sinus: Boolean=False); override; 762 gammaColorCorrection: boolean = True; Sinus: Boolean=False; 763 ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); override; 438 764 procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient; 439 765 gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 440 Sinus: Boolean=False ); override;766 Sinus: Boolean=False; ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); override; 441 767 function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel; 442 768 AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; override; … … 449 775 450 776 {Canvas drawing functions} 451 procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;452 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override;453 procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;454 ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override;455 procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); override;456 777 procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); override; 457 778 procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override; … … 463 784 procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); override; 464 785 procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override; 465 procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); override; 786 procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); override; overload; 787 function GetImageAffineBounds(AMatrix: TAffineMatrix; ASourceBounds: TRect; AClipOutput: boolean = true): TRect; override; overload; 788 function IsAffineRoughlyTranslation(AMatrix: TAffineMatrix; ASourceBounds: TRect): boolean; override; 789 466 790 procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override; 467 791 … … 476 800 function Equals(comp: TBGRACustomBitmap): boolean; override; 477 801 function Equals(comp: TBGRAPixel): boolean; override; 478 function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; override;479 function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; override;480 802 function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; override; 481 803 function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; override; … … 483 805 function Resample(newWidth, newHeight: integer; 484 806 mode: TResampleMode = rmFineResample): TBGRACustomBitmap; override; 485 procedure VerticalFlip(ARect: TRect); override; 486 procedure HorizontalFlip(ARect: TRect); override; 807 procedure VerticalFlip(ARect: TRect); override; overload; 808 procedure HorizontalFlip(ARect: TRect); override; overload; 487 809 function RotateCW: TBGRACustomBitmap; override; 488 810 function RotateCCW: TBGRACustomBitmap; override; … … 491 813 procedure LinearNegative; override; 492 814 procedure LinearNegativeRect(ABounds: TRect); override; 493 procedure InplaceGrayscale; override; 494 procedure InplaceGrayscale(ABounds: TRect); override; 815 procedure InplaceGrayscale(AGammaCorrection: boolean = true); override; 816 procedure InplaceGrayscale(ABounds: TRect; AGammaCorrection: boolean = true); override; 817 procedure InplaceNormalize(AEachChannel: boolean = True); override; 818 procedure InplaceNormalize(ABounds: TRect; AEachChannel: boolean = True); override; 495 819 procedure SwapRedBlue; override; 820 procedure SwapRedBlue(ARect: TRect); override; 496 821 procedure GrayscaleToAlpha; override; 497 822 procedure AlphaToGrayscale; override; 498 procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); override; 823 procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); override; overload; 499 824 procedure ApplyGlobalOpacity(alpha: byte); override; 825 procedure ApplyGlobalOpacity(ABounds: TRect; alpha: byte); override; 500 826 procedure ConvertToLinearRGB; override; 501 827 procedure ConvertFromLinearRGB; override; … … 510 836 function FilterContour: TBGRACustomBitmap; override; 511 837 function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; override; 512 function FilterBlurRadial(radius: integer; 513 blurType: TRadialBlurType): TBGRACustomBitmap; override; 514 function FilterBlurRadial(ABounds: TRect; radius: integer; 515 blurType: TRadialBlurType): TBGRACustomBitmap; override; 516 function FilterBlurMotion(distance: integer; angle: single; 517 oriented: boolean): TBGRACustomBitmap; override; 518 function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single; 519 oriented: boolean): TBGRACustomBitmap; override; 838 function FilterBlurRadial(radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; override; 839 function FilterBlurRadial(ABounds: TRect; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; override; 840 function FilterBlurRadial(radiusX, radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; override; 841 function FilterBlurRadial(ABounds: TRect; radiusX, radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; override; 842 function FilterBlurMotion(distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; override; 843 function FilterBlurMotion(ABounds: TRect; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; override; 520 844 function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; override; 521 845 function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; override; 522 function FilterEmboss(angle: single ): TBGRACustomBitmap; override;523 function FilterEmboss(angle: single; ABounds: TRect ): TBGRACustomBitmap; override;846 function FilterEmboss(angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; override; 847 function FilterEmboss(angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; override; 524 848 function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; override; 525 849 function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; override; … … 530 854 function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; override; 531 855 function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; override; 856 function FilterAffine(AMatrix: TAffineMatrix; correctBlur: boolean = false): TBGRACustomBitmap; override; 532 857 function FilterSphere: TBGRACustomBitmap; override; 533 858 function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; override; … … 535 860 function FilterCylinder: TBGRACustomBitmap; override; 536 861 function FilterPlane: TBGRACustomBitmap; override; 537 538 property CanvasBGRA: TBGRACanvas read GetCanvasBGRA;539 property Canvas2D: TBGRACanvas2D read GetCanvas2D;540 862 end; 541 863 … … 544 866 TBGRAPtrBitmap = class(TBGRADefaultBitmap) 545 867 protected 868 function GetLineOrder: TRawImageLineOrder; override; 869 procedure SetLineOrder(AValue: TRawImageLineOrder); override; 546 870 procedure ReallocData; override; 547 871 procedure FreeData; override; 872 procedure CannotResize; 873 procedure NotImplemented; 874 procedure RebuildBitmap; override; 875 876 function CreateDefaultFontRenderer: TBGRACustomFontRenderer; override; //to override 877 function LoadFromRawImage({%H-}ARawImage: TRawImage; {%H-}DefaultOpacity: byte; 878 {%H-}AlwaysReplaceAlpha: boolean=False; {%H-}RaiseErrorOnInvalidPixelFormat: boolean 879 =True): boolean; override; //to override 548 880 public 549 881 constructor Create(AWidth, AHeight: integer; AData: Pointer); overload; 550 882 function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; override; 551 883 procedure SetDataPtr(AData: Pointer); 552 property LineOrder: TRawImageLineOrder Read FLineOrder Write FLineOrder; 884 property LineOrder: TRawImageLineOrder Read GetLineOrder Write SetLineOrder; 885 886 procedure DataDrawTransparent({%H-}ACanvas: TCanvas; {%H-}Rect: TRect; {%H-}AData: Pointer; 887 {%H-}ALineOrder: TRawImageLineOrder; {%H-}AWidth, {%H-}AHeight: integer); override; //to override 888 procedure DataDrawOpaque({%H-}ACanvas: TCanvas; {%H-}Rect: TRect; {%H-}AData: Pointer; 889 {%H-}ALineOrder: TRawImageLineOrder; {%H-}AWidth, {%H-}AHeight: integer); override; //to override 890 procedure GetImageFromCanvas({%H-}CanvasSource: TCanvas; {%H-}x, {%H-}y: integer); override; //to override 891 892 procedure Assign({%H-}Source: TPersistent); override; 893 procedure TakeScreenshot({%H-}ARect: TRect); override; 894 procedure TakeScreenshotOfPrimaryMonitor; override; 895 procedure LoadFromDevice({%H-}DC: System.THandle); override; 896 procedure LoadFromDevice({%H-}DC: System.THandle; {%H-}ARect: TRect); override; 553 897 end; 554 898 … … 560 904 gammaColorCorrection: boolean = True; Sinus: Boolean=False); 561 905 562 implementation563 564 uses Math, LCLIntf, LCLType,565 BGRABlend, BGRAFilters, BGRAText, BGRATextFX, BGRAGradientScanner,566 BGRAResample, BGRATransform, BGRAPolygon, BGRAPolygonAliased,567 BGRAPath, FPReadPcx, FPWritePcx, FPReadXPM, FPWriteXPM;568 569 906 type 907 908 { TBitmapTracker } 909 570 910 TBitmapTracker = class(TBitmap) 571 911 protected … … 576 916 end; 577 917 918 implementation 919 920 uses Math, BGRAUTF8, BGRABlend, BGRAFilters, BGRAGradientScanner, 921 BGRAResample, BGRAPolygon, BGRAPolygonAliased, 922 BGRAPath, FPReadPcx, FPWritePcx, FPReadXPM, FPWriteXPM, 923 BGRAReadBMP, BGRAReadJpeg, 924 BGRADithering, BGRAFilterScanner; 925 926 { TBitmapTracker } 927 578 928 constructor TBitmapTracker.Create(AUser: TBGRADefaultBitmap); 579 929 begin … … 592 942 593 943 function TBGRADefaultBitmap.CheckEmpty: boolean; 944 const 945 alphaMask = $ff shl TBGRAPixel_AlphaShift; 594 946 var 595 947 i: integer; … … 597 949 begin 598 950 p := Data; 599 for i := NbPixels- 1 downto 0 do600 begin 601 if p^.alpha<> 0 then951 for i := (NbPixels shr 1) - 1 downto 0 do 952 begin 953 if PInt64(p)^ and (alphaMask or (alphaMask shl 32)) <> 0 then 602 954 begin 603 955 Result := False; 604 956 exit; 605 957 end; 606 Inc(p); 958 Inc(p,2); 959 end; 960 if Odd(NbPixels) and (p^.alpha <> 0) then 961 begin 962 Result := false; 963 exit; 607 964 end; 608 965 Result := True; … … 616 973 function TBGRADefaultBitmap.GetCustomPenStyle: TBGRAPenStyle; 617 974 begin 618 result := DuplicatePenStyle(F CustomPenStyle);975 result := DuplicatePenStyle(FPenStroker.CustomPenStyle); 619 976 end; 620 977 … … 628 985 else 629 986 FCanvasOpacity := 0; 987 end; 988 989 procedure TBGRADefaultBitmap.DoLoadFromBitmap; 990 begin 991 //nothing 630 992 end; 631 993 … … 648 1010 procedure TBGRADefaultBitmap.SetCustomPenStyle(const AValue: TBGRAPenStyle); 649 1011 begin 650 F CustomPenStyle := DuplicatePenStyle(AValue);1012 FPenStroker.CustomPenStyle := DuplicatePenStyle(AValue); 651 1013 end; 652 1014 653 1015 procedure TBGRADefaultBitmap.SetPenStyle(const AValue: TPenStyle); 654 1016 begin 655 Case AValue of 656 psSolid: CustomPenStyle := SolidPenStyle; 657 psDash: CustomPenStyle := DashPenStyle; 658 psDot: CustomPenStyle := DotPenStyle; 659 psDashDot: CustomPenStyle := DashDotPenStyle; 660 psDashDotDot: CustomPenStyle := DashDotDotPenStyle; 661 else CustomPenStyle := ClearPenStyle; 662 end; 663 FPenStyle := AValue; 1017 FPenStroker.Style := AValue; 664 1018 end; 665 1019 666 1020 function TBGRADefaultBitmap.GetPenStyle: TPenStyle; 667 1021 begin 668 Result:= FPenSt yle;1022 Result:= FPenStroker.Style; 669 1023 end; 670 1024 671 1025 function TBGRADefaultBitmap.GetLineCap: TPenEndCap; 672 1026 begin 673 result := F LineCap;1027 result := FPenStroker.LineCap; 674 1028 end; 675 1029 676 1030 procedure TBGRADefaultBitmap.SetLineCap(AValue: TPenEndCap); 677 1031 begin 678 if AValue <> FLineCap then 679 begin 680 FLineCap:= AValue; 681 if Assigned(FArrow) then FArrow.LineCap := AValue; 682 end; 1032 if AValue <> FPenStroker.LineCap then 1033 begin 1034 FPenStroker.LineCap := AValue; 1035 if Assigned(FPenStroker.Arrow) then 1036 FPenStroker.Arrow.LineCap := AValue; 1037 end; 1038 end; 1039 1040 function TBGRADefaultBitmap.GetPenStroker: TBGRACustomPenStroker; 1041 begin 1042 result := FPenStroker; 683 1043 end; 684 1044 … … 771 1131 function TBGRADefaultBitmap.GetFontRenderer: TBGRACustomFontRenderer; 772 1132 begin 773 if FFontRenderer = nil then FFontRenderer := TLCLFontRenderer.Create; 1133 if FFontRenderer = nil then FFontRenderer := CreateDefaultFontRenderer; 1134 if FFontRenderer = nil then raise exception.Create('No font renderer'); 774 1135 result := FFontRenderer; 775 1136 result.FontName := FontName; … … 787 1148 end; 788 1149 1150 function TBGRADefaultBitmap.GetFontAnchorVerticalOffset: single; 1151 begin 1152 case FontVerticalAnchor of 1153 fvaTop: result := 0; 1154 fvaCenter: result := FontFullHeight*0.5; 1155 fvaCapLine: result := FontPixelMetric.CapLine; 1156 fvaCapCenter: result := (FontPixelMetric.CapLine+FontPixelMetric.Baseline)*0.5; 1157 fvaXLine: result := FontPixelMetric.xLine; 1158 fvaXCenter: result := (FontPixelMetric.xLine+FontPixelMetric.Baseline)*0.5; 1159 fvaBaseline: result := FontPixelMetric.Baseline; 1160 fvaDescentLine: result := FontPixelMetric.DescentLine; 1161 fvaBottom: result := FontFullHeight; 1162 else 1163 result := 0; 1164 end; 1165 end; 1166 1167 function TBGRADefaultBitmap.GetFontAnchorRotatedOffset: TPointF; 1168 begin 1169 result := GetFontAnchorRotatedOffset(FontOrientation); 1170 end; 1171 1172 function TBGRADefaultBitmap.GetFontAnchorRotatedOffset( 1173 ACustomOrientation: integer): TPointF; 1174 begin 1175 result := PointF(0, GetFontAnchorVerticalOffset); 1176 if ACustomOrientation <> 0 then 1177 result := AffineMatrixRotationDeg(-ACustomOrientation*0.1)*result; 1178 end; 1179 789 1180 { Get scanline without checking bounds nor updated from TBitmap } 790 1181 function TBGRADefaultBitmap.GetScanlineFast(y: integer): PBGRAPixel; inline; … … 894 1285 BGRAClass := TBGRABitmapAny(self.ClassType); 895 1286 Result := BGRAClass.Create(Filename,AIsUtf8); 1287 end; 1288 1289 function TBGRADefaultBitmap.NewBitmap(Filename: string; AIsUtf8: boolean; 1290 AOptions: TBGRALoadingOptions): TBGRACustomBitmap; 1291 var 1292 BGRAClass: TBGRABitmapAny; 1293 begin 1294 BGRAClass := TBGRABitmapAny(self.ClassType); 1295 Result := BGRAClass.Create(Filename,AIsUtf8,AOptions); 1296 end; 1297 1298 function TBGRADefaultBitmap.NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap; 1299 var 1300 BGRAClass: TBGRABitmapAny; 1301 begin 1302 BGRAClass := TBGRABitmapAny(self.ClassType); 1303 Result := BGRAClass.Create(AFPImage); 1304 end; 1305 1306 procedure TBGRADefaultBitmap.LoadFromStream(Str: TStream; 1307 Handler: TFPCustomImageReader; AOptions: TBGRALoadingOptions); 1308 var OldBmpOption: TBMPTransparencyOption; 1309 OldJpegPerf: TJPEGReadPerformance; 1310 begin 1311 if (loBmpAutoOpaque in AOptions) and (Handler is TBGRAReaderBMP) then 1312 begin 1313 OldBmpOption := TBGRAReaderBMP(Handler).TransparencyOption; 1314 TBGRAReaderBMP(Handler).TransparencyOption := toAuto; 1315 inherited LoadFromStream(Str, Handler, AOptions); 1316 TBGRAReaderBMP(Handler).TransparencyOption := OldBmpOption; 1317 end else 1318 if (loJpegQuick in AOptions) and (Handler is TBGRAReaderJpeg) then 1319 begin 1320 OldJpegPerf := TBGRAReaderJpeg(Handler).Performance; 1321 TBGRAReaderJpeg(Handler).Performance := jpBestSpeed; 1322 inherited LoadFromStream(Str, Handler, AOptions); 1323 TBGRAReaderJpeg(Handler).Performance := OldJpegPerf; 1324 end else 1325 inherited LoadFromStream(Str, Handler, AOptions); 896 1326 end; 897 1327 … … 919 1349 FWidth := AWidth; 920 1350 FHeight := AHeight; 1351 FScanWidth := FWidth; 1352 FScanHeight:= FHeight; 921 1353 FNbPixels := AWidth * AHeight; 922 1354 if FNbPixels < 0 then // 2 Go limit … … 936 1368 end; 937 1369 1370 constructor TBGRADefaultBitmap.Create(AFPImage: TFPCustomImage); 1371 begin 1372 Init; 1373 inherited Create(AFPImage.Width, AFPImage.Height); 1374 Assign(AFPImage); 1375 end; 1376 938 1377 { Creates an image of dimensions AWidth and AHeight and filled with transparent pixels. } 939 constructor TBGRADefaultBitmap.Create(ABitmap: TBitmap );1378 constructor TBGRADefaultBitmap.Create(ABitmap: TBitmap; AUseTransparent: boolean); 940 1379 begin 941 1380 Init; 942 1381 inherited Create(ABitmap.Width, ABitmap.Height); 943 Assign(ABitmap );1382 Assign(ABitmap, AUseTransparent); 944 1383 end; 945 1384 … … 973 1412 destructor TBGRADefaultBitmap.Destroy; 974 1413 begin 975 F reeData;1414 FPenStroker.Free; 976 1415 FFontRenderer.Free; 977 FBitmap.Free;978 1416 FCanvasFP.Free; 979 1417 FCanvasBGRA.Free; 980 1418 FCanvas2D.Free; 981 FArrow.Free; 1419 FreeData; 1420 FreeBitmap; 982 1421 inherited Destroy; 983 1422 end; … … 997 1436 end; 998 1437 1438 constructor TBGRADefaultBitmap.Create(AFilename: string; AIsUtf8: boolean; 1439 AOptions: TBGRALoadingOptions); 1440 begin 1441 Init; 1442 inherited Create(0, 0); 1443 if AIsUtf8 then 1444 LoadFromFileUTF8(Afilename, AOptions) 1445 else 1446 LoadFromFile(Afilename, AOptions); 1447 end; 1448 999 1449 { Creates an image by loading its content from the stream AStream. } 1000 1450 constructor TBGRADefaultBitmap.Create(AStream: TStream); … … 1003 1453 inherited Create(0, 0); 1004 1454 LoadFromStream(AStream); 1005 end;1006 1007 procedure TBGRADefaultBitmap.Assign(ARaster: TRasterImage);1008 var TempBmp: TBitmap;1009 ConvertOk: boolean;1010 begin1011 DiscardBitmapChange;1012 SetSize(ARaster.Width, ARaster.Height);1013 if not LoadFromRawImage(ARaster.RawImage,0,False,False) then1014 if ARaster is TBitmap then1015 begin //try to convert1016 TempBmp := TBitmap.Create;1017 TempBmp.Width := ARaster.Width;1018 TempBmp.Height := ARaster.Height;1019 TempBmp.Canvas.Draw(0,0,ARaster);1020 ConvertOk := LoadFromRawImage(TempBmp.RawImage,0,False,False);1021 TempBmp.Free;1022 if not ConvertOk then1023 raise Exception.Create('Unable to convert image to 24 bit');1024 end else1025 raise Exception.Create('Unable to convert image to 24 bit');1026 If Empty then AlphaFill(255); // if bitmap seems to be empty, assume1027 // it is an opaque bitmap without alpha channel1028 end;1029 1030 procedure TBGRADefaultBitmap.Assign(MemBitmap: TBGRACustomBitmap);1031 begin1032 DiscardBitmapChange;1033 SetSize(MemBitmap.Width, MemBitmap.Height);1034 PutImage(0, 0, MemBitmap, dmSet);1035 1455 end; 1036 1456 … … 1042 1462 AStream.Write(lWidth,sizeof(lWidth)); 1043 1463 AStream.Write(lHeight,sizeof(lHeight)); 1464 If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False); 1044 1465 for y := 0 to Height-1 do 1045 1466 AStream.Write(ScanLine[y]^, Width*sizeof(TBGRAPixel)); 1046 end;1047 1048 {$hints off} 1467 If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False); 1468 end; 1469 1049 1470 procedure TBGRADefaultBitmap.Deserialize(AStream: TStream); 1050 1471 var lWidth,lHeight,y: integer; 1051 1472 begin 1052 AStream.Read( lWidth,sizeof(lWidth));1053 AStream.Read( lHeight,sizeof(lHeight));1473 AStream.Read({%H-}lWidth,sizeof(lWidth)); 1474 AStream.Read({%H-}lHeight,sizeof(lHeight)); 1054 1475 lWidth := LEtoN(lWidth); 1055 1476 lHeight := LEtoN(lHeight); … … 1057 1478 for y := 0 to Height-1 do 1058 1479 AStream.Read(ScanLine[y]^, Width*sizeof(TBGRAPixel)); 1059 end; 1060 {$hints on} 1480 If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False); 1481 InvalidateBitmap; 1482 end; 1061 1483 1062 1484 class procedure TBGRADefaultBitmap.SerializeEmpty(AStream: TStream); … … 1068 1490 end; 1069 1491 1070 procedure TBGRADefaultBitmap.SaveToFile(const filename: string); 1071 var 1072 ext: string; 1073 writer: TFPCustomImageWriter; 1074 begin 1075 ext := AnsiLowerCase(ExtractFileExt(filename)); 1076 1077 { When saving to PNG, define some parameters so that the 1078 image be readable by most programs } 1079 if ext = '.png' then 1080 writer := CreateAdaptedPngWriter 1081 else 1082 if (ext='.xpm') and (Width*Height > 32768) then //xpm is slow so avoid big images 1083 raise exception.Create('Image is too big to be saved as XPM') else 1084 writer := nil; 1085 1086 if writer <> nil then //use custom writer if defined 1087 begin 1088 inherited SaveToFile(Filename, writer); 1089 writer.Free; 1090 end 1091 else 1092 inherited SaveToFile(Filename); 1093 end; 1094 1095 procedure TBGRADefaultBitmap.SaveToStreamAsPng(Str: TStream); 1096 var writer: TFPWriterPNG; 1097 begin 1098 writer := CreateAdaptedPngWriter; 1099 SaveToStream(Str,writer); 1100 writer.Free; 1492 procedure TBGRADefaultBitmap.Assign(Source: TPersistent); 1493 var pdest: PBGRAPixel; 1494 x,y: NativeInt; 1495 begin 1496 if Source is TBGRACustomBitmap then 1497 begin 1498 DiscardBitmapChange; 1499 SetSize(TBGRACustomBitmap(Source).Width, TBGRACustomBitmap(Source).Height); 1500 PutImage(0, 0, TBGRACustomBitmap(Source), dmSet); 1501 end else 1502 if Source is TFPCustomImage then 1503 begin 1504 DiscardBitmapChange; 1505 SetSize(TFPCustomImage(Source).Width, TFPCustomImage(Source).Height); 1506 for y := 0 to TFPCustomImage(Source).Height-1 do 1507 begin 1508 pdest := ScanLine[y]; 1509 for x := 0 to TFPCustomImage(Source).Width-1 do 1510 begin 1511 pdest^ := FPColorToBGRA(TFPCustomImage(Source).Colors[x,y]); 1512 inc(pdest); 1513 end; 1514 end; 1515 end else 1516 inherited Assign(Source); 1517 end; 1518 1519 procedure TBGRADefaultBitmap.Assign(Source: TBitmap; AUseTransparent: boolean); 1520 var 1521 transpColor: TBGRAPixel; 1522 begin 1523 Assign(Source); 1524 if AUseTransparent and TBitmap(Source).Transparent then 1525 begin 1526 if TBitmap(Source).TransparentMode = tmFixed then 1527 transpColor := ColorToBGRA(TBitmap(Source).TransparentColor) 1528 else 1529 transpColor := GetPixel(0,Height-1); 1530 ReplaceColor(transpColor, BGRAPixelTransparent); 1531 end; 1101 1532 end; 1102 1533 … … 1132 1563 iFactY: int32or64): TBGRAPixel; 1133 1564 var 1134 ixMod1,ixMod2: int32or64; 1135 w1,w2,w3,w4,alphaW: UInt32or64; 1136 bSum, gSum, rSum: UInt32or64; 1137 aSum: UInt32or64; 1138 1139 c: TBGRAPixel; 1565 ixMod2: int32or64; 1566 pUpLeft, pUpRight, pDownLeft, pDownRight: PBGRAPixel; 1140 1567 scan: PBGRAPixel; 1141 1568 begin 1142 w4 := (iFactX*iFactY+127) shr 8;1143 w3 := iFactY-w4;1144 w1 := cardinal(256-iFactX)-w3;1145 w2 := iFactX-w4;1146 1147 rSum := 0;1148 gSum := 0;1149 bSum := 0;1150 aSum := 0;1151 1152 1569 scan := GetScanlineFast(iy); 1153 1570 1154 ixMod1 := ix; 1155 c := (scan + ix)^; 1156 alphaW := c.alpha * w1; 1157 aSum += alphaW; 1158 1159 rSum += c.red * alphaW; 1160 gSum += c.green * alphaW; 1161 bSum += c.blue * alphaW; 1162 1571 pUpLeft := (scan + ix); 1163 1572 ixMod2 := ix+1; 1164 1573 if ixMod2=Width then ixMod2 := 0; 1165 c := (scan + ixMod2)^; 1166 alphaW := c.alpha * w2; 1167 aSum += alphaW; 1168 1169 rSum += c.red * alphaW; 1170 gSum += c.green * alphaW; 1171 bSum += c.blue * alphaW; 1574 pUpRight := (scan + ixMod2); 1172 1575 1173 1576 Inc(iy); 1174 1577 if iy = Height then iy := 0; 1175 1578 scan := GetScanlineFast(iy); 1176 1177 c := (scan + ixMod2)^; 1178 alphaW := c.alpha * w4; 1179 aSum += alphaW; 1180 1181 rSum += c.red * alphaW; 1182 gSum += c.green * alphaW; 1183 bSum += c.blue * alphaW; 1184 1185 c := (scan + ixMod1)^; 1186 alphaW := c.alpha * w3; 1187 aSum += alphaW; 1188 1189 rSum += c.red * alphaW; 1190 gSum += c.green * alphaW; 1191 bSum += c.blue * alphaW; 1192 1193 if (aSum < 128) then 1194 Result := BGRAPixelTransparent 1195 else 1196 begin 1197 Result.red := (rSum + aSum shr 1) div aSum; 1198 Result.green := (gSum + aSum shr 1) div aSum; 1199 Result.blue := (bSum + aSum shr 1) div aSum; 1200 Result.alpha := (aSum + 128) shr 8; 1201 end; 1579 pDownLeft := (scan + ix); 1580 pDownRight := (scan + ixMod2); 1581 1582 InterpolateBilinear(pUpLeft, pUpRight, pDownLeft, 1583 pDownRight, iFactX, iFactY, @result); 1202 1584 end; 1203 1585 … … 1205 1587 iFactY: int32or64; smoothBorder: boolean): TBGRAPixel; 1206 1588 var 1207 w1,w2,w3,w4,alphaW: cardinal; 1208 rSum, gSum, bSum: cardinal; //rgbDiv = aSum 1209 aSum, aDiv: cardinal; 1210 c: TBGRAPixel; 1589 pUpLeft, pUpRight, pDownLeft, pDownRight: PBGRAPixel; 1211 1590 scan: PBGRAPixel; 1212 1591 begin 1213 rSum := 0;1214 gSum := 0;1215 bSum := 0;1216 aSum := 0;1217 aDiv := 0;1218 1219 w4 := (iFactX*iFactY+127) shr 8;1220 w3 := iFactY-w4;1221 {$PUSH}{$HINTS OFF}1222 w1 := (256-iFactX)-w3;1223 {$POP}1224 w2 := iFactX-w4;1225 1226 { For each pixel around the coordinate, compute1227 the weight for it and multiply values by it before1228 adding to the sum }1229 1592 if (iy >= 0) and (iy < Height) then 1230 1593 begin … … 1232 1595 1233 1596 if (ix >= 0) and (ix < Width) then 1234 begin 1235 c := (scan + ix)^; 1236 alphaW := c.alpha * w1; 1237 aDiv += w1; 1238 aSum += alphaW; 1239 rSum += c.red * alphaW; 1240 gSum += c.green * alphaW; 1241 bSum += c.blue * alphaW; 1242 end; 1243 1244 Inc(ix); 1597 pUpLeft := scan+ix 1598 else if smoothBorder then 1599 pUpLeft := @BGRAPixelTransparent 1600 else 1601 pUpLeft := nil; 1602 1603 if (ix+1 >= 0) and (ix+1 < Width) then 1604 pUpRight := scan+(ix+1) 1605 else if smoothBorder then 1606 pUpRight := @BGRAPixelTransparent 1607 else 1608 pUpRight := nil; 1609 end else 1610 if smoothBorder then 1611 begin 1612 pUpLeft := @BGRAPixelTransparent; 1613 pUpRight := @BGRAPixelTransparent; 1614 end else 1615 begin 1616 pUpLeft := nil; 1617 pUpRight := nil; 1618 end; 1619 1620 if (iy+1 >= 0) and (iy+1 < Height) then 1621 begin 1622 scan := GetScanlineFast(iy+1); 1623 1245 1624 if (ix >= 0) and (ix < Width) then 1246 begin 1247 c := (scan + ix)^; 1248 alphaW := c.alpha * w2; 1249 aDiv += w2; 1250 aSum += alphaW; 1251 rSum += c.red * alphaW; 1252 gSum += c.green * alphaW; 1253 bSum += c.blue * alphaW; 1254 end; 1255 end 1256 else 1257 begin 1258 Inc(ix); 1259 end; 1260 1261 Inc(iy); 1262 if (iy >= 0) and (iy < Height) then 1263 begin 1264 scan := GetScanlineFast(iy); 1265 1266 if (ix >= 0) and (ix < Width) then 1267 begin 1268 c := (scan + ix)^; 1269 alphaW := c.alpha * w4; 1270 aDiv += w4; 1271 aSum += alphaW; 1272 rSum += c.red * alphaW; 1273 gSum += c.green * alphaW; 1274 bSum += c.blue * alphaW; 1275 end; 1276 1277 Dec(ix); 1278 if (ix >= 0) and (ix < Width) then 1279 begin 1280 c := (scan + ix)^; 1281 alphaW := c.alpha * w3; 1282 aDiv += w3; 1283 aSum += alphaW; 1284 rSum += c.red * alphaW; 1285 gSum += c.green * alphaW; 1286 bSum += c.blue * alphaW; 1287 end; 1288 end; 1289 1290 if aSum < 128 then //if there is no alpha 1291 Result := BGRAPixelTransparent 1292 else 1293 begin 1294 Result.red := (rSum + aSum shr 1) div aSum; 1295 Result.green := (gSum + aSum shr 1) div aSum; 1296 Result.blue := (bSum + aSum shr 1) div aSum; 1297 if smoothBorder or (aDiv = 256) then 1298 Result.alpha := (aSum + 128) shr 8 1625 pDownLeft := scan+ix 1626 else if smoothBorder then 1627 pDownLeft := @BGRAPixelTransparent 1299 1628 else 1300 Result.alpha := (aSum + aDiv shr 1) div aDiv; 1301 end; 1302 end; 1303 1304 function TBGRADefaultBitmap.GetPolyLineOption: TBGRAPolyLineOptions; 1305 begin 1306 result := []; 1307 if Assigned(FArrow) and FArrow.IsStartDefined then result += [plNoStartCap]; 1308 if Assigned(FArrow) and FArrow.IsEndDefined then result += [plNoEndCap]; 1629 pDownLeft := nil; 1630 1631 if (ix+1 >= 0) and (ix+1 < Width) then 1632 pDownRight := scan+(ix+1) 1633 else if smoothBorder then 1634 pDownRight := @BGRAPixelTransparent 1635 else 1636 pDownRight := nil; 1637 end else 1638 if smoothBorder then 1639 begin 1640 pDownLeft := @BGRAPixelTransparent; 1641 pDownRight := @BGRAPixelTransparent; 1642 end else 1643 begin 1644 pDownLeft := nil; 1645 pDownRight := nil; 1646 end; 1647 1648 InterpolateBilinear(pUpLeft, pUpRight, pDownLeft, 1649 pDownRight, iFactX, iFactY, @result); 1309 1650 end; 1310 1651 1311 1652 function TBGRADefaultBitmap.GetArrow: TBGRAArrow; 1312 1653 begin 1313 if FArrow = nil then 1314 begin 1315 FArrow := TBGRAArrow.Create; 1316 FArrow.LineCap := LineCap; 1317 end; 1318 result := FArrow; 1654 if FPenStroker.Arrow = nil then 1655 begin 1656 FPenStroker.Arrow := TBGRAArrow.Create; 1657 FPenStroker.Arrow.LineCap := LineCap; 1658 FPenStroker.ArrowOwned := true; 1659 end; 1660 result := FPenStroker.Arrow as TBGRAArrow; 1319 1661 end; 1320 1662 … … 1342 1684 procedure TBGRADefaultBitmap.SetPixel(x, y: int32or64; c: TColor); 1343 1685 var 1344 p: PB yte;1686 p: PBGRAPixel; 1345 1687 begin 1346 1688 if not PtInClipRect(x,y) then exit; 1347 1689 LoadFromBitmapIfNeeded; 1348 p := PByte(GetScanlineFast(y) + x); 1349 p^ := c shr 16; 1350 Inc(p); 1351 p^ := c shr 8; 1352 Inc(p); 1353 p^ := c; 1354 Inc(p); 1355 p^ := 255; 1690 p := GetScanlineFast(y) + x; 1691 RedGreenBlue(c, p^.red,p^.green,p^.blue); 1692 p^.alpha := 255; 1356 1693 InvalidateBitmap; 1357 1694 end; … … 1639 1976 end; 1640 1977 1641 { Load raw image data. It must be 32bit or 24 bits per pixel}1642 function TBGRADefaultBitmap.LoadFromRawImage(ARawImage: TRawImage;1643 DefaultOpacity: byte; AlwaysReplaceAlpha: boolean; RaiseErrorOnInvalidPixelFormat: boolean): boolean;1644 var1645 psource_byte, pdest_byte,1646 psource_first, pdest_first: PByte;1647 psource_delta, pdest_delta: integer;1648 1649 n: integer;1650 mustSwapRedBlue, mustReverse32: boolean;1651 1652 procedure CopyAndSwapIfNecessary(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);1653 begin1654 if mustReverse32 then1655 begin1656 while count > 0 do1657 begin1658 pdest^.blue := psrc^.alpha;1659 pdest^.green := psrc^.red;1660 pdest^.red := psrc^.green;1661 pdest^.alpha := psrc^.blue;1662 dec(count);1663 inc(pdest);1664 inc(psrc);1665 end;1666 end else1667 if mustSwapRedBlue then1668 begin1669 while count > 0 do1670 begin1671 pdest^.red := psrc^.blue;1672 pdest^.green := psrc^.green;1673 pdest^.blue := psrc^.red;1674 pdest^.alpha := psrc^.alpha;1675 dec(count);1676 inc(pdest);1677 inc(psrc);1678 end;1679 end else1680 move(psrc^,pdest^,count*sizeof(TBGRAPixel));1681 end;1682 1683 procedure CopyRGBAndSwapIfNecessary(psrc: PByte; pdest: PBGRAPixel; count: integer);1684 begin1685 if mustSwapRedBlue then1686 begin1687 while count > 0 do1688 begin1689 pdest^.blue := (psrc+2)^;1690 pdest^.green := (psrc+1)^;1691 pdest^.red := psrc^;1692 pdest^.alpha := DefaultOpacity;1693 inc(psrc,3);1694 inc(pdest);1695 dec(count);1696 end;1697 end else1698 begin1699 while count > 0 do1700 begin1701 PWord(pdest)^ := PWord(psrc)^;1702 pdest^.red := (psrc+2)^;1703 pdest^.alpha := DefaultOpacity;1704 inc(psrc,3);1705 inc(pdest);1706 dec(count);1707 end;1708 end;1709 end;1710 1711 procedure CopyAndSwapIfNecessaryAndSetAlpha(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);1712 begin1713 if mustReverse32 then1714 begin1715 while count > 0 do1716 begin1717 pdest^.blue := psrc^.alpha;1718 pdest^.green := psrc^.red;1719 pdest^.red := psrc^.green;1720 pdest^.alpha := DefaultOpacity; //use default opacity1721 inc(psrc);1722 inc(pdest);1723 dec(count);1724 end;1725 end else1726 if mustSwapRedBlue then1727 begin1728 while count > 0 do1729 begin1730 pdest^.red := psrc^.blue;1731 pdest^.green := psrc^.green;1732 pdest^.blue := psrc^.red;1733 pdest^.alpha := DefaultOpacity; //use default opacity1734 inc(psrc);1735 inc(pdest);1736 dec(count);1737 end;1738 end else1739 begin1740 while count > 0 do1741 begin1742 PWord(pdest)^ := PWord(psrc)^;1743 pdest^.red := psrc^.red;1744 pdest^.alpha := DefaultOpacity; //use default opacity1745 inc(psrc);1746 inc(pdest);1747 dec(count);1748 end;1749 end;1750 end;1751 1752 procedure CopyAndSwapIfNecessaryAndReplaceAlpha(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);1753 var OpacityOrMask, OpacityAndMask, sourceval: Longword;1754 begin1755 OpacityOrMask := NtoLE(longword(DefaultOpacity) shl 24);1756 OpacityAndMask := NtoLE($FFFFFF);1757 if mustReverse32 then1758 begin1759 OpacityAndMask := NtoBE($FFFFFF);1760 while count > 0 do1761 begin1762 sourceval := plongword(psrc)^ and OpacityAndMask;1763 if (sourceval <> 0) and (psrc^.blue{=alpha} = 0) then //if not black but transparent1764 begin1765 pdest^.blue := psrc^.alpha;1766 pdest^.green := psrc^.red;1767 pdest^.red := psrc^.green;1768 pdest^.alpha := DefaultOpacity; //use default opacity1769 end1770 else1771 begin1772 pdest^.blue := psrc^.alpha;1773 pdest^.green := psrc^.red;1774 pdest^.red := psrc^.green;1775 pdest^.alpha := psrc^.blue;1776 end;1777 dec(count);1778 inc(pdest);1779 inc(psrc);1780 end;1781 end else1782 if mustSwapRedBlue then1783 begin1784 while count > 0 do1785 begin1786 sourceval := plongword(psrc)^ and OpacityAndMask;1787 if (sourceval <> 0) and (psrc^.alpha = 0) then //if not black but transparent1788 begin1789 pdest^.red := psrc^.blue;1790 pdest^.green := psrc^.green;1791 pdest^.blue := psrc^.red;1792 pdest^.alpha := DefaultOpacity; //use default opacity1793 end1794 else1795 begin1796 pdest^.red := psrc^.blue;1797 pdest^.green := psrc^.green;1798 pdest^.blue := psrc^.red;1799 pdest^.alpha := psrc^.alpha;1800 end;1801 dec(count);1802 inc(pdest);1803 inc(psrc);1804 end;1805 end else1806 begin1807 while count > 0 do1808 begin1809 sourceval := plongword(psrc)^ and OpacityAndMask;1810 if (sourceval <> 0) and (psrc^.alpha = 0) then //if not black but transparent1811 plongword(pdest)^ := sourceval or OpacityOrMask //use default opacity1812 else1813 pdest^ := psrc^;1814 dec(count);1815 inc(pdest);1816 inc(psrc);1817 end;1818 end;1819 end;1820 1821 begin1822 if (ARawImage.Description.Width <> cardinal(Width)) or1823 (ARawImage.Description.Height <> cardinal(Height)) then1824 raise Exception.Create('Bitmap size is inconsistant');1825 1826 DiscardBitmapChange;1827 if (Height=0) or (Width=0) then1828 begin1829 result := true;1830 exit;1831 end;1832 1833 if ARawImage.Description.LineOrder = riloTopToBottom then1834 begin1835 psource_first := ARawImage.Data;1836 psource_delta := ARawImage.Description.BytesPerLine;1837 end else1838 begin1839 psource_first := ARawImage.Data + (ARawImage.Description.Height-1) * ARawImage.Description.BytesPerLine;1840 psource_delta := -ARawImage.Description.BytesPerLine;1841 end;1842 1843 if ((ARawImage.Description.RedShift = 0) and1844 (ARawImage.Description.BlueShift = 16) and1845 (ARawImage.Description.ByteOrder = riboLSBFirst)) or1846 ((ARawImage.Description.RedShift = 24) and1847 (ARawImage.Description.BlueShift = 8) and1848 (ARawImage.Description.ByteOrder = riboMSBFirst)) then1849 begin1850 mustSwapRedBlue:= true;1851 mustReverse32 := false;1852 end1853 else1854 begin1855 mustSwapRedBlue:= false;1856 if ((ARawImage.Description.RedShift = 8) and1857 (ARawImage.Description.GreenShift = 16) and1858 (ARawImage.Description.BlueShift = 24) and1859 (ARawImage.Description.ByteOrder = riboLSBFirst)) or1860 ((ARawImage.Description.RedShift = 16) and1861 (ARawImage.Description.GreenShift = 8) and1862 (ARawImage.Description.BlueShift = 0) and1863 (ARawImage.Description.ByteOrder = riboMSBFirst)) then1864 mustReverse32 := true1865 else1866 mustReverse32 := false;1867 end;1868 1869 if self.LineOrder = riloTopToBottom then1870 begin1871 pdest_first := PByte(self.Data);1872 pdest_delta := self.Width*sizeof(TBGRAPixel);1873 end else1874 begin1875 pdest_first := PByte(self.Data) + (self.Height-1)*self.Width*sizeof(TBGRAPixel);1876 pdest_delta := -self.Width*sizeof(TBGRAPixel);1877 end;1878 1879 { 32 bits per pixel }1880 if (ARawImage.Description.BitsPerPixel = 32) and1881 (ARawImage.DataSize >= longword(NbPixels) * 4) then1882 begin1883 { If there is an alpha channel }1884 if (ARawImage.Description.AlphaPrec = 8) and not AlwaysReplaceAlpha then1885 begin1886 if DefaultOpacity = 0 then1887 begin1888 if ARawImage.Description.LineOrder = FLineOrder then1889 CopyAndSwapIfNecessary(PBGRAPixel(ARawImage.Data), FData, NbPixels) else1890 begin1891 psource_byte := psource_first;1892 pdest_byte := pdest_first;1893 for n := FHeight-1 downto 0 do1894 begin1895 CopyAndSwapIfNecessary(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);1896 inc(psource_byte, psource_delta);1897 inc(pdest_byte, pdest_delta);1898 end;1899 end;1900 end1901 else1902 begin1903 psource_byte := psource_first;1904 pdest_byte := pdest_first;1905 for n := FHeight-1 downto 0 do1906 begin1907 CopyAndSwapIfNecessaryAndReplaceAlpha(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);1908 inc(psource_byte, psource_delta);1909 inc(pdest_byte, pdest_delta);1910 end;1911 end;1912 end1913 else1914 begin { If there isn't any alpha channel }1915 psource_byte := psource_first;1916 pdest_byte := pdest_first;1917 for n := FHeight-1 downto 0 do1918 begin1919 CopyAndSwapIfNecessaryAndSetAlpha(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);1920 inc(psource_byte, psource_delta);1921 inc(pdest_byte, pdest_delta);1922 end;1923 end;1924 end1925 else1926 { 24 bit per pixel }1927 if (ARawImage.Description.BitsPerPixel = 24) then1928 begin1929 psource_byte := psource_first;1930 pdest_byte := pdest_first;1931 for n := FHeight-1 downto 0 do1932 begin1933 CopyRGBAndSwapIfNecessary(psource_byte, PBGRAPixel(pdest_byte), FWidth);1934 inc(psource_byte, psource_delta);1935 inc(pdest_byte, pdest_delta);1936 end;1937 end1938 else1939 begin1940 if RaiseErrorOnInvalidPixelFormat then1941 raise Exception.Create('Invalid raw image format (' + IntToStr(1942 ARawImage.Description.Depth) + ' found)') else1943 begin1944 result := false;1945 exit;1946 end;1947 end;1948 1949 InvalidateBitmap;1950 result := true;1951 end;1952 1953 1978 procedure TBGRADefaultBitmap.LoadFromBitmapIfNeeded; 1954 1979 begin 1955 1980 if FBitmapModified then 1956 1981 begin 1957 if FBitmap <> nil then 1958 LoadFromRawImage(FBitmap.RawImage, FCanvasOpacity); 1982 DoLoadFromBitmap; 1959 1983 DiscardBitmapChange; 1960 1984 end; … … 2024 2048 FWidth := 0; 2025 2049 FHeight := 0; 2050 FScanWidth := FWidth; 2051 FScanHeight:= FHeight; 2026 2052 FLineOrder := riloTopToBottom; 2027 2053 FCanvasOpacity := 255; … … 2033 2059 FontStyle := []; 2034 2060 FontAntialias := False; 2061 FontVerticalAnchor:= fvaTop; 2035 2062 FFontHeight := 20; 2036 2063 2037 PenStyle := psSolid;2038 LineCap := pecRound;2039 JoinStyle := pjsBevel;2040 JoinMiterLimit := 2;2041 2064 ResampleFilter := rfHalfCosine; 2042 2065 ScanInterpolationFilter := rfLinear; 2043 2066 ScanOffset := Point(0,0); 2067 2068 FPenStroker := TBGRAPenStroker.Create; 2069 FPenStroker.Arrow := TBGRAArrow.Create; 2070 FPenStroker.Arrow.LineCap := LineCap; 2071 FPenStroker.ArrowOwned := true; 2044 2072 end; 2045 2073 … … 2051 2079 function TBGRADefaultBitmap.GetInternalColor(x, y: integer): TFPColor; 2052 2080 begin 2053 if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then exit; 2054 result := BGRAToFPColor((Scanline[y] + x)^); 2081 if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then 2082 result := colTransparent 2083 else 2084 result := BGRAToFPColor((Scanline[y] + x)^); 2055 2085 end; 2056 2086 … … 2069 2099 c: TFPColor; 2070 2100 begin 2071 if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then exit; 2072 c := BGRAToFPColor((Scanline[y] + x)^); 2073 Result := palette.IndexOf(c); 2101 if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then 2102 result := 0 2103 else 2104 begin 2105 c := BGRAToFPColor((Scanline[y] + x)^); 2106 Result := palette.IndexOf(c); 2107 end; 2074 2108 end; 2075 2109 2076 2110 procedure TBGRADefaultBitmap.Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean); 2077 2111 begin 2078 if self = nil then 2079 exit; 2112 if (self = nil) or (Width = 0) or (Height = 0) then exit; 2080 2113 if Opaque then 2081 2114 DataDrawOpaque(ACanvas, Rect(X, Y, X + Width, Y + Height), Data, … … 2092 2125 procedure TBGRADefaultBitmap.Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean); 2093 2126 begin 2094 if self = nil then 2095 exit; 2127 if (self = nil) or (Width = 0) or (Height = 0) then exit; 2096 2128 if Opaque then 2097 2129 DataDrawOpaque(ACanvas, Rect, Data, FLineOrder, FWidth, FHeight) … … 2099 2131 begin 2100 2132 LoadFromBitmapIfNeeded; 2101 if Empty then2102 exit;2103 2133 ACanvas.StretchDraw(Rect, Bitmap); 2104 2134 end; … … 2309 2339 end; 2310 2340 2311 procedure TBGRADefaultBitmap.SetArrowStart(AStyle: TBGRAArrowStyle; 2312 ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single); 2313 begin 2314 GetArrow.SetStart(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset); 2315 end; 2316 2317 procedure TBGRADefaultBitmap.SetArrowEnd(AStyle: TBGRAArrowStyle; 2318 ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single); 2319 begin 2320 GetArrow.SetEnd(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset); 2321 end; 2322 2323 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); 2324 var tempCanvas: TBGRACanvas2D; 2325 begin 2326 tempCanvas:= TBGRACanvas2D.Create(self); 2327 tempCanvas.strokeStyle(c); 2328 tempCanvas.lineWidth := w; 2329 tempCanvas.lineStyle(CustomPenStyle); 2330 tempCanvas.lineCapLCL := LineCap; 2331 tempCanvas.lineJoinLCL := JoinStyle; 2332 tempCanvas.path(APath); 2333 tempCanvas.stroke; 2334 tempCanvas.Free; 2335 end; 2336 2337 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); 2338 var tempCanvas: TBGRACanvas2D; 2339 begin 2340 tempCanvas:= TBGRACanvas2D.Create(self); 2341 tempCanvas.strokeStyle(texture); 2342 tempCanvas.lineWidth := w; 2343 tempCanvas.lineStyle(CustomPenStyle); 2344 tempCanvas.lineCapLCL := LineCap; 2345 tempCanvas.lineJoinLCL := JoinStyle; 2346 tempCanvas.path(APath); 2347 tempCanvas.stroke; 2348 tempCanvas.Free; 2341 procedure TBGRADefaultBitmap.InternalTextOutCurved( 2342 ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; 2343 ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); 2344 var 2345 pstr: pchar; 2346 left,charlen: integer; 2347 nextchar: string; 2348 charwidth, angle, textlen: single; 2349 begin 2350 if (ATexture = nil) and (AColor.alpha = 0) then exit; 2351 sUTF8 := CleanTextOutString(sUTF8); 2352 if sUTF8 = '' then exit; 2353 pstr := @sUTF8[1]; 2354 left := length(sUTF8); 2355 if AALign<> taLeftJustify then 2356 begin 2357 textlen := TextSize(sUTF8).cx + (UTF8Length(sUTF8)-1)*ALetterSpacing; 2358 case AAlign of 2359 taCenter: ACursor.MoveBackward(textlen*0.5); 2360 taRightJustify: ACursor.MoveBackward(textlen); 2361 end; 2362 end; 2363 while left > 0 do 2364 begin 2365 charlen := UTF8CharacterLength(pstr); 2366 setlength(nextchar, charlen); 2367 move(pstr^, nextchar[1], charlen); 2368 inc(pstr,charlen); 2369 dec(left,charlen); 2370 charwidth := TextSize(nextchar).cx; 2371 ACursor.MoveForward(charwidth); 2372 ACursor.MoveBackward(charwidth, false); 2373 ACursor.MoveForward(charwidth*0.5); 2374 with ACursor.CurrentTangent do angle := arctan2(y,x); 2375 with ACursor.CurrentCoordinate do 2376 begin 2377 if ATexture = nil then 2378 TextOutAngle(x,y, system.round(-angle*1800/Pi), nextchar, AColor, taCenter) 2379 else 2380 TextOutAngle(x,y, system.round(-angle*1800/Pi), nextchar, ATexture, taCenter); 2381 end; 2382 ACursor.MoveForward(charwidth*0.5 + ALetterSpacing); 2383 end; 2384 end; 2385 2386 procedure TBGRADefaultBitmap.InternalArc(cx, cy, rx, ry: single; StartAngleRad, 2387 EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions; 2388 ADrawChord: boolean; ATexture: IBGRAScanner); 2389 var 2390 pts, ptsFill: array of TPointF; 2391 temp: single; 2392 multi: TBGRAMultishapeFiller; 2393 begin 2394 if (rx = 0) or (ry = 0) then exit; 2395 if ADrawChord then AOptions := AOptions+[aoClosePath]; 2396 if not (aoFillPath in AOptions) then 2397 AFillColor := BGRAPixelTransparent; 2398 2399 if (ABorderColor.alpha = 0) and (AFillColor.alpha = 0) then exit; 2400 2401 if abs(StartAngleRad-EndAngleRad) >= 2*PI - 1e-6 then 2402 begin 2403 if aoPie in AOptions then 2404 EndAngleRad:= StartAngleRad+2*PI 2405 else 2406 EllipseAntialias(cx,cy,rx,ry,ABorderColor,w,AFillColor); 2407 exit; 2408 end; 2409 2410 if EndAngleRad < StartAngleRad then 2411 begin 2412 temp := StartAngleRad; 2413 StartAngleRad:= EndAngleRad; 2414 EndAngleRad:= temp; 2415 end; 2416 2417 pts := ComputeArcRad(cx,cy,rx,ry,StartAngleRad,EndAngleRad); 2418 if aoPie in AOptions then pts := ConcatPointsF([PointsF([PointF(cx,cy)]),pts]); 2419 2420 multi := TBGRAMultishapeFiller.Create; 2421 multi.PolygonOrder := poLastOnTop; 2422 if AFillColor.alpha <> 0 then 2423 begin 2424 if not (aoPie in AOptions) and (length(pts)>=2) then ptsFill := ConcatPointsF([PointsF([(pts[0]+pts[high(pts)])*0.5]),pts]) 2425 else ptsFill := pts; 2426 if ATexture <> nil then 2427 multi.AddPolygon(ptsFill, ATexture) 2428 else 2429 multi.AddPolygon(ptsFill, AFillColor); 2430 end; 2431 if ABorderColor.alpha <> 0 then 2432 begin 2433 if [aoPie,aoClosePath]*AOptions <> [] then 2434 multi.AddPolygon(ComputeWidePolygon(pts,w), ABorderColor) 2435 else 2436 multi.AddPolygon(ComputeWidePolyline(pts,w), ABorderColor); 2437 end; 2438 multi.Antialiasing := true; 2439 multi.Draw(self); 2440 multi.Free; 2441 end; 2442 2443 function TBGRADefaultBitmap.IsAffineRoughlyTranslation(AMatrix: TAffineMatrix; ASourceBounds: TRect): boolean; 2444 const oneOver512 = 1/512; 2445 var Orig,HAxis,VAxis: TPointF; 2446 begin 2447 Orig := AMatrix*PointF(ASourceBounds.Left,ASourceBounds.Top); 2448 if (abs(Orig.x-round(Orig.x)) > oneOver512) or 2449 (abs(Orig.y-round(Orig.y)) > oneOver512) then 2450 begin 2451 result := false; 2452 exit; 2453 end; 2454 HAxis := AMatrix*PointF(ASourceBounds.Right-1,ASourceBounds.Top); 2455 if (abs(HAxis.x - (round(Orig.x)+ASourceBounds.Right-1 - ASourceBounds.Left)) > oneOver512) or 2456 (abs(HAxis.y - round(Orig.y)) > oneOver512) then 2457 begin 2458 result := false; 2459 exit; 2460 end; 2461 VAxis := AMatrix*PointF(ASourceBounds.Left,ASourceBounds.Bottom-1); 2462 if (abs(VAxis.y - (round(Orig.y)+ASourceBounds.Bottom-1 - ASourceBounds.Top)) > oneOver512) or 2463 (abs(VAxis.x - round(Orig.x)) > oneOver512) then 2464 begin 2465 result := false; 2466 exit; 2467 end; 2468 result := true; 2349 2469 end; 2350 2470 … … 2381 2501 c: TBGRAPixel; w: single); 2382 2502 begin 2383 if Assigned(FArrow) then 2384 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2385 else 2386 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit); 2503 FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,c), c); 2387 2504 end; 2388 2505 … … 2390 2507 texture: IBGRAScanner; w: single); 2391 2508 begin 2392 if Assigned(FArrow) then 2393 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2394 else 2395 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit); 2509 FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w), texture); 2396 2510 end; 2397 2511 2398 2512 procedure TBGRADefaultBitmap.DrawLineAntialias(x1, y1, x2, y2: single; 2399 c: TBGRAPixel; w: single; Closed: boolean); 2400 var 2401 options: TBGRAPolyLineOptions; 2402 begin 2403 if not closed then options := [plRoundCapOpen] else options := []; 2404 options += GetPolyLineOption; 2405 if Assigned(FArrow) then 2406 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2407 else 2408 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit) 2513 c: TBGRAPixel; w: single; ClosedCap: boolean); 2514 begin 2515 FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,c,ClosedCap), c); 2409 2516 end; 2410 2517 2411 2518 procedure TBGRADefaultBitmap.DrawLineAntialias(x1, y1, x2, y2: single; 2412 texture: IBGRAScanner; w: single; Closed: boolean); 2413 var 2414 options: TBGRAPolyLineOptions; 2415 c: TBGRAPixel; 2416 begin 2417 if not closed then 2418 begin 2419 options := [plRoundCapOpen]; 2420 c := BGRAWhite; //needed for alpha junction 2421 end else 2422 begin 2423 options := []; 2424 c := BGRAPixelTransparent; 2425 end; 2426 options += GetPolyLineOption; 2427 if Assigned(FArrow) then 2428 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2429 else 2430 BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit); 2519 texture: IBGRAScanner; w: single; ClosedCap: boolean); 2520 begin 2521 FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,ClosedCap), texture); 2431 2522 end; 2432 2523 … … 2434 2525 c: TBGRAPixel; w: single); 2435 2526 begin 2436 if Assigned(FArrow) then 2437 BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2438 else 2439 BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit) 2527 FillPolyAntialias( FPenStroker.ComputePolyline(points,w,c), c); 2440 2528 end; 2441 2529 … … 2443 2531 const points: array of TPointF; texture: IBGRAScanner; w: single); 2444 2532 begin 2445 if Assigned(FArrow) then 2446 BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 2447 else 2448 BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit); 2533 FillPolyAntialias( FPenStroker.ComputePolyline(points,w), texture); 2449 2534 end; 2450 2535 2451 2536 procedure TBGRADefaultBitmap.DrawPolyLineAntialias(const points: array of TPointF; 2452 c: TBGRAPixel; w: single; Closed : boolean);2453 var 2454 options: TBGRAPolyLineOptions;2455 begin 2456 if not closed then options := [plRoundCapOpen] else options := []; 2457 options += GetPolyLineOption; 2458 if Assigned(FArrow) then2459 BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)2460 else 2461 BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit);2537 c: TBGRAPixel; w: single; ClosedCap: boolean); 2538 begin 2539 FillPolyAntialias( FPenStroker.ComputePolyline(points,w,c,ClosedCap), c); 2540 end; 2541 2542 procedure TBGRADefaultBitmap.DrawPolyLineAntialias( 2543 const points: array of TPointF; texture: IBGRAScanner; w: single; 2544 ClosedCap: boolean); 2545 begin 2546 FillPolyAntialias( FPenStroker.ComputePolyline(points,w,ClosedCap), texture); 2462 2547 end; 2463 2548 … … 2478 2563 end; 2479 2564 2565 procedure TBGRADefaultBitmap.DrawPolyLineAntialiasAutocycle( 2566 const points: array of TPointF; c: TBGRAPixel; w: single); 2567 begin 2568 FillPolyAntialias( FPenStroker.ComputePolylineAutocycle(points,w), c); 2569 end; 2570 2571 procedure TBGRADefaultBitmap.DrawPolyLineAntialiasAutocycle( 2572 const points: array of TPointF; texture: IBGRAScanner; w: single); 2573 begin 2574 FillPolyAntialias( FPenStroker.ComputePolylineAutocycle(points,w), texture); 2575 end; 2576 2480 2577 procedure TBGRADefaultBitmap.DrawPolygonAntialias(const points: array of TPointF; 2481 2578 c: TBGRAPixel; w: single); 2482 2579 begin 2483 BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,[plCycle],nil,JoinMiterLimit);2580 FillPolyAntialias( FPenStroker.ComputePolygon(points,w), c); 2484 2581 end; 2485 2582 … … 2487 2584 const points: array of TPointF; texture: IBGRAScanner; w: single); 2488 2585 begin 2489 BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,[plCycle],texture,JoinMiterLimit);2586 FillPolyAntialias( FPenStroker.ComputePolygon(points,w), texture); 2490 2587 end; 2491 2588 … … 2534 2631 end; 2535 2632 2536 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; c: TBGRAPixel); 2537 var tempCanvas: TBGRACanvas2D; 2538 begin 2539 tempCanvas:= TBGRACanvas2D.Create(self); 2540 tempCanvas.fillStyle(c); 2541 tempCanvas.path(APath); 2542 tempCanvas.fill; 2543 tempCanvas.Free; 2544 end; 2545 2546 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; texture: IBGRAScanner); 2547 var tempCanvas: TBGRACanvas2D; 2548 begin 2549 tempCanvas:= TBGRACanvas2D.Create(self); 2550 tempCanvas.fillStyle(texture); 2551 tempCanvas.path(APath); 2552 tempCanvas.fill; 2553 tempCanvas.Free; 2633 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AFillColor: TBGRAPixel); 2634 begin 2635 FillPolyAntialias(APath.getPoints,AFillColor); 2636 end; 2637 2638 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AFillTexture: IBGRAScanner); 2639 begin 2640 FillPolyAntialias(APath.getPoints,AFillTexture); 2641 end; 2642 2643 procedure TBGRADefaultBitmap.ErasePath(APath: IBGRAPath; alpha: byte); 2644 begin 2645 ErasePolyAntialias(APath.getPoints,alpha); 2646 end; 2647 2648 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2649 AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); 2650 var tempPath: TBGRAPath; 2651 multi: TBGRAMultishapeFiller; 2652 begin 2653 tempPath := TBGRAPath.Create(APath); 2654 multi := TBGRAMultishapeFiller.Create; 2655 multi.PolygonOrder := poLastOnTop; 2656 multi.AddPathFill(tempPath,AMatrix,AFillColor); 2657 multi.AddPathStroke(tempPath,AMatrix,AStrokeColor,AWidth,FPenStroker); 2658 multi.Draw(self); 2659 multi.Free; 2660 tempPath.Free; 2661 end; 2662 2663 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2664 AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); 2665 var tempPath: TBGRAPath; 2666 multi: TBGRAMultishapeFiller; 2667 begin 2668 tempPath := TBGRAPath.Create(APath); 2669 multi := TBGRAMultishapeFiller.Create; 2670 multi.PolygonOrder := poLastOnTop; 2671 multi.AddPathFill(tempPath,AMatrix,AFillColor); 2672 multi.AddPathStroke(tempPath,AMatrix,AStrokeTexture,AWidth,FPenStroker); 2673 multi.Draw(self); 2674 multi.Free; 2675 tempPath.Free; 2676 end; 2677 2678 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2679 AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); 2680 var tempPath: TBGRAPath; 2681 multi: TBGRAMultishapeFiller; 2682 begin 2683 tempPath := TBGRAPath.Create(APath); 2684 multi := TBGRAMultishapeFiller.Create; 2685 multi.PolygonOrder := poLastOnTop; 2686 multi.AddPathFill(tempPath,AMatrix,AFillTexture); 2687 multi.AddPathStroke(tempPath,AMatrix,AStrokeColor,AWidth,FPenStroker); 2688 multi.Draw(self); 2689 multi.Free; 2690 tempPath.Free; 2691 end; 2692 2693 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2694 AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); 2695 var 2696 tempPath: TBGRAPath; 2697 multi: TBGRAMultishapeFiller; 2698 begin 2699 tempPath := TBGRAPath.Create(APath); 2700 multi := TBGRAMultishapeFiller.Create; 2701 multi.PolygonOrder := poLastOnTop; 2702 multi.AddPathFill(tempPath,AMatrix,AFillTexture); 2703 multi.AddPathStroke(tempPath,AMatrix,AStrokeTexture,AWidth,FPenStroker); 2704 multi.Draw(self); 2705 multi.Free; 2706 tempPath.Free; 2707 end; 2708 2709 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2710 AStrokeColor: TBGRAPixel; AWidth: single); 2711 var tempPath: TBGRAPath; 2712 begin 2713 tempPath := TBGRAPath.Create(APath); 2714 tempPath.stroke(self, AMatrix, AStrokeColor, AWidth); 2715 tempPath.Free; 2716 end; 2717 2718 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2719 AStrokeTexture: IBGRAScanner; AWidth: single); 2720 var tempPath: TBGRAPath; 2721 begin 2722 tempPath := TBGRAPath.Create(APath); 2723 tempPath.stroke(self, AMatrix, AStrokeTexture, AWidth); 2724 tempPath.Free; 2725 end; 2726 2727 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2728 AFillColor: TBGRAPixel); 2729 begin 2730 FillPolyAntialias(APath.getPoints(AMatrix),AFillColor); 2731 end; 2732 2733 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; 2734 AFillTexture: IBGRAScanner); 2735 begin 2736 FillPolyAntialias(APath.getPoints(AMatrix),AFillTexture); 2737 end; 2738 2739 procedure TBGRADefaultBitmap.ErasePath(APath: IBGRAPath; 2740 AMatrix: TAffineMatrix; alpha: byte); 2741 begin 2742 ErasePolyAntialias(APath.getPoints(AMatrix),alpha); 2743 end; 2744 2745 procedure TBGRADefaultBitmap.ArrowStartAsNone; 2746 begin 2747 GetArrow.StartAsNone; 2748 end; 2749 2750 procedure TBGRADefaultBitmap.ArrowStartAsClassic(AFlipped: boolean; 2751 ACut: boolean; ARelativePenWidth: single); 2752 begin 2753 GetArrow.StartAsClassic(AFlipped,ACut,ARelativePenWidth); 2754 end; 2755 2756 procedure TBGRADefaultBitmap.ArrowStartAsTriangle(ABackOffset: single; 2757 ARounded: boolean; AHollow: boolean; AHollowPenWidth: single); 2758 begin 2759 GetArrow.StartAsTriangle(ABackOffset,ARounded,AHollow,AHollowPenWidth); 2760 end; 2761 2762 procedure TBGRADefaultBitmap.ArrowStartAsTail; 2763 begin 2764 GetArrow.StartAsTail; 2765 end; 2766 2767 procedure TBGRADefaultBitmap.ArrowEndAsNone; 2768 begin 2769 GetArrow.EndAsNone; 2770 end; 2771 2772 procedure TBGRADefaultBitmap.ArrowEndAsClassic(AFlipped: boolean; 2773 ACut: boolean; ARelativePenWidth: single); 2774 begin 2775 GetArrow.EndAsClassic(AFlipped,ACut,ARelativePenWidth); 2776 end; 2777 2778 procedure TBGRADefaultBitmap.ArrowEndAsTriangle(ABackOffset: single; 2779 ARounded: boolean; AHollow: boolean; AHollowPenWidth: single); 2780 begin 2781 GetArrow.EndAsTriangle(ABackOffset,ARounded,AHollow,AHollowPenWidth); 2782 end; 2783 2784 procedure TBGRADefaultBitmap.ArrowEndAsTail; 2785 begin 2786 GetArrow.EndAsTail; 2554 2787 end; 2555 2788 … … 2632 2865 2633 2866 procedure TBGRADefaultBitmap.FillQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF; 2634 texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True); 2635 var 2636 center: TPointF; 2637 centerTex: TPointF; 2638 begin 2639 center := (pt1+pt2+pt3+pt4)*(1/4); 2640 centerTex := (tex1+tex2+tex3+tex4)*(1/4); 2641 FillTriangleLinearMapping(pt1,pt2,center, texture,tex1,tex2,centerTex, TextureInterpolation); 2642 FillTriangleLinearMapping(pt2,pt3,center, texture,tex2,tex3,centerTex, TextureInterpolation); 2643 FillTriangleLinearMapping(pt3,pt4,center, texture,tex3,tex4,centerTex, TextureInterpolation); 2644 FillTriangleLinearMapping(pt4,pt1,center, texture,tex4,tex1,centerTex, TextureInterpolation); 2867 texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; 2868 TextureInterpolation: Boolean; ACulling: TFaceCulling); 2869 var 2870 scan: TBGRAQuadLinearScanner; 2871 begin 2872 if ((abs(pt1.y-pt2.y)<1e-6) and (abs(pt3.y-pt4.y)<1e-6)) or 2873 ((abs(pt3.y-pt2.y)<1e-6) and (abs(pt1.y-pt4.y)<1e-6)) then 2874 FillPolyLinearMapping([pt1,pt2,pt3,pt4], texture, 2875 [tex1,tex2,tex3,tex4], TextureInterpolation) 2876 else 2877 begin 2878 scan := TBGRAQuadLinearScanner.Create(texture, 2879 [tex1,tex2,tex3,tex4], 2880 [pt1,pt2,pt3,pt4],TextureInterpolation); 2881 scan.Culling := ACulling; 2882 FillPoly([pt1,pt2,pt3,pt4],scan,dmDrawWithTransparency); 2883 scan.Free; 2884 end; 2645 2885 end; 2646 2886 … … 2663 2903 2664 2904 procedure TBGRADefaultBitmap.FillQuadLinearMappingAntialias(pt1, pt2, pt3, 2665 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); 2905 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; 2906 ACulling: TFaceCulling); 2666 2907 var multi : TBGRAMultishapeFiller; 2667 2908 begin 2668 2909 multi := TBGRAMultishapeFiller.Create; 2669 multi.AddQuadLinearMapping(pt1, pt2, pt3, pt4, texture, tex1,tex2,tex3,tex4 );2910 multi.AddQuadLinearMapping(pt1, pt2, pt3, pt4, texture, tex1,tex2,tex3,tex4, ACulling); 2670 2911 multi.Draw(self); 2671 2912 multi.free; … … 2673 2914 2674 2915 procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3, 2675 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); 2916 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; 2917 ADrawMode: TDrawMode); 2676 2918 var 2677 2919 persp: TBGRAPerspectiveScannerTransform; 2678 2920 begin 2679 2921 persp := TBGRAPerspectiveScannerTransform.Create(texture,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]); 2680 FillPoly([pt1,pt2,pt3,pt4],persp, dmDrawWithTransparency);2922 FillPoly([pt1,pt2,pt3,pt4],persp,ADrawMode); 2681 2923 persp.Free; 2682 2924 end; … … 2684 2926 procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3, 2685 2927 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; 2686 ACleanBorders: TRect );2928 ACleanBorders: TRect; ADrawMode: TDrawMode); 2687 2929 var 2688 2930 persp: TBGRAPerspectiveScannerTransform; … … 2691 2933 clean := TBGRAExtendedBorderScanner.Create(texture,ACleanBorders); 2692 2934 persp := TBGRAPerspectiveScannerTransform.Create(clean,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]); 2693 FillPoly([pt1,pt2,pt3,pt4],persp, dmDrawWithTransparency);2935 FillPoly([pt1,pt2,pt3,pt4],persp,ADrawMode); 2694 2936 persp.Free; 2695 2937 clean.Free; … … 2720 2962 end; 2721 2963 2964 procedure TBGRADefaultBitmap.FillQuadAffineMapping(Orig, HAxis, VAxis: TPointF; 2965 AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean; ADrawMode: TDrawMode; AOpacity: byte); 2966 var pts3: TPointF; 2967 affine: TBGRAAffineBitmapTransform; 2968 begin 2969 if not APixelCenteredCoordinates then 2970 begin 2971 Orig -= PointF(0.5,0.5); 2972 HAxis -= PointF(0.5,0.5); 2973 VAxis -= PointF(0.5,0.5); 2974 end; 2975 pts3 := HAxis+(VAxis-Orig); 2976 affine := TBGRAAffineBitmapTransform.Create(AImage,False,AImage.ScanInterpolationFilter,not APixelCenteredCoordinates); 2977 affine.GlobalOpacity:= AOpacity; 2978 affine.Fit(Orig,HAxis,VAxis); 2979 FillPoly([Orig,HAxis,pts3,VAxis],affine,ADrawMode); 2980 affine.Free; 2981 end; 2982 2983 procedure TBGRADefaultBitmap.FillQuadAffineMappingAntialias(Orig, HAxis, 2984 VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean; AOpacity: byte); 2985 var pts3: TPointF; 2986 affine: TBGRAAffineBitmapTransform; 2987 begin 2988 if not APixelCenteredCoordinates then 2989 begin 2990 Orig -= PointF(0.5,0.5); 2991 HAxis -= PointF(0.5,0.5); 2992 VAxis -= PointF(0.5,0.5); 2993 end; 2994 pts3 := HAxis+(VAxis-Orig); 2995 affine := TBGRAAffineBitmapTransform.Create(AImage,False,AImage.ScanInterpolationFilter,not APixelCenteredCoordinates); 2996 affine.GlobalOpacity:= AOpacity; 2997 affine.Fit(Orig,HAxis,VAxis); 2998 FillPolyAntialias([Orig,HAxis,pts3,VAxis],affine); 2999 affine.Free; 3000 end; 3001 2722 3002 procedure TBGRADefaultBitmap.FillPolyLinearMapping(const points: array of TPointF; 2723 3003 texture: IBGRAScanner; texCoords: array of TPointF; … … 2838 3118 end; 2839 3119 3120 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; 3121 AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); 3122 begin 3123 DrawPath(APath,AffineMatrixIdentity,AStrokeColor,AWidth,AFillColor); 3124 end; 3125 3126 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; 3127 AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); 3128 begin 3129 DrawPath(APath,AffineMatrixIdentity,AStrokeTexture,AWidth,AFillColor); 3130 end; 3131 3132 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; 3133 AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); 3134 begin 3135 DrawPath(APath,AffineMatrixIdentity,AStrokeColor,AWidth,AFillTexture); 3136 end; 3137 3138 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; 3139 AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); 3140 begin 3141 DrawPath(APath,AffineMatrixIdentity,AStrokeTexture,AWidth,AFillTexture); 3142 end; 3143 3144 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single); 3145 begin 3146 DrawPath(APath, AffineMatrixIdentity, AStrokeColor, AWidth); 3147 end; 3148 3149 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single); 3150 begin 3151 DrawPath(APath, AffineMatrixIdentity, AStrokeTexture, AWidth); 3152 end; 3153 2840 3154 procedure TBGRADefaultBitmap.EllipseAntialias(x, y, rx, ry: single; 2841 3155 c: TBGRAPixel; w: single); 2842 3156 begin 2843 if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;2844 if IsSolidPenStyle(FCustomPenStyle) then3157 if (PenStyle = psClear) or (c.alpha = 0) then exit; 3158 if (PenStyle = psSolid) then 2845 3159 BGRAPolygon.BorderEllipseAntialias(self, x, y, rx, ry, w, c, FEraseMode, LinearAntialiasing) 2846 3160 else … … 2851 3165 texture: IBGRAScanner; w: single); 2852 3166 begin 2853 if IsClearPenStyle(FCustomPenStyle) then exit;2854 if IsSolidPenStyle(FCustomPenStyle) then3167 if (PenStyle = psClear) then exit; 3168 if (PenStyle = psSolid) then 2855 3169 BGRAPolygon.BorderEllipseAntialiasWithTexture(self, x, y, rx, ry, w, texture, LinearAntialiasing) 2856 3170 else … … 2874 3188 { use multishape filler for fine junction between polygons } 2875 3189 multi := TBGRAMultishapeFiller.Create; 2876 if not IsClearPenStyle(FCustomPenStyle) and (c.alpha <> 0) then2877 begin 2878 if IsSolidPenStyle(FCustomPenStyle) then3190 if not (PenStyle = psClear) and (c.alpha <> 0) then 3191 begin 3192 if (PenStyle = psSolid) then 2879 3193 begin 2880 3194 multi.AddEllipse(x,y,rx-hw,ry-hw,back); … … 2941 3255 hw: single; 2942 3256 begin 2943 if IsClearPenStyle(FCustomPenStyle) or (c.alpha=0) or (w=0) then3257 if (PenStyle = psClear) or (c.alpha=0) or (w=0) then 2944 3258 begin 2945 3259 if back <> BGRAPixelTransparent then … … 2966 3280 multi := TBGRAMultishapeFiller.Create; 2967 3281 multi.FillMode := FillMode; 2968 if (JoinStyle = pjsMiter) and IsSolidPenStyle(FCustomPenStyle) then3282 if (JoinStyle = pjsMiter) and (PenStyle = psSolid) then 2969 3283 multi.AddRectangleBorder(x,y,x2,y2,w,c) 2970 3284 else … … 2985 3299 multi: TBGRAMultishapeFiller; 2986 3300 begin 2987 if IsClearPenStyle(FCustomPenStyle) or (w=0) then exit;3301 if (PenStyle = psClear) or (w=0) then exit; 2988 3302 2989 3303 hw := w/2; … … 3005 3319 multi := TBGRAMultishapeFiller.Create; 3006 3320 multi.FillMode := FillMode; 3007 if (JoinStyle = pjsMiter) and IsSolidPenStyle(FCustomPenStyle) then3321 if (JoinStyle = pjsMiter) and (PenStyle = psSolid) then 3008 3322 multi.AddRectangleBorder(x,y,x2,y2,w, texture) 3009 3323 else … … 3016 3330 c: TBGRAPixel; w: single; options: TRoundRectangleOptions); 3017 3331 begin 3018 if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;3019 if IsSolidPenStyle(FCustomPenStyle) then3332 if (PenStyle = psClear) or (c.alpha = 0) then exit; 3333 if (PenStyle = psSolid) then 3020 3334 BGRAPolygon.BorderRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,c,False, LinearAntialiasing) 3021 3335 else … … 3029 3343 multi: TBGRAMultishapeFiller; 3030 3344 begin 3031 if IsClearPenStyle(FCustomPenStyle) or (pencolor.alpha = 0) then3345 if (PenStyle = psClear) or (pencolor.alpha = 0) then 3032 3346 begin 3033 3347 FillRoundRectAntialias(x,y,x2,y2,rx,ry,fillColor,options); 3034 3348 exit; 3035 3349 end; 3036 if IsSolidPenStyle(FCustomPenStyle) then3350 if (PenStyle = psSolid) then 3037 3351 BGRAPolygon.BorderAndFillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,pencolor,fillcolor,nil,nil,False) 3038 3352 else … … 3053 3367 multi: TBGRAMultishapeFiller; 3054 3368 begin 3055 if IsClearPenStyle(FCustomPenStyle) then3369 if (PenStyle = psClear) then 3056 3370 begin 3057 3371 FillRoundRectAntialias(x,y,x2,y2,rx,ry,fillTexture,options); 3058 3372 exit; 3059 3373 end else 3060 if IsSolidPenStyle(FCustomPenStyle) then3374 if (PenStyle = psSolid) then 3061 3375 BGRAPolygon.BorderAndFillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,BGRAPixelTransparent,BGRAPixelTransparent,pentexture,filltexture,False) 3062 3376 else … … 3074 3388 texture: IBGRAScanner; w: single; options: TRoundRectangleOptions); 3075 3389 begin 3076 if IsClearPenStyle(FCustomPenStyle) then exit;3077 if IsSolidPenStyle(FCustomPenStyle) then3390 if (PenStyle = psClear) then exit; 3391 if (PenStyle = psSolid) then 3078 3392 BGRAPolygon.BorderRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,w,options,texture, LinearAntialiasing) 3079 3393 else … … 3260 3574 3261 3575 procedure TBGRADefaultBitmap.FillRect(x, y, x2, y2: integer; 3262 texture: IBGRAScanner; mode: TDrawMode );3576 texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint); 3263 3577 var 3264 3578 yb, tx, delta: integer; … … 3278 3592 for yb := y to y2 do 3279 3593 begin 3280 texture.ScanMoveTo(x ,yb);3594 texture.ScanMoveTo(x+AScanOffset.X,yb+AScanOffset.Y); 3281 3595 ScannerPutPixels(texture, p, tx, mode); 3282 3596 Inc(p, delta); … … 3284 3598 3285 3599 InvalidateBitmap; 3600 end; 3601 3602 procedure TBGRADefaultBitmap.FillRect(x, y, x2, y2: integer; 3603 texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm); 3604 var dither: TDitheringTask; 3605 begin 3606 if not CheckClippedRectBounds(x,y,x2,y2) then exit; 3607 dither := CreateDitheringTask(ditheringAlgorithm, texture, self, rect(x,y,x2,y2)); 3608 dither.ScanOffset := AScanOffset; 3609 dither.DrawMode := mode; 3610 dither.Execute; 3611 dither.Free; 3286 3612 end; 3287 3613 … … 3315 3641 end; 3316 3642 3317 procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel );3643 procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel; pixelCenteredCoordinates: boolean); 3318 3644 var tx,ty: single; 3319 3645 begin 3646 if not pixelCenteredCoordinates then 3647 begin 3648 x -= 0.5; 3649 y -= 0.5; 3650 x2 -= 0.5; 3651 y2 -= 0.5; 3652 end; 3653 3320 3654 tx := x2-x; 3321 3655 ty := y2-y; 3322 if ( tx=0) or (ty=0) then exit;3656 if (abs(tx)<1e-3) or (abs(ty)<1e-3) then exit; 3323 3657 if (abs(tx) > 2) and (abs(ty) > 2) then 3324 3658 begin … … 3345 3679 3346 3680 procedure TBGRADefaultBitmap.EraseRectAntialias(x, y, x2, y2: single; 3347 alpha: byte); 3348 begin 3681 alpha: byte; pixelCenteredCoordinates: boolean); 3682 begin 3683 if not pixelCenteredCoordinates then 3684 begin 3685 x -= 0.5; 3686 y -= 0.5; 3687 x2 -= 0.5; 3688 y2 -= 0.5; 3689 end; 3349 3690 ErasePolyAntialias([pointf(x, y), pointf(x2, y), pointf(x2, y2), pointf(x, y2)], alpha); 3350 3691 end; 3351 3692 3352 3693 procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single; 3353 texture: IBGRAScanner); 3354 begin 3694 texture: IBGRAScanner; pixelCenteredCoordinates: boolean); 3695 begin 3696 if not pixelCenteredCoordinates then 3697 begin 3698 x -= 0.5; 3699 y -= 0.5; 3700 x2 -= 0.5; 3701 y2 -= 0.5; 3702 end; 3355 3703 FillPolyAntialias([pointf(x, y), pointf(x2, y), pointf(x2, y2), pointf(x, y2)], texture); 3356 3704 end; 3357 3705 3358 3706 procedure TBGRADefaultBitmap.FillRoundRectAntialias(x, y, x2, y2, rx,ry: single; 3359 c: TBGRAPixel; options: TRoundRectangleOptions); 3360 begin 3707 c: TBGRAPixel; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean); 3708 begin 3709 if not pixelCenteredCoordinates then 3710 begin 3711 x -= 0.5; 3712 y -= 0.5; 3713 x2 -= 0.5; 3714 y2 -= 0.5; 3715 end; 3361 3716 BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,c,False, LinearAntialiasing); 3362 3717 end; 3363 3718 3364 3719 procedure TBGRADefaultBitmap.FillRoundRectAntialias(x, y, x2, y2, rx, 3365 ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions); 3366 begin 3720 ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean); 3721 begin 3722 if not pixelCenteredCoordinates then 3723 begin 3724 x -= 0.5; 3725 y -= 0.5; 3726 x2 -= 0.5; 3727 y2 -= 0.5; 3728 end; 3367 3729 BGRAPolygon.FillRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,options,texture, LinearAntialiasing); 3368 3730 end; 3369 3731 3370 3732 procedure TBGRADefaultBitmap.EraseRoundRectAntialias(x, y, x2, y2, rx, 3371 ry: single; alpha: byte; options: TRoundRectangleOptions); 3372 begin 3733 ry: single; alpha: byte; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean); 3734 begin 3735 if not pixelCenteredCoordinates then 3736 begin 3737 x -= 0.5; 3738 y -= 0.5; 3739 x2 -= 0.5; 3740 y2 -= 0.5; 3741 end; 3373 3742 BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,BGRA(0,0,0,alpha),True, LinearAntialiasing); 3374 3743 end; … … 3391 3760 sUTF8: string; c: TBGRAPixel; align: TAlignment); 3392 3761 begin 3393 FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),c,align); 3762 with (PointF(x,y)-GetFontAnchorRotatedOffset(orientationTenthDegCCW)) do 3763 FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),c,align); 3394 3764 end; 3395 3765 … … 3397 3767 sUTF8: string; texture: IBGRAScanner; align: TAlignment); 3398 3768 begin 3399 FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),texture,align); 3769 with (PointF(x,y)-GetFontAnchorRotatedOffset(orientationTenthDegCCW)) do 3770 FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),texture,align); 3771 end; 3772 3773 procedure TBGRADefaultBitmap.TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); 3774 begin 3775 InternalTextOutCurved(ACursor, sUTF8, AColor, nil, AAlign, ALetterSpacing); 3776 end; 3777 3778 procedure TBGRADefaultBitmap.TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); 3779 begin 3780 InternalTextOutCurved(ACursor, sUTF8, BGRAPixelTransparent, ATexture, AAlign, ALetterSpacing); 3400 3781 end; 3401 3782 … … 3409 3790 c: TBGRAPixel; align: TAlignment); 3410 3791 begin 3411 FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),c,align); 3792 with (PointF(x,y)-GetFontAnchorRotatedOffset) do 3793 FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),c,align); 3412 3794 end; 3413 3795 … … 3415 3797 sUTF8: string; style: TTextStyle; c: TBGRAPixel); 3416 3798 begin 3417 FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,c); 3799 with (PointF(x,y)-GetFontAnchorRotatedOffset(0)) do 3800 FontRenderer.TextRect(self,ARect,system.round(x),system.round(y),sUTF8,style,c); 3418 3801 end; 3419 3802 … … 3421 3804 style: TTextStyle; texture: IBGRAScanner); 3422 3805 begin 3423 FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,texture); 3806 with (PointF(x,y)-GetFontAnchorRotatedOffset(0)) do 3807 FontRenderer.TextRect(self,ARect,system.round(x),system.round(y),sUTF8,style,texture); 3424 3808 end; 3425 3809 … … 3470 3854 w: single): ArrayOfTPointF; 3471 3855 begin 3472 if Assigned(FArrow) then 3473 Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 3474 else 3475 Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit) 3856 result := FPenStroker.ComputePolyline(points,w); 3476 3857 end; 3477 3858 3478 3859 function TBGRADefaultBitmap.ComputeWidePolyline(const points: array of TPointF; 3479 w: single; Closed: boolean): ArrayOfTPointF; 3480 var 3481 options: TBGRAPolyLineOptions; 3482 begin 3483 if not closed then options := [plRoundCapOpen] else options := []; 3484 options += GetPolyLineOption; 3485 if Assigned(FArrow) then 3486 Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX) 3487 else 3488 Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit); 3860 w: single; ClosedCap: boolean): ArrayOfTPointF; 3861 begin 3862 result := FPenStroker.ComputePolyline(points,w,ClosedCap); 3489 3863 end; 3490 3864 … … 3492 3866 w: single): ArrayOfTPointF; 3493 3867 begin 3494 Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption+[plCycle],JoinMiterLimit);3868 result := FPenStroker.ComputePolygon(points,w); 3495 3869 end; 3496 3870 … … 3598 3972 3599 3973 procedure TBGRADefaultBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap; 3600 texture: IBGRAScanner; ADrawMode: TDrawMode );3974 texture: IBGRAScanner; ADrawMode: TDrawMode; AOpacity: byte); 3601 3975 var 3602 3976 scan: TBGRACustomScanner; 3603 3977 begin 3604 3978 if AMask = nil then exit; 3605 scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),texture );3979 scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),texture, AOpacity); 3606 3980 self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,ADrawMode); 3607 3981 scan.Free; … … 3626 4000 n: integer; 3627 4001 colorMask,beforeBGR, afterBGR: longword; 3628 begin 3629 colorMask := NtoLE($00FFFFFF); 3630 beforeBGR := NtoLE((before and $FF shl 16) + (before and $FF00) + (before shr 16 and $FF)); 3631 afterBGR := NtoLE((after and $FF shl 16) + (after and $FF00) + (after shr 16 and $FF)); 4002 rAfter,gAfter,bAfter,rBefore,gBefore,bBefore: byte; 4003 begin 4004 colorMask := LongWord(BGRA(255,255,255,0)); 4005 RedGreenBlue(before, rBefore,gBefore,bBefore); 4006 RedGreenBlue(after, rAfter,gAfter,bAfter); 4007 beforeBGR := LongWord(BGRA(rBefore,gBefore,bBefore,0)); 4008 afterBGR := LongWord(BGRA(rAfter,gAfter,bAfter,0)); 3632 4009 3633 4010 p := PLongWord(Data); … … 3654 4031 for n := NbPixels - 1 downto 0 do 3655 4032 begin 3656 if p^ = beforethen4033 if PDWord(p)^ = DWord(before) then 3657 4034 p^ := after; 3658 4035 Inc(p); 4036 end; 4037 InvalidateBitmap; 4038 end; 4039 4040 procedure TBGRADefaultBitmap.ReplaceColor(ABounds: TRect; before, after: TColor); 4041 var p: PLongWord; 4042 xb,yb,xcount: integer; 4043 4044 colorMask,beforeBGR, afterBGR: longword; 4045 rAfter,gAfter,bAfter,rBefore,gBefore,bBefore: byte; 4046 begin 4047 colorMask := LongWord(BGRA(255,255,255,0)); 4048 RedGreenBlue(before, rBefore,gBefore,bBefore); 4049 RedGreenBlue(after, rAfter,gAfter,bAfter); 4050 beforeBGR := LongWord(BGRA(rBefore,gBefore,bBefore,0)); 4051 afterBGR := LongWord(BGRA(rAfter,gAfter,bAfter,0)); 4052 4053 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 4054 xcount := ABounds.Right-ABounds.Left; 4055 for yb := ABounds.Top to ABounds.Bottom-1 do 4056 begin 4057 p := PLongWord(ScanLine[yb]+ABounds.Left); 4058 for xb := xcount-1 downto 0 do 4059 begin 4060 if p^ and colorMask = beforeBGR then 4061 p^ := (p^ and not ColorMask) or afterBGR; 4062 Inc(p); 4063 end; 4064 end; 4065 InvalidateBitmap; 4066 end; 4067 4068 procedure TBGRADefaultBitmap.ReplaceColor(ABounds: TRect; before, 4069 after: TBGRAPixel); 4070 var p: PBGRAPixel; 4071 xb,yb,xcount: integer; 4072 begin 4073 if before.alpha = 0 then 4074 begin 4075 ReplaceTransparent(ABounds,after); 4076 exit; 4077 end; 4078 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 4079 xcount := ABounds.Right-ABounds.Left; 4080 for yb := ABounds.Top to ABounds.Bottom-1 do 4081 begin 4082 p := ScanLine[yb]+ABounds.Left; 4083 for xb := xcount-1 downto 0 do 4084 begin 4085 if PDWord(p)^ = DWord(before) then 4086 p^ := after; 4087 Inc(p); 4088 end; 3659 4089 end; 3660 4090 InvalidateBitmap; … … 3673 4103 p^ := after; 3674 4104 Inc(p); 4105 end; 4106 InvalidateBitmap; 4107 end; 4108 4109 procedure TBGRADefaultBitmap.ReplaceTransparent(ABounds: TRect; 4110 after: TBGRAPixel); 4111 var p: PBGRAPixel; 4112 xb,yb,xcount: integer; 4113 begin 4114 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 4115 xcount := ABounds.Right-ABounds.Left; 4116 for yb := ABounds.Top to ABounds.Bottom-1 do 4117 begin 4118 p := ScanLine[yb]+ABounds.Left; 4119 for xb := xcount-1 downto 0 do 4120 begin 4121 if p^.alpha = 0 then 4122 p^ := after; 4123 Inc(p); 4124 end; 3675 4125 end; 3676 4126 InvalidateBitmap; … … 3826 4276 procedure TBGRADefaultBitmap.GradientFill(x, y, x2, y2: integer; 3827 4277 c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 3828 gammaColorCorrection: boolean = True; Sinus: Boolean=False); 3829 begin 3830 BGRAGradientFill(self, x, y, x2, y2, c1, c2, gtype, o1, o2, mode, gammaColorCorrection, Sinus); 4278 gammaColorCorrection: boolean; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm); 4279 var 4280 scanner: TBGRAGradientScanner; 4281 begin 4282 if (c1.alpha = 0) and (c2.alpha = 0) then 4283 FillRect(x, y, x2, y2, BGRAPixelTransparent, mode) 4284 else 4285 if ditherAlgo <> daNearestNeighbor then 4286 GradientFillDithered(x,y,x2,y2,c1,c2,gtype,o1,o2,mode,gammaColorCorrection,sinus,ditherAlgo) 4287 else 4288 begin 4289 scanner := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus); 4290 FillRect(x,y,x2,y2,scanner,mode); 4291 scanner.Free; 4292 end; 3831 4293 end; 3832 4294 3833 4295 procedure TBGRADefaultBitmap.GradientFill(x, y, x2, y2: integer; 3834 4296 gradient: TBGRACustomGradient; gtype: TGradientType; o1, o2: TPointF; 3835 mode: TDrawMode; Sinus: Boolean );4297 mode: TDrawMode; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm); 3836 4298 var 3837 4299 scanner: TBGRAGradientScanner; 3838 4300 begin 4301 if ditherAlgo <> daNearestNeighbor then 4302 GradientFillDithered(x,y,x2,y2,gradient,gtype,o1,o2,mode,sinus,ditherAlgo) 4303 else 4304 begin 4305 scanner := TBGRAGradientScanner.Create(gradient,gtype,o1,o2,sinus); 4306 FillRect(x,y,x2,y2,scanner,mode); 4307 scanner.Free; 4308 end; 4309 end; 4310 4311 procedure TBGRADefaultBitmap.GradientFillDithered(x, y, x2, y2: integer; c1, 4312 c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; 4313 mode: TDrawMode; gammaColorCorrection: boolean; Sinus: Boolean; 4314 ditherAlgo: TDitheringAlgorithm); 4315 var 4316 scanner: TBGRAGradientScanner; 4317 begin 4318 if (c1.alpha = 0) and (c2.alpha = 0) then 4319 FillRect(x, y, x2, y2, BGRAPixelTransparent, dmSet) 4320 else 4321 begin 4322 scanner := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus); 4323 FillRect(x,y,x2,y2,scanner,mode,ditherAlgo); 4324 scanner.Free; 4325 end; 4326 end; 4327 4328 procedure TBGRADefaultBitmap.GradientFillDithered(x, y, x2, y2: integer; 4329 gradient: TBGRACustomGradient; gtype: TGradientType; o1, o2: TPointF; 4330 mode: TDrawMode; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm); 4331 var 4332 scanner: TBGRAGradientScanner; 4333 begin 3839 4334 scanner := TBGRAGradientScanner.Create(gradient,gtype,o1,o2,sinus); 3840 FillRect(x,y,x2,y2,scanner,mode );4335 FillRect(x,y,x2,y2,scanner,mode,ditherAlgo); 3841 4336 scanner.Free; 3842 4337 end; … … 3850 4345 function TBGRADefaultBitmap.ScanAtInteger(X, Y: integer): TBGRAPixel; 3851 4346 begin 3852 if FData <> nilthen3853 result := (GetScanlineFast(PositiveMod(Y+ScanOffset.Y, F Height))+PositiveMod(X+ScanOffset.X, FWidth))^4347 if (FScanWidth <> 0) and (FScanHeight <> 0) then 4348 result := (GetScanlineFast(PositiveMod(Y+ScanOffset.Y, FScanHeight))+PositiveMod(X+ScanOffset.X, FScanWidth))^ 3854 4349 else 3855 4350 result := BGRAPixelTransparent; … … 3859 4354 procedure TBGRADefaultBitmap.ScanMoveTo(X, Y: Integer); 3860 4355 begin 3861 if FData = nilthen exit;4356 if (FScanWidth = 0) or (FScanHeight = 0) then exit; 3862 4357 LoadFromBitmapIfNeeded; 3863 FScanCurX := PositiveMod(X+ScanOffset.X, F Width);3864 FScanCurY := PositiveMod(Y+ScanOffset.Y, F Height);4358 FScanCurX := PositiveMod(X+ScanOffset.X, FScanWidth); 4359 FScanCurY := PositiveMod(Y+ScanOffset.Y, FScanHeight); 3865 4360 FScanPtr := ScanLine[FScanCurY]; 3866 4361 end; … … 3868 4363 function TBGRADefaultBitmap.ScanNextPixel: TBGRAPixel; 3869 4364 begin 3870 if FData <> nilthen4365 if (FScanWidth <> 0) and (FScanHeight <> 0) then 3871 4366 begin 3872 4367 result := (FScanPtr+FScanCurX)^; 3873 4368 inc(FScanCurX); 3874 if FScanCurX = F Width then //cycle4369 if FScanCurX = FScanWidth then //cycle 3875 4370 FScanCurX := 0; 3876 4371 end … … 3884 4379 iFactX,iFactY: Int32or64; 3885 4380 begin 3886 if FData = nilthen4381 if (FScanWidth = 0) or (FScanHeight = 0) then 3887 4382 begin 3888 4383 result := BGRAPixelTransparent; … … 3892 4387 ix := round(x*256); 3893 4388 iy := round(y*256); 4389 if ScanInterpolationFilter = rfBox then 4390 begin 4391 ix := PositiveMod((ix+128)+(ScanOffset.X shl 8), FScanWidth shl 8) shr 8; 4392 iy := PositiveMod((iy+128)+(ScanOffset.Y shl 8), FScanHeight shl 8) shr 8; 4393 result := (GetScanlineFast(iy)+ix)^; 4394 exit; 4395 end; 3894 4396 iFactX := ix and 255; 3895 4397 iFactY := iy and 255; 3896 ix := PositiveMod(ix+(ScanOffset.X shl 8), F Width shl 8) shr 8;3897 iy := PositiveMod(iy+(ScanOffset.Y shl 8), F Height shl 8) shr 8;4398 ix := PositiveMod(ix+(ScanOffset.X shl 8), FScanWidth shl 8) shr 8; 4399 iy := PositiveMod(iy+(ScanOffset.Y shl 8), FScanHeight shl 8) shr 8; 3898 4400 if (iFactX = 0) and (iFactY = 0) then 3899 4401 begin … … 3920 4422 c: TBGRAPixel; 3921 4423 begin 4424 if (FScanWidth <= 0) or (FScanHeight <= 0) then 4425 begin 4426 if mode = dmSet then 4427 FillDWord(pdest^, count, DWord(BGRAPixelTransparent)); 4428 exit; 4429 end; 3922 4430 case mode of 3923 4431 dmLinearBlend: … … 3936 4444 while count > 0 do 3937 4445 begin 3938 nbCopy := F Width-FScanCurX;4446 nbCopy := FScanWidth-FScanCurX; 3939 4447 if count < nbCopy then nbCopy := count; 3940 4448 move((FScanPtr+FScanCurX)^,pdest^,nbCopy*sizeof(TBGRAPixel)); 3941 4449 inc(pdest,nbCopy); 3942 4450 inc(FScanCurX,nbCopy); 3943 if FScanCurX = F Width then FScanCurX := 0;4451 if FScanCurX = FScanWidth then FScanCurX := 0; 3944 4452 dec(count,nbCopy); 3945 4453 end; … … 3994 4502 p: PBGRAPixel; 3995 4503 n: integer; 4504 colormask: longword; 3996 4505 begin 3997 4506 if CanvasAlphaCorrection then 3998 4507 begin 3999 4508 p := FData; 4509 colormask := longword(BGRA(255,255,255,0)); 4000 4510 for n := NbPixels - 1 downto 0 do 4001 4511 begin 4002 if (longword(p^) and $FFFFFF<> 0) and (p^.alpha = 0) then4512 if (longword(p^) and colormask <> 0) and (p^.alpha = 0) then 4003 4513 p^.alpha := FCanvasOpacity; 4004 4514 Inc(p); … … 4299 4809 Parameters are the bitmap origin, the end of the horizontal axis and the end of the vertical axis. 4300 4810 The output bounds correspond to the pixels that will be affected in the destination. } 4301 procedure TBGRADefaultBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF; 4302 Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte); 4811 procedure TBGRADefaultBitmap.PutImageAffine(AMatrix: TAffineMatrix; 4812 Source: TBGRACustomBitmap; AOutputBounds: TRect; 4813 AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte); 4303 4814 var affine: TBGRAAffineBitmapTransform; 4304 SourceBounds: TRect;4305 begin 4306 if (Source = nil) or ( AOpacity = 0) then exit;4815 sourceBounds: TRect; 4816 begin 4817 if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit; 4307 4818 IntersectRect(AOutputBounds,AOutputBounds,ClipRect); 4308 4819 if IsRectEmpty(AOutputBounds) then exit; 4309 4820 4310 if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and 4311 (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and 4312 (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then 4313 begin 4314 SourceBounds := AOutputBounds; 4315 OffsetRect(SourceBounds, -round(origin.x),-round(origin.y)); 4316 IntersectRect(SourceBounds,SourceBounds,rect(0,0,Source.Width,Source.Height)); 4317 PutImagePart(round(origin.x)+SourceBounds.Left,round(origin.y)+SourceBounds.Top,Source,SourceBounds,AMode,AOpacity); 4318 exit; 4319 end; 4320 4321 { Create affine transformation } 4322 affine := TBGRAAffineBitmapTransform.Create(Source, false, AResampleFilter); 4323 affine.GlobalOpacity := AOpacity; 4324 affine.Fit(Origin,HAxis,VAxis); 4325 FillRect(AOutputBounds,affine,AMode); 4326 affine.Free; 4821 if IsAffineRoughlyTranslation(AMatrix, rect(0,0,Source.Width,Source.Height)) then 4822 begin 4823 sourceBounds := AOutputBounds; 4824 OffsetRect(sourceBounds, -round(AMatrix[1,3]),-round(AMatrix[2,3])); 4825 IntersectRect(sourceBounds,sourceBounds,rect(0,0,Source.Width,Source.Height)); 4826 PutImagePart(round(AMatrix[1,3])+sourceBounds.Left,round(AMatrix[2,3])+sourceBounds.Top,Source,sourceBounds,AMode,AOpacity); 4827 end else 4828 begin 4829 affine := TBGRAAffineBitmapTransform.Create(Source, false, AResampleFilter); 4830 affine.GlobalOpacity := AOpacity; 4831 affine.ViewMatrix := AMatrix; 4832 FillRect(AOutputBounds,affine,AMode); 4833 affine.Free; 4834 end; 4835 end; 4836 4837 function TBGRADefaultBitmap.GetImageAffineBounds(AMatrix: TAffineMatrix; 4838 ASourceBounds: TRect; AClipOutput: boolean): TRect; 4839 const pointMargin = 0.5 - 1/512; 4840 4841 procedure FirstPoint(pt: TPointF); 4842 begin 4843 result.Left := round(pt.X); 4844 result.Top := round(pt.Y); 4845 result.Right := round(pt.X)+1; 4846 result.Bottom := round(pt.Y)+1; 4847 end; 4848 4849 //include specified point in the bounds 4850 procedure IncludePoint(pt: TPointF); 4851 begin 4852 if round(pt.X) < result.Left then result.Left := round(pt.X); 4853 if round(pt.Y) < result.Top then result.Top := round(pt.Y); 4854 if round(pt.X)+1 > result.Right then result.Right := round(pt.X)+1; 4855 if round(pt.Y)+1 > result.Bottom then result.Bottom := round(pt.Y)+1; 4856 end; 4857 4858 begin 4859 result := EmptyRect; 4860 if IsRectEmpty(ASourceBounds) then exit; 4861 if IsAffineRoughlyTranslation(AMatrix,ASourceBounds) then 4862 begin 4863 result := ASourceBounds; 4864 OffsetRect(result,round(AMatrix[1,3]),round(AMatrix[2,3])); 4865 end else 4866 begin 4867 FirstPoint(AMatrix*PointF(ASourceBounds.Left-pointMargin,ASourceBounds.Top-pointMargin)); 4868 IncludePoint(AMatrix*PointF(ASourceBounds.Right-1+pointMargin,ASourceBounds.Top-pointMargin)); 4869 IncludePoint(AMatrix*PointF(ASourceBounds.Left-pointMargin,ASourceBounds.Bottom-1+pointMargin)); 4870 IncludePoint(AMatrix*PointF(ASourceBounds.Right-1+pointMargin,ASourceBounds.Bottom-1+pointMargin)); 4871 end; 4872 if AClipOutput then IntersectRect(result,result,ClipRect); 4327 4873 end; 4328 4874 … … 4434 4980 function TBGRADefaultBitmap.FilterSmooth: TBGRACustomBitmap; 4435 4981 begin 4436 Result := BGRAFilters.FilterBlurRadial Precise(self, 0.3);4982 Result := BGRAFilters.FilterBlurRadial(self, 3, rbPrecise); 4437 4983 end; 4438 4984 … … 4479 5025 end; 4480 5026 4481 function TBGRADefaultBitmap.FilterBlurRadial(radius: integer;5027 function TBGRADefaultBitmap.FilterBlurRadial(radius: single; 4482 5028 blurType: TRadialBlurType): TBGRACustomBitmap; 4483 5029 begin … … 4485 5031 end; 4486 5032 4487 function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radius: integer;5033 function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radius: single; 4488 5034 blurType: TRadialBlurType): TBGRACustomBitmap; 4489 5035 var task: TFilterTask; … … 4497 5043 end; 4498 5044 5045 function TBGRADefaultBitmap.FilterBlurRadial(radiusX, radiusY: single; 5046 blurType: TRadialBlurType): TBGRACustomBitmap; 5047 begin 5048 Result := BGRAFilters.FilterBlurRadial(self, radiusX,radiusY, blurType); 5049 end; 5050 5051 function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radiusX, 5052 radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; 5053 var task: TFilterTask; 5054 begin 5055 task := BGRAFilters.CreateRadialBlurTask(self, ABounds, radiusX,radiusY, blurType); 5056 try 5057 result := task.Execute; 5058 finally 5059 task.Free; 5060 end; 5061 end; 5062 4499 5063 function TBGRADefaultBitmap.FilterPixelate(pixelSize: integer; 4500 5064 useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap; … … 4503 5067 end; 4504 5068 4505 function TBGRADefaultBitmap.FilterBlurMotion(distance: integer;5069 function TBGRADefaultBitmap.FilterBlurMotion(distance: single; 4506 5070 angle: single; oriented: boolean): TBGRACustomBitmap; 4507 5071 begin … … 4509 5073 end; 4510 5074 4511 function TBGRADefaultBitmap.FilterBlurMotion(ABounds: TRect; distance: integer;5075 function TBGRADefaultBitmap.FilterBlurMotion(ABounds: TRect; distance: single; 4512 5076 angle: single; oriented: boolean): TBGRACustomBitmap; 4513 5077 var task: TFilterTask; … … 4539 5103 end; 4540 5104 4541 function TBGRADefaultBitmap.FilterEmboss(angle: single): TBGRACustomBitmap; 4542 begin 4543 Result := BGRAFilters.FilterEmboss(self, angle); 4544 end; 4545 4546 function TBGRADefaultBitmap.FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; 4547 begin 4548 Result := BGRAFilters.FilterEmboss(self, angle, ABounds); 5105 function TBGRADefaultBitmap.FilterEmboss(angle: single; 5106 AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap; 5107 begin 5108 Result := BGRAFilters.FilterEmboss(self, angle, AStrength, AOptions); 5109 end; 5110 5111 function TBGRADefaultBitmap.FilterEmboss(angle: single; ABounds: TRect; 5112 AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap; 5113 begin 5114 Result := BGRAFilters.FilterEmboss(self, angle, ABounds, AStrength, AOptions); 4549 5115 end; 4550 5116 … … 4592 5158 begin 4593 5159 Result := BGRAFilters.FilterRotate(self, origin, angle, correctBlur); 5160 end; 5161 5162 function TBGRADefaultBitmap.FilterAffine(AMatrix: TAffineMatrix; 5163 correctBlur: boolean): TBGRACustomBitmap; 5164 begin 5165 Result := NewBitmap(Width,Height); 5166 Result.PutImageAffine(AMatrix,self,255,correctBlur); 4594 5167 end; 4595 5168 … … 4620 5193 if pix.alpha = 0 then 4621 5194 result := clNone else 4622 result := pix.red + pix.green shl 8 + pix.blue shl 16;5195 result := RGBToColor(pix.red,pix.green,pix.blue); 4623 5196 {$hints on} 4624 5197 end; … … 4651 5224 end; 4652 5225 4653 function TBGRADefaultBitmap.CreateAdaptedPngWriter: TFPWriterPNG; 4654 begin 4655 result := TFPWriterPNG.Create; 4656 result.Indexed := False; 4657 result.UseAlpha := HasTransparentPixels; 4658 result.WordSized := false; 5226 function TBGRADefaultBitmap.GetPenJoinStyle: TPenJoinStyle; 5227 begin 5228 result := FPenStroker.JoinStyle; 5229 end; 5230 5231 procedure TBGRADefaultBitmap.SetPenJoinStyle(const AValue: TPenJoinStyle); 5232 begin 5233 FPenStroker.JoinStyle := AValue; 5234 end; 5235 5236 function TBGRADefaultBitmap.GetPenMiterLimit: single; 5237 begin 5238 result := FPenStroker.MiterLimit; 5239 end; 5240 5241 procedure TBGRADefaultBitmap.SetPenMiterLimit(const AValue: single); 5242 begin 5243 FPenStroker.MiterLimit := AValue; 4659 5244 end; 4660 5245 … … 4825 5410 It is NOT EXACTLY an involution, when applied twice, some color information is lost } 4826 5411 procedure TBGRADefaultBitmap.Negative; 4827 var 4828 p: PBGRAPixel; 4829 n: integer; 4830 begin 4831 LoadFromBitmapIfNeeded; 4832 p := Data; 4833 for n := NbPixels - 1 downto 0 do 4834 begin 4835 if p^.alpha <> 0 then 4836 begin 4837 p^.red := GammaCompressionTab[not GammaExpansionTab[p^.red]]; 4838 p^.green := GammaCompressionTab[not GammaExpansionTab[p^.green]]; 4839 p^.blue := GammaCompressionTab[not GammaExpansionTab[p^.blue]]; 4840 end; 4841 Inc(p); 4842 end; 4843 InvalidateBitmap; 5412 begin 5413 TBGRAFilterScannerNegative.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), True); 4844 5414 end; 4845 5415 4846 5416 procedure TBGRADefaultBitmap.NegativeRect(ABounds: TRect); 4847 var p: PBGRAPixel;4848 xb,yb,xcount: integer;4849 5417 begin 4850 5418 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 4851 xcount := ABounds.Right-ABounds.Left; 4852 for yb := ABounds.Top to ABounds.Bottom-1 do 4853 begin 4854 p := ScanLine[yb]+ABounds.Left; 4855 for xb := xcount-1 downto 0 do 4856 begin 4857 if p^.alpha <> 0 then 4858 begin 4859 p^.red := GammaCompressionTab[not GammaExpansionTab[p^.red]]; 4860 p^.green := GammaCompressionTab[not GammaExpansionTab[p^.green]]; 4861 p^.blue := GammaCompressionTab[not GammaExpansionTab[p^.blue]]; 4862 end; 4863 Inc(p); 4864 end; 4865 end; 5419 TBGRAFilterScannerNegative.ComputeFilterInplace(self, ABounds, True); 4866 5420 end; 4867 5421 … … 4870 5424 It is an involution, i.e it does nothing when applied twice } 4871 5425 procedure TBGRADefaultBitmap.LinearNegative; 4872 var 4873 p: PBGRAPixel; 4874 n: integer; 4875 begin 4876 LoadFromBitmapIfNeeded; 4877 p := Data; 4878 for n := NbPixels - 1 downto 0 do 4879 begin 4880 if p^.alpha <> 0 then 4881 begin 4882 p^.red := not p^.red; 4883 p^.green := not p^.green; 4884 p^.blue := not p^.blue; 4885 end; 4886 Inc(p); 4887 end; 4888 InvalidateBitmap; 5426 begin 5427 TBGRAFilterScannerNegative.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), False); 4889 5428 end; 4890 5429 4891 5430 procedure TBGRADefaultBitmap.LinearNegativeRect(ABounds: TRect); 4892 var p: PBGRAPixel;4893 xb,yb,xcount: integer;4894 5431 begin 4895 5432 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 4896 xcount := ABounds.Right-ABounds.Left; 4897 for yb := ABounds.Top to ABounds.Bottom-1 do 4898 begin 4899 p := ScanLine[yb]+ABounds.Left; 4900 for xb := xcount-1 downto 0 do 4901 begin 4902 if p^.alpha <> 0 then 4903 begin 4904 p^.red := not p^.red; 4905 p^.green := not p^.green; 4906 p^.blue := not p^.blue; 4907 end; 4908 Inc(p); 4909 end; 4910 end; 4911 end; 4912 4913 procedure TBGRADefaultBitmap.InplaceGrayscale; 4914 begin 4915 InplaceGrayscale(rect(0,0,Width,Height)); 4916 end; 4917 4918 procedure TBGRADefaultBitmap.InplaceGrayscale(ABounds: TRect); 4919 var 4920 task: TFilterTask; 4921 begin 4922 task := CreateGrayscaleTask(self, ABounds); 4923 task.Destination := self; 4924 task.Execute; 4925 task.Free; 5433 TBGRAFilterScannerNegative.ComputeFilterInplace(self, ABounds, False); 5434 end; 5435 5436 procedure TBGRADefaultBitmap.InplaceGrayscale(AGammaCorrection: boolean = true); 5437 begin 5438 TBGRAFilterScannerGrayscale.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), AGammaCorrection); 5439 end; 5440 5441 procedure TBGRADefaultBitmap.InplaceGrayscale(ABounds: TRect; AGammaCorrection: boolean = true); 5442 begin 5443 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 5444 TBGRAFilterScannerGrayscale.ComputeFilterInplace(self, ABounds, AGammaCorrection); 5445 end; 5446 5447 procedure TBGRADefaultBitmap.InplaceNormalize(AEachChannel: boolean); 5448 begin 5449 InplaceNormalize(rect(0,0,Width,Height),AEachChannel); 5450 end; 5451 5452 procedure TBGRADefaultBitmap.InplaceNormalize(ABounds: TRect; 5453 AEachChannel: boolean); 5454 var scanner: TBGRAFilterScannerNormalize; 5455 begin 5456 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 5457 scanner := TBGRAFilterScannerNormalize.Create(self,Point(0,0),ABounds,AEachChannel); 5458 FillRect(ABounds,scanner,dmSet); 5459 scanner.Free; 4926 5460 end; 4927 5461 … … 4930 5464 It is an involution, i.e it does nothing when applied twice } 4931 5465 procedure TBGRADefaultBitmap.SwapRedBlue; 5466 begin 5467 TBGRAFilterScannerSwapRedBlue.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), False); 5468 end; 5469 5470 procedure TBGRADefaultBitmap.SwapRedBlue(ARect: TRect); 5471 begin 5472 if not CheckClippedRectBounds(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom) then exit; 5473 TBGRAFilterScannerSwapRedBlue.ComputeFilterInplace(self, ARect, False); 5474 end; 5475 5476 { Convert a grayscale image into a black image with alpha value } 5477 procedure TBGRADefaultBitmap.GrayscaleToAlpha; 4932 5478 var 4933 5479 n: integer; 4934 temp: longword;4935 5480 p: PLongword; 4936 5481 begin … … 4941 5486 exit; 4942 5487 repeat 4943 temp := LEtoN(p^); 4944 p^ := NtoLE(((temp and $FF) shl 16) or ((temp and $FF0000) shr 16) or 4945 temp and $FF00FF00); 5488 p^ := (p^ shr TBGRAPixel_RedShift and $FF) shl TBGRAPixel_AlphaShift; 4946 5489 Inc(p); 4947 5490 Dec(n); … … 4950 5493 end; 4951 5494 4952 { Convert a grayscale image into a black image with alpha value } 4953 procedure TBGRADefaultBitmap.GrayscaleToAlpha; 5495 procedure TBGRADefaultBitmap.AlphaToGrayscale; 4954 5496 var 4955 5497 n: integer; … … 4963 5505 exit; 4964 5506 repeat 4965 temp := LEtoN(p^); 4966 p^ := NtoLE((temp and $FF) shl 24); 4967 Inc(p); 4968 Dec(n); 4969 until n = 0; 4970 InvalidateBitmap; 4971 end; 4972 4973 procedure TBGRADefaultBitmap.AlphaToGrayscale; 4974 var 4975 n: integer; 4976 temp: longword; 4977 p: PLongword; 4978 begin 4979 LoadFromBitmapIfNeeded; 4980 p := PLongword(Data); 4981 n := NbPixels; 4982 if n = 0 then 4983 exit; 4984 repeat 4985 temp := LEtoN(p^ shr 24); 4986 p^ := NtoLE(temp or (temp shl 8) or (temp shl 16) or $FF000000); 5507 temp := (p^ shr TBGRAPixel_AlphaShift) and $ff; 5508 p^ := (temp shl TBGRAPixel_RedShift) or (temp shl TBGRAPixel_GreenShift) 5509 or (temp shl TBGRAPixel_BlueShift) or ($ff shl TBGRAPixel_AlphaShift); 4987 5510 Inc(p); 4988 5511 Dec(n); … … 5045 5568 end; 5046 5569 end; 5570 end; 5571 5572 procedure TBGRADefaultBitmap.ApplyGlobalOpacity(ABounds: TRect; alpha: byte); 5573 var p: PBGRAPixel; 5574 xb,yb,xcount: integer; 5575 begin 5576 if not IntersectRect(ABounds,ABounds,ClipRect) then exit; 5577 xcount := ABounds.Right-ABounds.Left; 5578 for yb := ABounds.Top to ABounds.Bottom-1 do 5579 begin 5580 p := ScanLine[yb]+ABounds.Left; 5581 for xb := xcount-1 downto 0 do 5582 begin 5583 p^.alpha := ApplyOpacity(p^.alpha, alpha); 5584 Inc(p); 5585 end; 5586 end; 5587 InvalidateBitmap; 5047 5588 end; 5048 5589 … … 5127 5668 end; 5128 5669 5129 { Get bounds of non zero values of specified channel }5130 function TBGRADefaultBitmap.GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect;5131 begin5132 result := GetImageBounds([Channel], ANothingValue);5133 end;5134 5135 function TBGRADefaultBitmap.GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect;5136 var5137 minx, miny, maxx, maxy: integer;5138 xb, xb2, yb: integer;5139 p: PDWord;5140 colorMask, colorZeros: DWord;5141 begin5142 maxx := -1;5143 maxy := -1;5144 minx := self.Width;5145 miny := self.Height;5146 colorMask := 0;5147 colorZeros := 0;5148 if cBlue in Channels then5149 begin5150 colorMask := colorMask or $ff;5151 colorZeros:= colorZeros or ANothingValue;5152 end;5153 if cGreen in Channels then5154 begin5155 colorMask := colorMask or $ff00;5156 colorZeros:= colorZeros or (ANothingValue shl 8);5157 end;5158 if cRed in Channels then5159 begin5160 colorMask := colorMask or $ff0000;5161 colorZeros:= colorZeros or (ANothingValue shl 16);5162 end;5163 if cAlpha in Channels then5164 begin5165 colorMask := colorMask or $ff000000;5166 colorZeros:= colorZeros or (ANothingValue shl 24);5167 end;5168 colorMask := NtoLE(colorMask);5169 colorZeros := NtoLE(colorZeros);5170 for yb := 0 to self.Height - 1 do5171 begin5172 p := PDWord(self.ScanLine[yb]);5173 for xb := 0 to self.Width - 1 do5174 begin5175 if (p^ and colorMask) <> colorZeros then5176 begin5177 if xb < minx then5178 minx := xb;5179 if yb < miny then5180 miny := yb;5181 if xb > maxx then5182 maxx := xb;5183 if yb > maxy then5184 maxy := yb;5185 5186 inc(p, self.width-1-xb);5187 for xb2 := self.Width-1 downto xb+1 do5188 begin5189 if (p^ and colorMask) <> colorZeros then5190 begin5191 if xb2 > maxx then5192 maxx := xb2;5193 break;5194 end;5195 dec(p);5196 end;5197 break;5198 end;5199 Inc(p);5200 end;5201 end;5202 if minx > maxx then5203 begin5204 Result.left := 0;5205 Result.top := 0;5206 Result.right := 0;5207 Result.bottom := 0;5208 end5209 else5210 begin5211 Result.left := minx;5212 Result.top := miny;5213 Result.right := maxx + 1;5214 Result.bottom := maxy + 1;5215 end;5216 end;5217 5218 5670 function TBGRADefaultBitmap.GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; 5219 5671 var … … 5378 5830 begin 5379 5831 if LineOrder = riloTopToBottom then 5380 ptrbmp := TBGRAPtrBitmap.Create(Width,Bottom-Top,ScanLine[Top]) else5381 ptrbmp := TBGRAPtrBitmap.Create(Width,Bottom-Top,ScanLine[Bottom-1]);5832 ptrbmp := CreatePtrBitmap(Width,Bottom-Top,ScanLine[Top]) else 5833 ptrbmp := CreatePtrBitmap(Width,Bottom-Top,ScanLine[Bottom-1]); 5382 5834 ptrbmp.LineOrder := LineOrder; 5383 5835 result := ptrbmp; 5384 5836 end; 5385 end;5386 5387 { Draw BGRA data to a canvas with transparency }5388 procedure TBGRADefaultBitmap.DataDrawTransparent(ACanvas: TCanvas;5389 Rect: TRect; AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);5390 var5391 Temp: TBitmap;5392 RawImage: TRawImage;5393 BitmapHandle, MaskHandle: HBitmap;5394 begin5395 RawImage.Init;5396 RawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight);5397 RawImage.Description.LineOrder := ALineOrder;5398 RawImage.Data := PByte(AData);5399 RawImage.DataSize := AWidth * AHeight * sizeof(TBGRAPixel);5400 if not RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False) then5401 raise FPImageException.Create('Failed to create bitmap handle');5402 Temp := TBitmap.Create;5403 Temp.Handle := BitmapHandle;5404 Temp.MaskHandle := MaskHandle;5405 ACanvas.StretchDraw(Rect, Temp);5406 Temp.Free;5407 end;5408 5409 { Draw BGRA data to a canvas without transparency }5410 procedure TBGRADefaultBitmap.DataDrawOpaque(ACanvas: TCanvas;5411 Rect: TRect; AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);5412 var5413 Temp: TBitmap;5414 RawImage: TRawImage;5415 BitmapHandle, MaskHandle: HBitmap;5416 TempData: Pointer;5417 x, y: integer;5418 PTempData: PByte;5419 PSource: PByte;5420 ADataSize: integer;5421 ALineEndMargin: integer;5422 CreateResult: boolean;5423 {$IFDEF DARWIN}5424 TempShift: Byte;5425 {$ENDIF}5426 begin5427 if (AHeight = 0) or (AWidth = 0) then5428 exit;5429 5430 ALineEndMargin := (4 - ((AWidth * 3) and 3)) and 3;5431 ADataSize := (AWidth * 3 + ALineEndMargin) * AHeight;5432 5433 {$HINTS OFF}5434 GetMem(TempData, ADataSize);5435 {$HINTS ON}5436 PTempData := TempData;5437 PSource := AData;5438 5439 {$IFDEF DARWIN} //swap red and blue values5440 for y := 0 to AHeight - 1 do5441 begin5442 for x := 0 to AWidth - 1 do5443 begin5444 PTempData^ := (PSource+2)^;5445 (PTempData+1)^ := (PSource+1)^;5446 (PTempData+2)^ := PSource^;5447 inc(PTempData,3);5448 inc(PSource,4);5449 end;5450 Inc(PTempData, ALineEndMargin);5451 end;5452 {$ELSE}5453 for y := 0 to AHeight - 1 do5454 begin5455 for x := 0 to AWidth - 1 do5456 begin5457 PWord(PTempData)^ := PWord(PSource)^;5458 (PTempData+2)^ := (PSource+2)^;5459 Inc(PTempData,3);5460 Inc(PSource, 4);5461 end;5462 Inc(PTempData, ALineEndMargin);5463 end;5464 {$ENDIF}5465 5466 RawImage.Init;5467 RawImage.Description.Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight);5468 {$IFDEF DARWIN}5469 TempShift := RawImage.Description.RedShift;5470 RawImage.Description.RedShift := RawImage.Description.BlueShift;5471 RawImage.Description.BlueShift := TempShift;5472 {$ENDIF}5473 5474 RawImage.Description.LineOrder := ALineOrder;5475 RawImage.Description.LineEnd := rileDWordBoundary;5476 5477 if integer(RawImage.Description.BytesPerLine) <> AWidth * 3 + ALineEndMargin then5478 begin5479 FreeMem(TempData);5480 raise FPImageException.Create('Line size is inconsistant');5481 end;5482 RawImage.Data := PByte(TempData);5483 RawImage.DataSize := ADataSize;5484 5485 CreateResult := RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False);5486 FreeMem(TempData);5487 5488 if not CreateResult then5489 raise FPImageException.Create('Failed to create bitmap handle');5490 5491 Temp := TBitmap.Create;5492 Temp.Handle := BitmapHandle;5493 Temp.MaskHandle := MaskHandle;5494 ACanvas.StretchDraw(Rect, Temp);5495 Temp.Free;5496 5837 end; 5497 5838 … … 5514 5855 end; 5515 5856 5516 procedure TBGRADefaultBitmap.RebuildBitmap; 5517 var 5518 RawImage: TRawImage; 5519 BitmapHandle, MaskHandle: HBitmap; 5520 begin 5521 if FBitmap <> nil then 5522 FBitmap.Free; 5523 5524 FBitmap := TBitmapTracker.Create(self); 5525 5526 if (FWidth > 0) and (FHeight > 0) then 5527 begin 5528 RawImage.Init; 5529 RawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(FWidth, FHeight); 5530 RawImage.Description.LineOrder := FLineOrder; 5531 RawImage.Data := PByte(FData); 5532 RawImage.DataSize := FWidth * FHeight * sizeof(TBGRAPixel); 5533 if not RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False) then 5534 raise FPImageException.Create('Failed to create bitmap handle'); 5535 FBitmap.Handle := BitmapHandle; 5536 FBitmap.MaskHandle := MaskHandle; 5537 end; 5538 5539 FBitmap.Canvas.AntialiasingMode := amOff; 5540 FBitmapModified := False; 5857 function TBGRADefaultBitmap.CreatePtrBitmap(AWidth, AHeight: integer; 5858 AData: PBGRAPixel): TBGRAPtrBitmap; 5859 begin 5860 result := TBGRAPtrBitmap.Create(AWidth,AHeight,AData); 5541 5861 end; 5542 5862 … … 5546 5866 end; 5547 5867 5548 procedure TBGRADefaultBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer);5549 var5550 bmp: TBitmap;5551 subBmp: TBGRACustomBitmap;5552 subRect: TRect;5553 cw,ch: integer;5554 begin5555 DiscardBitmapChange;5556 cw := CanvasSource.Width;5557 ch := CanvasSource.Height;5558 if (x < 0) or (y < 0) or (x+Width > cw) or5559 (y+Height > ch) then5560 begin5561 FillTransparent;5562 if (x+Width <= 0) or (y+Height <= 0) or5563 (x >= cw) or (y >= ch) then5564 exit;5565 5566 if (x > 0) then subRect.Left := x else subRect.Left := 0;5567 if (y > 0) then subRect.Top := y else subRect.Top := 0;5568 if (x+Width > cw) then subRect.Right := cw else5569 subRect.Right := x+Width;5570 if (y+Height > ch) then subRect.Bottom := ch else5571 subRect.Bottom := y+Height;5572 5573 subBmp := NewBitmap(subRect.Right-subRect.Left,subRect.Bottom-subRect.Top);5574 subBmp.GetImageFromCanvas(CanvasSource,subRect.Left,subRect.Top);5575 PutImage(subRect.Left-x,subRect.Top-y,subBmp,dmSet);5576 subBmp.Free;5577 exit;5578 end;5579 bmp := TBitmap.Create;5580 bmp.PixelFormat := pf24bit;5581 bmp.Width := Width;5582 bmp.Height := Height;5583 bmp.Canvas.CopyRect(Classes.rect(0, 0, Width, Height), CanvasSource,5584 Classes.rect(x, y, x + Width, y + Height));5585 LoadFromRawImage(bmp.RawImage, 255, True);5586 bmp.Free;5587 InvalidateBitmap;5588 end;5589 5590 5868 function TBGRADefaultBitmap.GetNbPixels: integer; 5591 5869 begin … … 5613 5891 end; 5614 5892 5893 procedure TBGRADefaultBitmap.SetLineOrder(AValue: TRawImageLineOrder); 5894 begin 5895 FLineOrder := AValue; 5896 end; 5897 5615 5898 function TBGRADefaultBitmap.GetCanvasOpacity: byte; 5616 5899 begin … … 5625 5908 { TBGRAPtrBitmap } 5626 5909 5910 function TBGRAPtrBitmap.GetLineOrder: TRawImageLineOrder; 5911 begin 5912 result := inherited GetLineOrder; 5913 end; 5914 5915 procedure TBGRAPtrBitmap.SetLineOrder(AValue: TRawImageLineOrder); 5916 begin 5917 inherited SetLineOrder(AValue); 5918 end; 5919 5627 5920 procedure TBGRAPtrBitmap.ReallocData; 5628 5921 begin … … 5633 5926 begin 5634 5927 FData := nil; 5928 end; 5929 5930 procedure TBGRAPtrBitmap.CannotResize; 5931 begin 5932 raise exception.Create('A pointer bitmap cannot be resized'); 5933 end; 5934 5935 procedure TBGRAPtrBitmap.NotImplemented; 5936 begin 5937 raise exception.Create('Not implemented'); 5938 end; 5939 5940 procedure TBGRAPtrBitmap.RebuildBitmap; 5941 begin 5942 NotImplemented; 5943 end; 5944 5945 function TBGRAPtrBitmap.CreateDefaultFontRenderer: TBGRACustomFontRenderer; 5946 begin 5947 result := nil; 5948 NotImplemented; 5949 end; 5950 5951 function TBGRAPtrBitmap.LoadFromRawImage(ARawImage: TRawImage; 5952 DefaultOpacity: byte; AlwaysReplaceAlpha: boolean; 5953 RaiseErrorOnInvalidPixelFormat: boolean): boolean; 5954 begin 5955 result := false; 5956 NotImplemented; 5635 5957 end; 5636 5958 … … 5650 5972 begin 5651 5973 FData := AData; 5974 end; 5975 5976 procedure TBGRAPtrBitmap.DataDrawTransparent(ACanvas: TCanvas; Rect: TRect; 5977 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); 5978 begin 5979 NotImplemented; 5980 end; 5981 5982 procedure TBGRAPtrBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; 5983 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); 5984 begin 5985 NotImplemented; 5986 end; 5987 5988 procedure TBGRAPtrBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer 5989 ); 5990 begin 5991 NotImplemented; 5992 end; 5993 5994 procedure TBGRAPtrBitmap.Assign(Source: TPersistent); 5995 begin 5996 CannotResize; 5997 end; 5998 5999 procedure TBGRAPtrBitmap.TakeScreenshot(ARect: TRect); 6000 begin 6001 CannotResize; 6002 end; 6003 6004 procedure TBGRAPtrBitmap.TakeScreenshotOfPrimaryMonitor; 6005 begin 6006 CannotResize; 6007 end; 6008 6009 procedure TBGRAPtrBitmap.LoadFromDevice(DC: System.THandle); 6010 begin 6011 CannotResize; 6012 end; 6013 6014 procedure TBGRAPtrBitmap.LoadFromDevice(DC: System.THandle; ARect: TRect); 6015 begin 6016 CannotResize; 5652 6017 end; 5653 6018 … … 5655 6020 c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode; 5656 6021 gammaColorCorrection: boolean = True; Sinus: Boolean=False); 5657 var 5658 gradScan : TBGRAGradientScanner; 5659 begin 5660 //handles transparency 5661 if (c1.alpha = 0) and (c2.alpha = 0) then 5662 begin 5663 bmp.FillRect(x, y, x2, y2, BGRAPixelTransparent, mode); 5664 exit; 5665 end; 5666 5667 gradScan := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus); 5668 bmp.FillRect(x,y,x2,y2,gradScan,mode); 5669 gradScan.Free; 6022 begin 6023 bmp.GradientFill(x,y,x2,y2,c1,c2,gtype,o1,o2,mode,gammaColorCorrection,sinus); 5670 6024 end; 5671 6025 … … 5683 6037 end; 5684 6038 5685 ImageHandlers.RegisterImageWriter ('Personal Computer eXchange', 'pcx', TFPWriterPcx);5686 ImageHandlers.RegisterImageReader ('Personal Computer eXchange', 'pcx', TFPReaderPcx);5687 5688 ImageHandlers.RegisterImageWriter ('X Pixmap', 'xpm', TFPWriterXPM);5689 ImageHandlers.RegisterImageReader ('X Pixmap', 'xpm', TFPReaderXPM);5690 5691 6039 end. 5692 6040 -
GraphicTest/Packages/bgrabitmap/bgradithering.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, BGRAFilter s, BGRAPalette, BGRABitmapTypes;8 Classes, SysUtils, BGRAFilterType, BGRAPalette, BGRABitmapTypes; 9 9 10 10 type 11 TOutputPixelProc = procedure(X,Y: NativeInt; AColorIndex: NativeInt; AColor: TBGRAPixel) of object; 11 12 12 13 { TDitheringTask } … … 17 18 FIgnoreAlpha: boolean; 18 19 FPalette: TBGRACustomApproxPalette; 20 FCurrentOutputScanline: PBGRAPixel; 21 FCurrentOutputY: NativeInt; 22 FOutputPixel : TOutputPixelProc; 23 FDrawMode: TDrawMode; 24 procedure OutputPixel(X,Y: NativeInt; {%H-}AColorIndex: NativeInt; AColor: TBGRAPixel); virtual; 25 procedure ApproximateColor(const AColor: TBGRAPixel; out AApproxColor: TBGRAPixel; out AIndex: integer); 19 26 public 27 constructor Create(ASource: IBGRAScanner; APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap; AIgnoreAlpha: boolean; ABounds: TRect); overload; 20 28 constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean; ABounds: TRect); overload; 21 29 constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean); overload; 30 property OnOutputPixel: TOutputPixelProc read FOutputPixel write FOutputPixel; 31 property DrawMode: TDrawMode read FDrawMode write FDrawMode; 22 32 end; 23 33 … … 34 44 protected 35 45 procedure DoExecute; override; 46 end; 47 48 { TDitheringToIndexedImage } 49 50 TDitheringToIndexedImage = class 51 protected 52 FBitOrder: TRawImageBitOrder; 53 FByteOrder: TRawImageByteOrder; 54 FBitsPerPixel: integer; 55 FLineOrder: TRawImageLineOrder; 56 FPalette: TBGRACustomApproxPalette; 57 FIgnoreAlpha: boolean; 58 FTransparentColorIndex: NativeInt; 59 60 //following variables are used during dithering 61 FCurrentScanlineSize: PtrInt; 62 FCurrentData: PByte; 63 FCurrentOutputY: NativeInt; 64 FCurrentOutputScanline: PByte; 65 FCurrentBitOrderMask: NativeInt; 66 FCurrentMaxY: NativeInt; 67 68 procedure SetPalette(AValue: TBGRACustomApproxPalette); 69 procedure SetIgnoreAlpha(AValue: boolean); 70 procedure SetLineOrder(AValue: TRawImageLineOrder); 71 procedure SetBitOrder(AValue: TRawImageBitOrder); virtual; 72 procedure SetBitsPerPixel(AValue: integer); virtual; 73 procedure SetByteOrder(AValue: TRawImageByteOrder); virtual; 74 procedure OutputPixelSubByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual; 75 procedure OutputPixelFullByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual; 76 function GetScanline(Y: NativeInt): Pointer; virtual; 77 function GetTransparentColorIndex: integer; 78 procedure SetTransparentColorIndex(AValue: integer); 79 public 80 constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer); //use platform byte order 81 constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer; AByteOrder: TRawImageByteOrder); //maybe necessary if larger than 8 bits per pixel 82 83 function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap): Pointer; overload; //use minimum scanline size 84 function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer; overload; 85 procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer); overload; //use minimum scanline size 86 procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt); overload; 87 function ComputeMinimumScanlineSize(AWidthInPixels: integer): PtrInt; 88 function AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): pointer; 89 90 //optional customization of format 91 property BitsPerPixel: integer read FBitsPerPixel write SetBitsPerPixel; 92 property BitOrder: TRawImageBitOrder read FBitOrder write SetBitOrder; 93 property ByteOrder: TRawImageByteOrder read FByteOrder write SetByteOrder; 94 property LineOrder: TRawImageLineOrder read FLineOrder write SetLineOrder; 95 96 property Palette: TBGRACustomApproxPalette read FPalette write SetPalette; 97 property IgnoreAlpha: boolean read FIgnoreAlpha write SetIgnoreAlpha; 98 99 //when there is no transparent color in the palette, or that IgnoreAlpha is set to True, 100 //this allows to define the index for the fully transparent color 101 property DefaultTransparentColorIndex: integer read GetTransparentColorIndex write SetTransparentColorIndex; 36 102 end; 37 103 … … 40 106 function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; 41 107 AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload; 108 function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect): TDitheringTask; overload; 109 function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; 110 AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload; 111 112 function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; 42 113 43 114 implementation 115 116 uses BGRABlend; 44 117 45 118 function AbsRGBADiff(const c1, c2: TExpandedPixel): NativeInt; … … 68 141 end; 69 142 143 function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; 144 ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect 145 ): TDitheringTask; 146 begin 147 result := CreateDitheringTask(AAlgorithm, ASource, ADestination, nil, true, ABounds); 148 end; 149 150 function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; 151 ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; 152 APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABounds: TRect 153 ): TDitheringTask; 154 begin 155 result := nil; 156 case AAlgorithm of 157 daNearestNeighbor: result := TNearestColorTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds); 158 daFloydSteinberg: result := TFloydSteinbergDitheringTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds); 159 else raise exception.Create('Unknown algorithm'); 160 end; 161 end; 162 163 function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm; 164 ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; 165 var 166 palette16bit: TBGRA16BitPalette; 167 dither: TDitheringTask; 168 begin 169 palette16bit := TBGRA16BitPalette.Create; 170 dither := CreateDitheringTask(AAlgorithm, ABitmap, palette16bit, false); 171 result := dither.Execute; 172 dither.Free; 173 palette16bit.Free; 174 end; 175 176 { TDitheringToIndexedImage } 177 178 procedure TDitheringToIndexedImage.SetBitsPerPixel(AValue: integer); 179 begin 180 if not (AValue in [1,2,4,8,16,32]) then 181 raise exception.Create('Invalid value for bits per pixel. Allowed values: 1,2,4,8,16,32.'); 182 if FBitsPerPixel=AValue then Exit; 183 FBitsPerPixel:=AValue; 184 end; 185 186 procedure TDitheringToIndexedImage.SetByteOrder(AValue: TRawImageByteOrder); 187 begin 188 if FByteOrder=AValue then Exit; 189 FByteOrder:=AValue; 190 end; 191 192 procedure TDitheringToIndexedImage.OutputPixelSubByte(X, Y: NativeInt; 193 AColorIndex: NativeInt; AColor: TBGRAPixel); 194 var p: PByte; 195 begin 196 if y <> FCurrentOutputY then 197 begin 198 FCurrentOutputY := y; 199 FCurrentOutputScanline := GetScanline(Y); 200 end; 201 if AColorIndex = -1 then AColorIndex := FTransparentColorIndex; 202 case FBitsPerPixel of 203 1: begin 204 p := FCurrentOutputScanline+(x shr 3); 205 p^ := p^ or ((AColorIndex and 1) shl ((x xor FCurrentBitOrderMask) and 7)); 206 end; 207 2: begin 208 p := FCurrentOutputScanline+(x shr 2); 209 p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 3) shl 1)); 210 end; 211 4: begin 212 p := FCurrentOutputScanline+(x shr 1); 213 p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 1) shl 2)); 214 end; 215 end; 216 end; 217 218 procedure TDitheringToIndexedImage.OutputPixelFullByte(X, Y: NativeInt; 219 AColorIndex: NativeInt; AColor: TBGRAPixel); 220 begin 221 if y <> FCurrentOutputY then 222 begin 223 FCurrentOutputY := y; 224 FCurrentOutputScanline := GetScanline(Y); 225 end; 226 if AColorIndex = -1 then AColorIndex := FTransparentColorIndex; 227 case FBitsPerPixel of 228 8: (FCurrentOutputScanline+x)^ := AColorIndex; 229 16: (PWord(FCurrentOutputScanline)+x)^ := AColorIndex; 230 32: (PDWord(FCurrentOutputScanline)+x)^ := AColorIndex; 231 end; 232 end; 233 234 function TDitheringToIndexedImage.GetScanline(Y: NativeInt): Pointer; 235 begin 236 if FLineOrder = riloTopToBottom then 237 result := FCurrentData + Y*FCurrentScanlineSize 238 else 239 result := FCurrentData + (FCurrentMaxY-Y)*FCurrentScanlineSize 240 end; 241 242 procedure TDitheringToIndexedImage.SetIgnoreAlpha(AValue: boolean); 243 begin 244 if FIgnoreAlpha=AValue then Exit; 245 FIgnoreAlpha:=AValue; 246 end; 247 248 procedure TDitheringToIndexedImage.SetTransparentColorIndex(AValue: integer); 249 begin 250 if FTransparentColorIndex=AValue then Exit; 251 FTransparentColorIndex:=AValue; 252 end; 253 254 function TDitheringToIndexedImage.GetTransparentColorIndex: integer; 255 begin 256 result := FTransparentColorIndex; 257 end; 258 259 procedure TDitheringToIndexedImage.SetPalette(AValue: TBGRACustomApproxPalette); 260 begin 261 if FPalette=AValue then Exit; 262 FPalette:=AValue; 263 end; 264 265 procedure TDitheringToIndexedImage.SetLineOrder(AValue: TRawImageLineOrder); 266 begin 267 if FLineOrder=AValue then Exit; 268 FLineOrder:=AValue; 269 end; 270 271 procedure TDitheringToIndexedImage.SetBitOrder(AValue: TRawImageBitOrder); 272 begin 273 if FBitOrder=AValue then Exit; 274 FBitOrder:=AValue; 275 end; 276 277 constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer); 278 begin 279 BitsPerPixel:= ABitsPerPixelForIndices; 280 BitOrder := riboReversedBits; //convention in BMP format 281 {$IFDEF ENDIAN_LITTLE} 282 ByteOrder:= riboLSBFirst; 283 {$ELSE} 284 ByteOrder:= riboMSBFirst; 285 {$ENDIF} 286 Palette := APalette; 287 IgnoreAlpha:= AIgnoreAlpha; 288 end; 289 290 constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer; 291 AByteOrder: TRawImageByteOrder); 292 begin 293 BitsPerPixel:= ABitsPerPixelForIndices; 294 BitOrder := riboReversedBits; //convention in BMP format 295 ByteOrder:= AByteOrder; 296 Palette := APalette; 297 IgnoreAlpha:= AIgnoreAlpha; 298 end; 299 300 function TDitheringToIndexedImage.ComputeMinimumScanlineSize( 301 AWidthInPixels: integer): PtrInt; 302 begin 303 result := (AWidthInPixels*FBitsPerPixel+7) shr 3; 304 end; 305 306 function TDitheringToIndexedImage.AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap; 307 AScanlineSize: PtrInt): pointer; 308 var size: integer; 309 begin 310 size := AScanlineSize * AImage.Height; 311 GetMem(result, size); 312 Fillchar(result^, size, 0); 313 end; 314 315 function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm; 316 AImage: TBGRACustomBitmap): Pointer; 317 begin 318 result := DitherImage(AAlgorithm, AImage, ComputeMinimumScanlineSize(AImage.Width)); 319 end; 320 321 procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm; 322 AImage: TBGRACustomBitmap; AData: Pointer); 323 begin 324 DitherImageTo(AAlgorithm, AImage, AData, ComputeMinimumScanlineSize(AImage.Width)); 325 end; 326 327 function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm; 328 AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer; 329 begin 330 result := AllocateSpaceForIndexedData(AImage, AScanlineSize); 331 DitherImageTo(AAlgorithm, AImage, result, AScanlineSize); 332 end; 333 334 procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm; 335 AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt); 336 var ditherTask: TDitheringTask; 337 begin 338 FCurrentOutputY := -1; 339 FCurrentOutputScanline := nil; 340 FCurrentData := AData; 341 FCurrentMaxY:= AImage.Height-1; 342 FCurrentScanlineSize:= AScanlineSize; 343 344 ditherTask := CreateDitheringTask(AAlgorithm, AImage, FPalette, FIgnoreAlpha); 345 try 346 ditherTask.Inplace := True; //do not allocate destination 347 if BitsPerPixel >= 8 then 348 ditherTask.OnOutputPixel := @OutputPixelFullByte 349 else 350 begin 351 ditherTask.OnOutputPixel:= @OutputPixelSubByte; 352 if BitOrder = riboBitsInOrder then 353 FCurrentBitOrderMask := 0 354 else 355 FCurrentBitOrderMask := $ff; 356 end; 357 ditherTask.Execute; 358 finally 359 ditherTask.Free; 360 end; 361 end; 362 70 363 { TDitheringTask } 364 365 procedure TDitheringTask.OutputPixel(X, Y: NativeInt; AColorIndex: NativeInt; 366 AColor: TBGRAPixel); 367 begin 368 if Y <> FCurrentOutputY then 369 begin 370 FCurrentOutputY := Y; 371 FCurrentOutputScanline := Destination.ScanLine[y]; 372 end; 373 PutPixels(FCurrentOutputScanline+x, @AColor, 1, FDrawMode, 255); 374 end; 375 376 procedure TDitheringTask.ApproximateColor(const AColor: TBGRAPixel; 377 out AApproxColor: TBGRAPixel; out AIndex: integer); 378 begin 379 if FPalette <> nil then 380 begin 381 AIndex := FPalette.FindNearestColorIndex(AColor, FIgnoreAlpha); 382 if AIndex = -1 then 383 AApproxColor := BGRAPixelTransparent 384 else 385 AApproxColor := FPalette.Color[AIndex]; 386 end else 387 begin 388 if AColor.alpha = 0 then 389 begin 390 AApproxColor := BGRAPixelTransparent; 391 AIndex := -1; 392 end else 393 begin 394 AApproxColor := AColor; 395 AIndex := 0; 396 end; 397 end; 398 end; 399 400 constructor TDitheringTask.Create(ASource: IBGRAScanner; 401 APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap; 402 AIgnoreAlpha: boolean; ABounds: TRect); 403 begin 404 FPalette := APalette; 405 SetSource(ASource); 406 FBounds := ABounds; 407 FIgnoreAlpha:= AIgnoreAlpha; 408 FCurrentOutputY := -1; 409 FCurrentOutputScanline:= nil; 410 OnOutputPixel:= @OutputPixel; 411 Destination := ADestination; 412 FDrawMode:= dmSet; 413 end; 71 414 72 415 constructor TDitheringTask.Create(bmp: TBGRACustomBitmap; … … 75 418 begin 76 419 FPalette := APalette; 77 FSource := bmp;420 SetSource(bmp); 78 421 FBounds := ABounds; 79 422 FIgnoreAlpha:= AIgnoreAlpha; 80 if AInPlace then Destination := FSource; 423 FCurrentOutputY := -1; 424 FCurrentOutputScanline:= nil; 425 OnOutputPixel:= @OutputPixel; 426 InPlace := AInPlace; 427 FDrawMode:= dmSet; 81 428 end; 82 429 … … 85 432 begin 86 433 FPalette := APalette; 87 FSource := bmp;434 SetSource(bmp); 88 435 FBounds := rect(0,0,bmp.Width,bmp.Height); 89 436 FIgnoreAlpha:= AIgnoreAlpha; 90 if AInPlace then Destination := FSource; 437 FCurrentOutputY := -1; 438 FCurrentOutputScanline:= nil; 439 OnOutputPixel:= @OutputPixel; 440 InPlace := AInPlace; 441 FDrawMode:= dmSet; 91 442 end; 92 443 … … 125 476 126 477 var 127 p,pNext,pDest: PBGRAPixel; 478 p,pNext: PExpandedPixel; 479 destX,destY: NativeInt; 128 480 orig,cur,approxExp: TExpandedPixel; 129 481 approx: TBGRAPixel; 482 approxIndex: integer; 130 483 curPix,diff: TAccPixel; 131 484 i: NativeInt; 132 485 yWrite: NativeInt; 133 486 tempLine, currentLine, nextLine: TLine; 487 488 nextScan,curScan: PExpandedPixel; 134 489 135 490 function ClampWordDiv(AValue: NativeInt): Word; inline; … … 158 513 setlength(currentLine,w); 159 514 setlength(nextLine,w); 515 curScan := nil; 516 nextScan := RequestSourceExpandedScanLine(FBounds.Left, FBounds.Top, FBounds.Right-FBounds.Left); 160 517 for yWrite := 0 to h-1 do 161 518 begin 162 519 if GetShouldStop(yWrite) then break; 163 p := FSource.ScanLine[yWrite+FBounds.Top]+FBounds.Left; 164 pDest := FDestination.ScanLine[yWrite+FBounds.Top]+FBounds.Left; 520 ReleaseSourceExpandedScanLine(curScan); 521 curScan := nextScan; 522 nextScan := nil; 523 p := curScan; 524 destX := FBounds.Left; 525 destY := yWrite+FBounds.Top; 165 526 if yWrite < h-1 then 166 pNext := FSource.ScanLine[yWrite+FBounds.Top+1]+FBounds.Left 167 else 168 pNext := nil; 527 nextScan := RequestSourceExpandedScanLine(FBounds.Left,yWrite+FBounds.Top+1, FBounds.Right-FBounds.Left); 528 pNext := nextScan; 169 529 if odd(yWrite) then 170 530 begin 171 531 inc(p, w); 172 inc( pDest, w);532 inc(destX, w); 173 533 if pNext<>nil then inc(pNext, w); 174 534 for i := w-1 downto 0 do 175 535 begin 176 536 dec(p); 177 dec( pDest);537 dec(destX); 178 538 if pNext<>nil then dec(pNext); 179 539 if p^.alpha <> 0 then 180 540 begin 181 orig := GammaExpansion(p^);541 orig := p^; 182 542 with currentLine[i] do 183 543 begin … … 191 551 cur.blue := ClampWordDiv(curPix.blue); 192 552 end; 193 approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);553 ApproximateColor(GammaCompression(cur), approx, approxIndex); 194 554 approxExp := GammaExpansion(approx); 195 555 diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift)); … … 207 567 if i > 0 then 208 568 begin 209 if AbsRGBADiff( GammaExpansion((p-1)^),orig) < MaxColorDiffForDiffusion then569 if AbsRGBADiff((p-1)^,orig) < MaxColorDiffForDiffusion then 210 570 AddError(currentLine[i-1], diff, 7); 211 571 end; … … 214 574 if i > 0 then 215 575 begin 216 if AbsRGBADiff( GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then576 if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then 217 577 AddError(nextLine[i-1], diff, 1); 218 578 end; 219 if AbsRGBADiff( GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then579 if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then 220 580 AddError(nextLine[i], diff, 5); 221 581 if i < w-1 then 222 582 begin 223 if AbsRGBADiff( GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then583 if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then 224 584 AddError(nextLine[i+1], diff, 3); 225 585 end; 226 586 end; 227 pDest^ := approx;587 OnOutputPixel(destX,destY,approxIndex,approx); 228 588 end; 229 589 end … … 234 594 if p^.alpha <> 0 then 235 595 begin 236 orig := GammaExpansion(p^);596 orig := p^; 237 597 with currentLine[i] do 238 598 begin … … 246 606 cur.blue := ClampWordDiv(curPix.blue); 247 607 end; 248 approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);608 ApproximateColor(GammaCompression(cur), approx, approxIndex); 249 609 approxExp := GammaExpansion(approx); 250 610 diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift)); … … 262 622 if i < w-1 then 263 623 begin 264 if AbsRGBADiff( GammaExpansion((p+1)^),orig) < MaxColorDiffForDiffusion then624 if AbsRGBADiff((p+1)^,orig) < MaxColorDiffForDiffusion then 265 625 AddError(currentLine[i+1], diff, 7); 266 626 end; 267 if nextLine<> nil then627 if pNext <> nil then 268 628 begin 269 629 if i > 0 then 270 630 begin 271 if AbsRGBADiff( GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then631 if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then 272 632 AddError(nextLine[i-1], diff, 3); 273 633 end; 274 if AbsRGBADiff( GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then634 if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then 275 635 AddError(nextLine[i], diff, 5); 276 636 if i < w-1 then 277 637 begin 278 if AbsRGBADiff( GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then638 if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then 279 639 AddError(nextLine[i+1], diff, 1); 280 640 end; 281 641 end; 282 pDest^ := approx;642 OnOutputPixel(destX,destY,approxIndex,approx); 283 643 end; 284 644 inc(p); 285 inc( pDest);645 inc(destX); 286 646 if pNext<>nil then inc(pNext); 287 647 end; … … 300 660 end; 301 661 end; 302 FDestination.InvalidateBitmap; 662 ReleaseSourceExpandedScanLine(curScan); 663 ReleaseSourceExpandedScanLine(nextScan); 664 Destination.InvalidateBitmap; 303 665 end; 304 666 … … 306 668 307 669 procedure TNearestColorTask.DoExecute; 308 var yb,xb: integer; 309 psrc,pdest: PBGRAPixel; 670 var yb,xb: NativeInt; 671 curScan,psrc: PBGRAPixel; 672 colorIndex: LongInt; 673 colorValue: TBGRAPixel; 310 674 begin 311 675 for yb := FBounds.Top to FBounds.Bottom - 1 do 312 676 begin 313 677 if GetShouldStop(yb) then break; 314 psrc := FSource.ScanLine[yb] + FBounds.Left;315 p dest := FDestination.ScanLine[yb] + FBounds.Left;316 for xb := FBounds. Right - FBounds.Left -1 downto 0do678 curScan := RequestSourceScanLine(FBounds.Left,yb,FBounds.Right-FBounds.Left); 679 psrc := curScan; 680 for xb := FBounds.Left to FBounds.Right-1 do 317 681 begin 318 pdest^ := FPalette.FindNearestColor(psrc^, FIgnoreAlpha);319 inc(pdest);682 ApproximateColor(psrc^, colorValue, colorIndex); 683 OnOutputPixel(xb,yb,colorIndex,colorValue); 320 684 inc(psrc); 321 685 end; 322 end; 323 FDestination.InvalidateBitmap; 686 ReleaseSourceScanLine(curScan); 687 end; 688 Destination.InvalidateBitmap; 324 689 end; 325 690 -
GraphicTest/Packages/bgrabitmap/bgradnetdeserial.pas
r472 r494 184 184 implementation 185 185 186 uses lazutf8classes;186 uses BGRAUTF8; 187 187 188 188 const -
GraphicTest/Packages/bgrabitmap/bgrafillinfo.pas
r472 r494 37 37 function GetBounds: TRect; override; 38 38 39 //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if40 //there is nothing to draw41 function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; override;42 43 39 //check if the point is inside the filling zone 44 40 function IsPointInside(x,y: single; windingMode: boolean): boolean; override; … … 54 50 procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); override; 55 51 52 //can be called after ComputeAndSort or ComputeIntersection to determine the current horizontal slice 53 //so that it can be checked if the intermediates scanlines can be skipped 54 function GetSliceIndex: integer; override; 55 56 56 end; 57 57 … … 61 61 private 62 62 FX, FY, FRX, FRY: single; 63 FSliceIndex: integer; 63 64 function GetCenter: TPointF; 64 65 protected … … 71 72 function GetBounds: TRect; override; 72 73 function SegmentsCurved: boolean; override; 74 function GetSliceIndex: integer; override; 73 75 property Center: TPointF read GetCenter; 74 76 property RadiusX: single read FRX; … … 90 92 function SegmentsCurved: boolean; override; 91 93 destructor Destroy; override; 94 function GetSliceIndex: integer; override; 92 95 property InnerBorder: TFillEllipseInfo read FInnerBorder; 93 96 property OuterBorder: TFillEllipseInfo read FOuterBorder; … … 179 182 constructor Create(const points: array of TPointF); 180 183 destructor Destroy; override; 184 function GetSliceIndex: integer; override; 181 185 end; 182 186 … … 208 212 FFirstWaiting, FFirstDrawing: POnePassRecord; 209 213 FShouldInitializeDrawing: boolean; 214 FSliceIndex: integer; 210 215 procedure ComputeIntersection(cury: single; 211 216 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override; … … 213 218 constructor Create(const points: array of TPointF); 214 219 function CreateIntersectionArray: ArrayOfTIntersectionInfo; override; 220 function GetSliceIndex: integer; override; 215 221 destructor Destroy; override; 216 222 end; … … 243 249 function IsPointInRectangle(x1, y1, x2, y2: single; point: TPointF): boolean; 244 250 251 function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer; 252 bmpDest: TBGRACustomBitmap): boolean; 253 245 254 implementation 246 255 247 256 uses Math; 257 258 function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer; 259 bmpDest: TBGRACustomBitmap): boolean; 260 var clip,bounds: TRect; 261 begin 262 result := true; 263 bounds := AShape.GetBounds; 264 265 if (bounds.Right <= bounds.left) or (bounds.bottom <= bounds.top) then 266 begin 267 result := false; 268 exit; 269 end; 270 271 miny := bounds.top; 272 maxy := bounds.bottom - 1; 273 minx := bounds.left; 274 maxx := bounds.right - 1; 275 276 clip := bmpDest.ClipRect; 277 278 if minx < clip.Left then 279 minx := clip.Left; 280 if maxx < clip.Left then 281 result := false; 282 283 if maxx > clip.Right - 1 then 284 maxx := clip.Right- 1; 285 if minx > clip.Right - 1 then 286 result := false; 287 288 if miny < clip.Top then 289 miny := clip.Top; 290 if maxy < clip.Top then 291 result := false; 292 293 if maxy > clip.Bottom - 1 then 294 maxy := clip.Bottom - 1; 295 if miny > clip.Bottom - 1 then 296 result := false; 297 end; 248 298 249 299 procedure ComputeAliasedRowBounds(x1,x2: single; minx,maxx: integer; out ix1,ix2: integer); … … 345 395 end; 346 396 347 function TFillShapeInfo.ComputeMinMax(out minx, miny, maxx, maxy: integer;348 bmpDest: TBGRACustomBitmap): boolean;349 var clip,bounds: TRect;350 begin351 result := true;352 bounds := GetBounds;353 354 if (bounds.Right <= bounds.left) or (bounds.bottom <= bounds.top) then355 begin356 result := false;357 exit;358 end;359 360 miny := bounds.top;361 maxy := bounds.bottom - 1;362 minx := bounds.left;363 maxx := bounds.right - 1;364 365 clip := bmpDest.ClipRect;366 367 if minx < clip.Left then368 minx := clip.Left;369 if maxx < clip.Left then370 result := false;371 372 if maxx > clip.Right - 1 then373 maxx := clip.Right- 1;374 if minx > clip.Right - 1 then375 result := false;376 377 if miny < clip.Top then378 miny := clip.Top;379 if maxy < clip.Top then380 result := false;381 382 if maxy > clip.Bottom - 1 then383 maxy := clip.Bottom - 1;384 if miny > clip.Bottom - 1 then385 result := false;386 end;387 397 388 398 function TFillShapeInfo.IsPointInside(x, y: single; windingMode: boolean … … 489 499 SortIntersection(inter,nbInter); 490 500 if windingMode then ConvertFromNonZeroWinding(inter,nbInter); 501 end; 502 503 function TFillShapeInfo.GetSliceIndex: integer; 504 begin 505 result := 0; 491 506 end; 492 507 … … 886 901 end; 887 902 903 function TFillPolyInfo.GetSliceIndex: integer; 904 begin 905 Result:= FCurSlice; 906 end; 907 888 908 { TOnePassFillPolyInfo } 889 909 … … 983 1003 p^.nextDrawing := FFirstDrawing; 984 1004 FFirstDrawing := p; 1005 inc(FSliceIndex); 985 1006 end; 986 1007 end … … 1013 1034 FFirstDrawing:= pnext; 1014 1035 p := pnext; 1036 Inc(FSliceIndex); 1015 1037 continue; 1016 1038 end; … … 1056 1078 1057 1079 SortByY; 1080 FSliceIndex := 0; 1058 1081 end; 1059 1082 … … 1086 1109 1087 1110 setlength(result, NbMaxIntersection); 1111 for i := 0 to high(result) do 1112 result[i] := nil; 1113 end; 1114 1115 function TOnePassFillPolyInfo.GetSliceIndex: integer; 1116 begin 1117 Result:= FSliceIndex; 1088 1118 end; 1089 1119 … … 1154 1184 FRY := abs(ry); 1155 1185 WindingFactor := 1; 1186 FSliceIndex:= -1; 1156 1187 end; 1157 1188 … … 1164 1195 begin 1165 1196 Result:= true; 1197 end; 1198 1199 function TFillEllipseInfo.GetSliceIndex: integer; 1200 begin 1201 Result:= FSliceIndex; 1166 1202 end; 1167 1203 … … 1190 1226 inter[nbinter].SetValues( FX + d, windingFactor, 1); 1191 1227 Inc(nbinter); 1228 FSliceIndex := 0; 1229 end else 1230 begin 1231 if cury < FY then 1232 FSliceIndex:= -1 1233 else 1234 FSliceIndex:= 1; 1192 1235 end; 1193 1236 end; … … 1241 1284 FInnerBorder.Free; 1242 1285 inherited Destroy; 1286 end; 1287 1288 function TFillBorderEllipseInfo.GetSliceIndex: integer; 1289 begin 1290 Result:= FOuterBorder.GetSliceIndex; 1243 1291 end; 1244 1292 -
GraphicTest/Packages/bgrabitmap/bgrafilters.pas
r472 r494 10 10 11 11 uses 12 Classes, BGRABitmapTypes ;12 Classes, BGRABitmapTypes, BGRAFilterType, BGRAFilterBlur; 13 13 14 14 type 15 TCheckShouldStopFunc = function(ACurrentY: integer) : boolean of object; 16 17 { TFilterTask } 18 19 TFilterTask = class 20 private 21 FCheckShouldStop: TCheckShouldStopFunc; 22 procedure SetDestination(AValue: TBGRACustomBitmap); 23 protected 24 FDestination: TBGRACustomBitmap; 25 FSource: TBGRACustomBitmap; 26 FCurrentY: integer; 27 function GetShouldStop(ACurrentY: integer): boolean; 28 procedure DoExecute; virtual; abstract; 29 public 30 function Execute: TBGRACustomBitmap; 31 property Destination: TBGRACustomBitmap read FDestination write SetDestination; 32 property CheckShouldStop: TCheckShouldStopFunc read FCheckShouldStop write FCheckShouldStop; 33 property CurrentY: integer read FCurrentY; 34 end; 35 36 { The median filter consist in calculating the median value of pixels. Here 37 a square of 9x9 pixel is considered. The median allow to select the most 38 representative colors. The option parameter allow to choose to smooth the 39 result or not. } 40 function FilterMedian(bmp: TBGRACustomBitmap; 41 Option: TMedianOption): TBGRACustomBitmap; 42 43 { SmartZoom x3 is a filter that upsizes 3 times the picture and add 44 pixels that could be logically expected (horizontal, vertical, diagonal lines) } 45 function FilterSmartZoom3(bmp: TBGRACustomBitmap; 46 Option: TMedianOption): TBGRACustomBitmap; 47 48 { Sharpen filter add more contrast between pixels } 49 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap; 50 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap; 51 52 { A radial blur applies a blur with a circular influence, i.e, each pixel 53 is merged with pixels within the specified radius. There is an exception 54 with rbFast blur, the optimization entails an hyperbolic shape. } 55 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer; 56 blurType: TRadialBlurType): TBGRACustomBitmap; 57 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer; 58 ABlurType: TRadialBlurType): TFilterTask; 59 60 { The precise blur allow to specify the blur radius with subpixel accuracy } 61 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap; 62 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TFilterTask; 63 64 { Motion blur merge pixels in a direction. The oriented parameter specifies 65 if the weights of the pixels are the same along the line or not. } 66 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; 67 angle: single; oriented: boolean): TBGRACustomBitmap; 68 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TFilterTask; 69 70 { General purpose blur filter, with a blur mask as parameter to describe 71 how pixels influence each other } 72 function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap; 73 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask; 74 75 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; 76 77 { Emboss filter compute a color difference in the angle direction } 78 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap; 79 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap; 80 81 { Emboss highlight computes a sort of emboss with 45 degrees angle and 82 with standard selection color (white/black and filled with blue) } 83 function FilterEmbossHighlight(bmp: TBGRACustomBitmap; 84 FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap; 85 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap; 86 FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; 87 88 { Normalize use the whole available range of values, making dark colors darkest possible 89 and light colors lightest possible } 90 function FilterNormalize(bmp: TBGRACustomBitmap; 91 eachChannel: boolean = True): TBGRACustomBitmap; 92 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect; 93 eachChannel: boolean = True): TBGRACustomBitmap; 94 95 { Rotate filter rotate the image and clip it in the bounding rectangle } 96 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF; 97 angle: single; correctBlur: boolean = false): TBGRACustomBitmap; 98 99 { Grayscale converts colored pixel into grayscale with same luminosity } 100 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 101 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; 102 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask; 103 104 { Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil } 105 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 106 107 { Distort the image as if it were on a sphere } 108 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 109 110 { Twirl distortion, i.e. a progressive rotation } 111 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 112 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 113 114 { Distort the image as if it were on a vertical cylinder } 115 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 116 117 { Compute a plane projection towards infinity (SLOW) } 118 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 119 120 implementation 121 122 uses Math, GraphType, Dialogs, BGRATransform, Types, SysUtils; 123 15 TFilterTask = BGRAFilterType.TFilterTask; 16 17 /////////////////////// PIXELWISE FILTERS //////////////////////////////// 124 18 type 125 19 { TGrayscaleTask } 126 20 { Grayscale converts colored pixel into grayscale with same luminosity } 127 21 TGrayscaleTask = class(TFilterTask) 128 22 private … … 134 28 end; 135 29 136 { TBoxBlurTask } 137 138 TBoxBlurTask = class(TFilterTask) 139 private 140 FBounds: TRect; 141 FRadius: integer; 142 public 143 constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer); 144 protected 145 procedure DoExecute; override; 146 end; 147 148 { TRadialBlurTask } 149 150 TRadialBlurTask = class(TFilterTask) 151 private 152 FBounds: TRect; 153 FRadius: integer; 154 FBlurType: TRadialBlurType; 155 public 156 constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; 157 blurType: TRadialBlurType); 158 protected 159 procedure DoExecute; override; 160 end; 161 162 { TCustomBlurTask } 163 164 TCustomBlurTask = class(TFilterTask) 165 private 166 FBounds: TRect; 167 FMask: TBGRACustomBitmap; 168 FMaskOwned: boolean; 169 public 170 constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false); 171 destructor Destroy; override; 172 protected 173 procedure DoExecute; override; 174 end; 175 176 { TRadialPreciseBlurTask } 177 178 TRadialPreciseBlurTask = class(TFilterTask) 179 private 180 FBounds: TRect; 181 FRadius: Single; 182 public 183 constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: single); 184 protected 185 procedure DoExecute; override; 186 end; 187 188 { TMotionBlurTask } 189 190 TMotionBlurTask = class(TFilterTask) 191 private 192 FBounds: TRect; 193 FDistance,FAngle: single; 194 FOriented: boolean; 195 public 196 constructor Create(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance, AAngle: single; AOriented: boolean); 197 protected 198 procedure DoExecute; override; 199 end; 200 201 procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; 202 blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward; 203 procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; ABounds: TRect; 204 radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward; 205 procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single; 206 angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward; 207 procedure FilterBlur(bmp: TBGRACustomBitmap; ABounds: TRect; 208 blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward; 30 { Grayscale converts colored pixel into grayscale with same luminosity } 31 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 32 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; 33 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask; 34 35 { Normalize use the whole available range of values, making dark colors darkest possible 36 and light colors lightest possible } 37 function FilterNormalize(bmp: TBGRACustomBitmap; 38 eachChannel: boolean = True): TBGRACustomBitmap; 39 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect; 40 eachChannel: boolean = True): TBGRACustomBitmap; 41 42 ////////////////////// 3X3 FILTERS //////////////////////////////////////////// 43 44 { Sharpen filter add more contrast between pixels } 45 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap; 46 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap; 47 48 { Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil } 49 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 50 51 { Emboss filter compute a color difference in the angle direction } 52 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; 53 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; 54 55 { Emboss highlight computes a sort of emboss with 45 degrees angle and 56 with standard selection color (white/black and filled with blue) } 57 function FilterEmbossHighlight(bmp: TBGRACustomBitmap; 58 FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap; 59 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap; 60 FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; 61 62 { The median filter consist in calculating the median value of pixels. Here 63 a square of 9x9 pixel is considered. The median allow to select the most 64 representative colors. The option parameter allow to choose to smooth the 65 result or not. } 66 function FilterMedian(bmp: TBGRACustomBitmap; Option: TMedianOption): TBGRACustomBitmap; 67 68 //////////////////////// DEFORMATION FILTERS ///////////////////////////////// 69 70 { Distort the image as if it were on a sphere } 71 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 72 73 { Twirl distortion, i.e. a progressive rotation } 74 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 75 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 76 77 { Distort the image as if it were on a vertical cylinder } 78 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 79 80 { Compute a plane projection towards infinity (SLOW) } 81 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 82 83 { Rotate filter rotate the image and clip it in the bounding rectangle } 84 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF; 85 angle: single; correctBlur: boolean = false): TBGRACustomBitmap; 86 87 ///////////////////////// BLUR FILTERS ////////////////////////////////////// 88 89 { A radial blur applies a blur with a circular influence, i.e, each pixel 90 is merged with pixels within the specified radius. There is an exception 91 with rbFast blur, the optimization entails an hyperbolic shape. } 92 type TRadialBlurTask = BGRAFilterBlur.TRadialBlurTask; 93 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; 94 function FilterBlurRadial(bmp: TBGRACustomBitmap; radiusX: single; radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; 95 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single; ABlurType: TRadialBlurType): TRadialBlurTask; 96 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadiusX,ARadiusY: single; ABlurType: TRadialBlurType): TRadialBlurTask; 97 98 { The precise blur allow to specify the blur radius with subpixel accuracy } 99 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap; deprecated 'Use FilterBlurRadial with blurType:=rbPrecise and radius multiplied by 10'; 100 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TRadialBlurTask; deprecated 'Use CreateRadialBlurTask with blurType:=rbPrecise and radius multiplied by 10'; 101 102 { Motion blur merge pixels in a direction. The oriented parameter specifies 103 if the weights of the pixels are the same along the line or not. } 104 type TMotionBlurTask = BGRAFilterBlur.TMotionBlurTask; 105 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; 106 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TMotionBlurTask; 107 108 { General purpose blur filter, with a blur mask as parameter to describe 109 how pixels influence each other } 110 function FilterBlur(bmp: TBGRACustomBitmap; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TBGRACustomBitmap; 111 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask; 112 113 ////////////////////////////// OTHER FILTERS ///////////////////////////////// 114 115 { SmartZoom x3 is a filter that upsizes 3 times the picture and add 116 pixels that could be logically expected (horizontal, vertical, diagonal lines) } 117 function FilterSmartZoom3(bmp: TBGRACustomBitmap; 118 Option: TMedianOption): TBGRACustomBitmap; 119 120 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; 121 122 implementation 123 124 uses Math, BGRATransform, Types, SysUtils, BGRAFilterScanner; 125 126 /////////////////////// PIXELWISE FILTERS //////////////////////////////// 127 128 { TGrayscaleTask } 129 130 constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect); 131 begin 132 SetSource(bmp); 133 FBounds := ABounds; 134 end; 135 136 procedure TGrayscaleTask.DoExecute; 137 var 138 yb: LongInt; 139 begin 140 if IsRectEmpty(FBounds) then exit; 141 for yb := FBounds.Top to FBounds.bottom - 1 do 142 begin 143 if GetShouldStop(yb) then break; 144 TBGRAFilterScannerGrayscale.ComputeFilterAt(FSource.scanline[yb] + FBounds.left, 145 Destination.scanline[yb] + FBounds.left, FBounds.right-FBounds.left, true); 146 end; 147 Destination.InvalidateBitmap; 148 end; 149 150 { Filter grayscale applies BGRAToGrayscale function to all pixels } 151 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 152 begin 153 result := FilterGrayscale(bmp,rect(0,0,bmp.width,bmp.Height)); 154 end; 155 156 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; 157 var scanner: TBGRAFilterScannerGrayscale; 158 begin 159 result := bmp.NewBitmap(bmp.Width,bmp.Height); 160 scanner := TBGRAFilterScannerGrayscale.Create(bmp,Point(0,0),True); 161 result.FillRect(ABounds,scanner,dmSet); 162 scanner.Free; 163 end; 164 165 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask; 166 begin 167 result := TGrayscaleTask.Create(bmp,ABounds); 168 end; 169 170 function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean 171 ): TBGRACustomBitmap; 172 begin 173 result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel); 174 end; 175 176 { Normalize compute min-max of specified channel and apply an affine transformation 177 to make it use the full range of values } 178 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect; 179 eachChannel: boolean = True): TBGRACustomBitmap; 180 var scanner: TBGRAFilterScannerNormalize; 181 remain: TRect; 182 begin 183 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 184 remain := EmptyRect; 185 if not IntersectRect(remain,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit; 186 scanner := TBGRAFilterScannerNormalize.Create(bmp,Point(0,0),remain,eachChannel); 187 result.FillRect(remain,scanner,dmSet); 188 scanner.Free; 189 end; 190 191 ////////////////////// 3X3 FILTERS //////////////////////////////////////////// 192 193 { This filter compute for each pixel the mean of the eight surrounding pixels, 194 then the difference between this average pixel and the pixel at the center 195 of the square. Finally the difference is added to the new pixel, exagerating 196 its difference with its neighbours. } 197 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap; 198 var scanner: TBGRAFilterScanner; 199 begin 200 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 201 if IsRectEmpty(ABounds) then exit; 202 scanner := TBGRASharpenScanner.Create(bmp,ABounds,AAmount); 203 result.FillRect(ABounds,scanner,dmSet); 204 scanner.Free; 205 end; 206 207 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer 208 ): TBGRACustomBitmap; 209 begin 210 result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount); 211 end; 212 213 { Filter contour computes for each pixel 214 the grayscale difference with surrounding pixels (in intensity and alpha) 215 and draw black pixels when there is a difference } 216 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 217 var scanner: TBGRAContourScanner; 218 begin 219 result := bmp.NewBitmap(bmp.Width, bmp.Height); 220 scanner := TBGRAContourScanner.Create(bmp,rect(0,0,bmp.width,bmp.height)); 221 result.Fill(scanner); 222 scanner.Free; 223 end; 224 225 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap; 226 begin 227 result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height), AStrength, AOptions); 228 end; 229 230 { Emboss filter computes the difference between each pixel and the surrounding pixels 231 in the specified direction. } 232 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect; AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap; 233 var 234 yb, xb: NativeInt; 235 dx, dy: single; 236 idx, idy: NativeInt; 237 x256,y256: NativeInt; 238 cMiddle: TBGRAPixel; 239 hMiddle: THSLAPixel; 240 241 tempPixel, refPixel: TBGRAPixel; 242 pdest: PBGRAPixel; 243 244 bounds: TRect; 245 psrc: PBGRAPixel; 246 redDiff,greenDiff,blueDiff: NativeUInt; 247 diff: NativeInt; 248 begin 249 //compute pixel position and weight 250 dx := cos(angle * Pi / 180); 251 dy := sin(angle * Pi / 180); 252 idx := floor(dx); 253 idy := floor(dy); 254 x256 := trunc((dx-idx)*256); 255 y256 := trunc((dy-idy)*256); 256 257 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 258 if IsRectEmpty(ABounds) then exit; 259 260 bounds := bmp.GetImageBounds; 261 262 if not IntersectRect(bounds, bounds, ABounds) then exit; 263 bounds.Left := max(0, bounds.Left - 1); 264 bounds.Top := max(0, bounds.Top - 1); 265 bounds.Right := min(bmp.Width, bounds.Right + 1); 266 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 267 268 if not (eoTransparent in AOptions) then 269 begin 270 if eoPreserveHue in AOptions then 271 Result.PutImagePart(ABounds.left,ABounds.top,bmp,ABounds,dmSet) 272 else 273 Result.FillRect(ABounds,BGRA(128, 128, 128, 255),dmSet); 274 end; 275 276 //loop through destination 277 for yb := bounds.Top to bounds.bottom - 1 do 278 begin 279 pdest := Result.scanline[yb] + bounds.Left; 280 psrc := bmp.ScanLine[yb]+bounds.Left; 281 282 for xb := bounds.Left+idx to bounds.Right-1+idx do 283 begin 284 refPixel := bmp.GetPixel256(xb,yb+idy,x256,y256); 285 cMiddle := psrc^; 286 inc(psrc); 287 288 if eoPreserveHue in AOptions then 289 begin 290 {$push}{$hints off} 291 diff := ((refPixel.red * refPixel.alpha - cMiddle.red * cMiddle.alpha)+ 292 (refPixel.green * refPixel.alpha - cMiddle.green * cMiddle.alpha)+ 293 (refPixel.blue * refPixel.alpha - cMiddle.blue * cMiddle.alpha))* AStrength div 128; 294 {$pop} 295 if diff > 0 then 296 hMiddle := BGRAToHSLA(refPixel) 297 else 298 hMiddle := BGRAToHSLA(cMiddle); 299 hMiddle.lightness := min(65535,max(0,hMiddle.lightness+diff)); 300 if eoTransparent in AOptions then 301 hMiddle.alpha := min(65535,abs(diff)); 302 pdest^ := HSLAToBGRA(hMiddle); 303 end else 304 begin 305 {$push}{$hints off} 306 redDiff := NativeUInt(max(0, 65536 + (refPixel.red * refPixel.alpha - cMiddle.red * cMiddle.alpha) * AStrength div 64)) shr 9; 307 greenDiff := NativeUInt(max(0, 65536 + (refPixel.green * refPixel.alpha - cMiddle.green * cMiddle.alpha) * AStrength div 64)) shr 9; 308 blueDiff := NativeUInt(max(0, 65536 + (refPixel.blue * refPixel.alpha - cMiddle.blue * cMiddle.alpha) * AStrength div 64)) shr 9; 309 {$pop} 310 if (redDiff <> 128) or (greenDiff <> 128) or (blueDiff <> 128) then 311 begin 312 tempPixel.red := min(255, redDiff); 313 tempPixel.green := min(255, greenDiff); 314 tempPixel.blue := min(255, blueDiff); 315 if eoTransparent in AOptions then 316 begin 317 tempPixel.alpha := min(255,abs(NativeInt(redDiff-128))+abs(NativeInt(greenDiff-128))+abs(NativeInt(blueDiff-128))); 318 pdest^ := tempPixel; 319 end else 320 begin 321 tempPixel.alpha := 255; 322 pdest^ := tempPixel; 323 end; 324 end; 325 end; 326 327 Inc(pdest); 328 end; 329 end; 330 Result.InvalidateBitmap; 331 end; 332 333 { Like general emboss, but with fixed direction and automatic color with transparency } 334 function FilterEmbossHighlight(bmp: TBGRACustomBitmap; 335 FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap; 336 var 337 bounds: TRect; 338 borderColorOverride: boolean; 339 borderColorLevel: Int32or64; 340 scan: TBGRAEmbossHightlightScanner; 341 begin 342 borderColorOverride := DefineBorderColor.alpha <> 0; 343 borderColorLevel := DefineBorderColor.red; 344 345 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 346 347 if borderColorOverride then 348 bounds := bmp.GetImageBounds(cRed, borderColorLevel) 349 else 350 bounds := bmp.GetImageBounds(cRed); 351 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then 352 exit; 353 bounds.Left := max(0, bounds.Left - 1); 354 bounds.Top := max(0, bounds.Top - 1); 355 bounds.Right := min(bmp.Width, bounds.Right + 1); 356 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 357 358 scan := TBGRAEmbossHightlightScanner.Create(bmp, bounds, borderColorOverride); 359 scan.AllowDirectRead := true; 360 scan.FillSelection := FillSelection; 361 if borderColorOverride then scan.SourceBorderColor := DefineBorderColor; 362 Result.FillRect(bounds, scan, dmSet); 363 scan.Free; 364 end; 365 366 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap; 367 FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; 368 var 369 bounds: TRect; 370 borderColorOverride: boolean; 371 borderColorLevel: int32or64; 372 scan: TBGRAEmbossHightlightScanner; 373 begin 374 borderColorOverride := DefineBorderColor.alpha <> 0; 375 borderColorLevel := DefineBorderColor.red; 376 377 if borderColorOverride then 378 bounds := bmp.GetImageBounds(cRed, borderColorLevel) 379 else 380 bounds := bmp.GetImageBounds(cRed); 381 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then 382 begin 383 Result := bmp.NewBitmap(0, 0); 384 exit; 385 end; 386 bounds.Left := max(0, bounds.Left - 1); 387 bounds.Top := max(0, bounds.Top - 1); 388 bounds.Right := min(bmp.Width, bounds.Right + 1); 389 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 390 391 Result := bmp.NewBitmap(bounds.Right-Bounds.Left+1, bounds.Bottom-Bounds.Top+1); 392 inc(Offset.X, bounds.Left); 393 inc(Offset.Y, bounds.Top); 394 395 scan := TBGRAEmbossHightlightScanner.Create(bmp, bounds, borderColorOverride); 396 scan.AllowDirectRead := true; 397 scan.FillSelection := FillSelection; 398 if borderColorOverride then scan.SourceBorderColor := DefineBorderColor; 399 Result.FillRect(rect(0,0,result.Width,result.Height), scan, dmSet, Offset); 400 scan.Free; 401 end; 402 403 { For each component, sort values to get the median } 404 function FilterMedian(bmp: TBGRACustomBitmap; 405 Option: TMedianOption): TBGRACustomBitmap; 406 407 function ComparePixLt(p1, p2: TBGRAPixel): boolean; 408 begin 409 if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then 410 Result := (int32or64(p1.red) shl 8) + (int32or64(p1.green) shl 16) + 411 int32or64(p1.blue) < (int32or64(p2.red) shl 8) + (int32or64(p2.green) shl 16) + 412 int32or64(p2.blue) 413 else 414 Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue); 415 end; 416 417 const 418 nbpix = 9; 419 var 420 yb, xb: int32or64; 421 dx, dy, n, i, j, k: int32or64; 422 a_pixels: array[0..nbpix - 1] of TBGRAPixel; 423 tempPixel, refPixel: TBGRAPixel; 424 tempValue: byte; 425 sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64; 426 tempAlpha: word; 427 bounds: TRect; 428 pdest: PBGRAPixel; 429 begin 430 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 431 432 bounds := bmp.GetImageBounds; 433 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then 434 exit; 435 bounds.Left := max(0, bounds.Left - 1); 436 bounds.Top := max(0, bounds.Top - 1); 437 bounds.Right := min(bmp.Width, bounds.Right + 1); 438 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 439 440 for yb := bounds.Top to bounds.bottom - 1 do 441 begin 442 pdest := Result.scanline[yb] + bounds.left; 443 for xb := bounds.left to bounds.right - 1 do 444 begin 445 n := 0; 446 for dy := -1 to 1 do 447 for dx := -1 to 1 do 448 begin 449 a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy); 450 if a_pixels[n].alpha = 0 then 451 a_pixels[n] := BGRAPixelTransparent; 452 Inc(n); 453 end; 454 for i := 1 to n - 1 do 455 begin 456 j := i; 457 while (j > 1) and (a_pixels[j].alpha < a_pixels[j - 1].alpha) do 458 begin 459 tempValue := a_pixels[j].alpha; 460 a_pixels[j].alpha := a_pixels[j - 1].alpha; 461 a_pixels[j - 1].alpha := tempValue; 462 Dec(j); 463 end; 464 j := i; 465 while (j > 1) and (a_pixels[j].red < a_pixels[j - 1].red) do 466 begin 467 tempValue := a_pixels[j].red; 468 a_pixels[j].red := a_pixels[j - 1].red; 469 a_pixels[j - 1].red := tempValue; 470 Dec(j); 471 end; 472 j := i; 473 while (j > 1) and (a_pixels[j].green < a_pixels[j - 1].green) do 474 begin 475 tempValue := a_pixels[j].green; 476 a_pixels[j].green := a_pixels[j - 1].green; 477 a_pixels[j - 1].green := tempValue; 478 Dec(j); 479 end; 480 j := i; 481 while (j > 1) and (a_pixels[j].blue < a_pixels[j - 1].blue) do 482 begin 483 tempValue := a_pixels[j].blue; 484 a_pixels[j].blue := a_pixels[j - 1].blue; 485 a_pixels[j - 1].blue := tempValue; 486 Dec(j); 487 end; 488 end; 489 490 refPixel := a_pixels[n div 2]; 491 492 if option in [moLowSmooth, moMediumSmooth, moHighSmooth] then 493 begin 494 sumR := 0; 495 sumG := 0; 496 sumB := 0; 497 sumA := 0; 498 BGRAdiv := 0; 499 nbA := 0; 500 501 case option of 502 moHighSmooth, moMediumSmooth: 503 begin 504 j := 2; 505 k := 2; 506 end; 507 else 508 begin 509 j := 1; 510 k := 1; 511 end; 512 end; 513 514 {$hints off} 515 for i := -k to j do 516 begin 517 tempPixel := a_pixels[n div 2 + i]; 518 tempAlpha := tempPixel.alpha; 519 if (option = moMediumSmooth) and ((i = -k) or (i = j)) then 520 tempAlpha := tempAlpha div 2; 521 522 sumR += tempPixel.red * tempAlpha; 523 sumG += tempPixel.green * tempAlpha; 524 sumB += tempPixel.blue * tempAlpha; 525 BGRAdiv += tempAlpha; 526 527 sumA += tempAlpha; 528 Inc(nbA); 529 end; 530 {$hints on} 531 if option = moMediumSmooth then 532 Dec(nbA); 533 534 if (BGRAdiv = 0) then 535 refPixel := BGRAPixelTransparent 536 else 537 begin 538 refPixel.red := round(sumR / BGRAdiv); 539 refPixel.green := round(sumG / BGRAdiv); 540 refPixel.blue := round(sumB / BGRAdiv); 541 refPixel.alpha := round(sumA / nbA); 542 end; 543 end; 544 545 pdest^ := refPixel; 546 Inc(pdest); 547 end; 548 end; 549 end; 550 551 //////////////////////// DEFORMATION FILTERS ///////////////////////////////// 552 553 { Compute the distance for each pixel to the center of the bitmap, 554 calculate the corresponding angle with arcsin, use this angle 555 to determine a distance from the center in the source bitmap } 556 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 557 var 558 cx, cy: single; 559 scanner: TBGRASphereDeformationScanner; 560 begin 561 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 562 cx := bmp.Width / 2 - 0.5; 563 cy := bmp.Height / 2 - 0.5; 564 scanner := TBGRASphereDeformationScanner.Create(bmp,PointF(cx,cy),bmp.Width/2,bmp.Height/2); 565 result.FillEllipseAntialias(cx,cy,bmp.Width/2-0.5,bmp.Height/2-0.5,scanner); 566 scanner.Free; 567 end; 568 569 { Applies twirl scanner. See TBGRATwirlScanner } 570 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 571 var twirl: TBGRATwirlScanner; 572 begin 573 twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent); 574 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 575 result.FillRect(ABounds, twirl, dmSet); 576 twirl.free; 577 end; 578 579 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; 580 ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap; 581 begin 582 result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent); 583 end; 584 585 { Compute the distance for each pixel to the vertical axis of the bitmap, 586 calculate the corresponding angle with arcsin, use this angle 587 to determine a distance from the vertical axis in the source bitmap } 588 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 589 var 590 cx: single; 591 scanner: TBGRAVerticalCylinderDeformationScanner; 592 begin 593 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 594 cx := bmp.Width / 2 - 0.5; 595 scanner := TBGRAVerticalCylinderDeformationScanner.Create(bmp,cx,bmp.Width/2); 596 result.Fill(scanner); 597 scanner.Free; 598 end; 599 600 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 601 const resampleGap=0.6; 602 var 603 cy, x1, x2, y1, y2, z1, z2, h: single; 604 yb: int32or64; 605 resampledBmp: TBGRACustomBitmap; 606 resampledBmpWidth: int32or64; 607 resampledFactor,newResampleFactor: single; 608 sub,resampledSub: TBGRACustomBitmap; 609 partRect: TRect; 610 resampleSizeY : int32or64; 611 begin 612 resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch); 613 resampledBmpWidth := resampledBmp.Width; 614 resampledFactor := 2; 615 Result := bmp.NewBitmap(bmp.Width, bmp.Height*2); 616 cy := result.Height / 2 - 0.5; 617 h := 1; 618 for yb := 0 to ((Result.Height-1) div 2) do 619 begin 620 y1 := (cy - (yb-0.5)) / (cy+0.5); 621 y2 := (cy - (yb+0.5)) / (cy+0.5); 622 if y2 <= 0 then continue; 623 z1 := h/y1; 624 z2 := h/y2; 625 newResampleFactor := 1/(z2-z1)*1.5; 626 627 x1 := (z1+1)/2; 628 x2 := (z2+1)/2; 629 if newResampleFactor <= resampledFactor*resampleGap then 630 begin 631 resampledFactor := newResampleFactor; 632 if resampledBmp <> bmp then resampledBmp.Free; 633 if (x2-x1 >= 1) then resampleSizeY := 1 else 634 resampleSizeY := round(1+((x2-x1)-1)/(1/bmp.Height-1)*(bmp.Height-1)); 635 resampledBmp := bmp.Resample(max(1,round(bmp.Width*resampledFactor)),resampleSizeY,rmSimpleStretch); 636 resampledBmpWidth := resampledBmp.Width; 637 end; 638 639 partRect := Rect(round(-resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x1*resampledBmp.Height), 640 round(resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x2*resampledBmp.Height)+1); 641 if x2-x1 > 1 then 642 begin 643 partRect.Top := 0; 644 partRect.Bottom := 1; 645 end; 646 sub := resampledBmp.GetPart(partRect); 647 if sub <> nil then 648 begin 649 resampledSub := sub.Resample(bmp.Width,1,rmFineResample); 650 result.PutImage(0,yb,resampledSub,dmSet); 651 result.PutImage(0,Result.Height-1-yb,resampledSub,dmSet); 652 resampledSub.free; 653 sub.free; 654 end; 655 end; 656 if resampledBmp <> bmp then resampledBmp.Free; 657 658 if result.Height <> bmp.Height then 659 begin 660 resampledBmp := result.Resample(bmp.Width,bmp.Height,rmSimpleStretch); 661 result.free; 662 result := resampledBmp; 663 end; 664 end; 665 666 { Rotates the image. To do this, loop through the destination and 667 calculates the position in the source bitmap with an affine transformation } 668 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF; 669 angle: single; correctBlur: boolean): TBGRACustomBitmap; 670 begin 671 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 672 Result.PutImageAngle(0,0,bmp,angle,origin.x,origin.y,255,true,correctBlur); 673 end; 674 675 ///////////////////////// BLUR FILTERS ////////////////////////////////////// 676 677 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; 678 var task: TFilterTask; 679 begin 680 task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radius,blurTYpe); 681 result := task.Execute; 682 task.Free; 683 end; 684 685 function FilterBlurRadial(bmp: TBGRACustomBitmap; radiusX: single; radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; 686 var task: TFilterTask; 687 begin 688 task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radiusX,radiusY,blurTYpe); 689 result := task.Execute; 690 task.Free; 691 end; 692 693 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single; ABlurType: TRadialBlurType): TRadialBlurTask; 694 begin 695 result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType); 696 end; 697 698 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; 699 ARadiusX,ARadiusY: single; ABlurType: TRadialBlurType): TRadialBlurTask; 700 begin 701 result := TRadialBlurTask.Create(ABmp,ABounds,ARadiusX,ARadiusY,ABlurType); 702 end; 703 704 { Precise blur } 705 706 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap; 707 var task: TRadialBlurTask; 708 begin 709 task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radius*10,rbPrecise); 710 result := task.Execute; 711 task.Free; 712 end; 713 714 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TRadialBlurTask; 715 begin 716 result := TRadialBlurTask.Create(ABmp,ABounds,ARadius*10,rbPrecise); 717 end; 718 719 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; 720 var task: TFilterTask; 721 begin 722 task := CreateMotionBlurTask(bmp, rect(0,0,bmp.Width,bmp.Height), distance, angle, oriented); 723 result := task.Execute; 724 task.Free; 725 end; 726 727 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; 728 ADistance, AAngle: single; AOriented: boolean): TMotionBlurTask; 729 begin 730 result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented); 731 end; 732 733 function FilterBlur(bmp: TBGRACustomBitmap; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TBGRACustomBitmap; 734 var task: TFilterTask; 735 begin 736 task := TCustomBlurTask.Create(bmp,rect(0,0,bmp.Width,bmp.Height), AMask, AMaskIsThreadSafe); 737 result := task.Execute; 738 task.Free; 739 end; 740 741 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; 742 AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask; 743 begin 744 result := TCustomBlurTask.Create(ABmp, ABounds, AMask, AMaskIsThreadSafe); 745 end; 746 747 ///////////////////////////////////// OTHER FILTERS /////////////////////////// 209 748 210 749 function FilterSmartZoom3(bmp: TBGRACustomBitmap; … … 364 903 end; 365 904 366 { This filter compute for each pixel the mean of the eight surrounding pixels,367 then the difference between this average pixel and the pixel at the center368 of the square. Finally the difference is added to the new pixel, exagerating369 its difference with its neighbours. }370 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;371 var372 yb, xcount: Int32or64;373 dx, dy: Int32or64;374 a_pixels: array[-2..1,-2..1] of PBGRAPixel;375 sumR, sumG, sumB, sumA, {RGBdiv, }nbA: UInt32or64;376 refPixel: TBGRAPixel;377 pdest,ptempPixel: PBGRAPixel;378 bounds: TRect;379 Amount256: boolean;380 lastXincluded: boolean;381 alpha,rgbDivShr1: uint32or64;382 begin383 if IsRectEmpty(ABounds) then exit;384 Amount256 := AAmount = 256;385 Result := bmp.NewBitmap(bmp.Width, bmp.Height);386 387 //determine where pixels are in the bitmap388 bounds := bmp.GetImageBounds;389 if not IntersectRect(bounds, bounds,ABounds) then exit;390 bounds.Left := max(0, bounds.Left - 1);391 bounds.Top := max(0, bounds.Top - 1);392 bounds.Right := min(bmp.Width, bounds.Right + 1);393 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);394 lastXincluded:= bounds.Right < bmp.Width;395 396 //loop through the destination bitmap397 for yb := bounds.Top to bounds.Bottom - 1 do398 begin399 pdest := Result.scanline[yb] + bounds.Left;400 fillchar({%H-}a_pixels,sizeof(a_pixels),0);401 for dy := -1 to 1 do402 if (yb+dy >= bounds.Top) and (yb+dy < bounds.Bottom) then403 a_pixels[dy,1] := bmp.ScanLine[yb+dy]+bounds.Left else404 a_pixels[dy,1] := nil;405 xcount := bounds.right-bounds.left;406 while xcount > 0 do407 begin408 dec(xcount);409 410 //for each pixel, read eight surrounding pixels in the source bitmap411 for dy := -1 to 1 do412 for dx := -1 to 0 do413 a_pixels[dy,dx] := a_pixels[dy,dx+1];414 if (xcount > 0) or lastXincluded then415 begin416 for dy := -1 to 1 do417 if a_pixels[dy,0] <> nil then a_pixels[dy,1] := a_pixels[dy,0]+1;418 end;419 420 //compute sum421 sumR := 0;422 sumG := 0;423 sumB := 0;424 sumA := 0;425 //RGBdiv := 0;426 nbA := 0;427 428 {$hints off}429 for dy := -1 to 1 do430 for dx := -1 to 1 do431 if (dx<>0) or (dy<>0) then432 begin433 ptempPixel := a_pixels[dy,dx];434 if ptempPixel <> nil then435 begin436 alpha := ptempPixel^.alpha;437 sumR += ptempPixel^.red * alpha;438 sumG += ptempPixel^.green * alpha;439 sumB += ptempPixel^.blue * alpha;440 //RGBdiv += alpha;441 sumA += alpha;442 Inc(nbA);443 end;444 end;445 {$hints on}446 447 //we finally have an average pixel448 if ({RGBdiv}sumA = 0) then449 refPixel := BGRAPixelTransparent450 else451 begin452 rgbDivShr1:= {RGBDiv}sumA shr 1;453 refPixel.red := (sumR + rgbDivShr1) div {RGBdiv}sumA;454 refPixel.green := (sumG + rgbDivShr1) div {RGBdiv}sumA;455 refPixel.blue := (sumB + rgbDivShr1) div {RGBdiv}sumA;456 refPixel.alpha := (sumA + nbA shr 1) div nbA;457 end;458 459 //read the pixel at the center of the square460 ptempPixel := a_pixels[0,0];461 if refPixel <> BGRAPixelTransparent then462 begin463 //compute sharpened pixel by adding the difference464 if not Amount256 then465 pdest^ := BGRA( max(0, min($FFFF, Int32or64(ptempPixel^.red shl 8) +466 AAmount*(ptempPixel^.red - refPixel.red))) shr 8,467 max(0, min($FFFF, Int32or64(ptempPixel^.green shl 8) +468 AAmount*(ptempPixel^.green - refPixel.green))) shr 8,469 max(0, min($FFFF, Int32or64(ptempPixel^.blue shl 8) +470 AAmount*(ptempPixel^.blue - refPixel.blue))) shr 8,471 max(0, min($FFFF, Int32or64(ptempPixel^.alpha shl 8) +472 AAmount*(ptempPixel^.alpha - refPixel.alpha))) shr 8 )473 else474 pdest^ := BGRA( max(0, min(255, (ptempPixel^.red shl 1) - refPixel.red)),475 max(0, min(255, (ptempPixel^.green shl 1) - refPixel.green)),476 max(0, min(255, (ptempPixel^.blue shl 1) - refPixel.blue)),477 max(0, min(255, (ptempPixel^.alpha shl 1) - refPixel.alpha)));478 end else479 pdest^ := ptempPixel^;480 Inc(pdest);481 end;482 end;483 Result.InvalidateBitmap;484 end;485 486 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer487 ): TBGRACustomBitmap;488 begin489 result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount);490 end;491 492 { Precise blur builds a blur mask with a gradient fill and use493 general purpose blur }494 procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;495 ABounds: TRect; radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);496 var497 blurShape: TBGRACustomBitmap;498 intRadius: integer;499 begin500 if radius = 0 then501 begin502 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);503 exit;504 end;505 intRadius := ceil(radius);506 blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);507 blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,508 BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF(509 intRadius - radius - 1, intRadius), dmSet);510 FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);511 blurShape.Free;512 end;513 514 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single515 ): TBGRACustomBitmap;516 begin517 result := bmp.NewBitmap(bmp.Width,bmp.Height);518 FilterBlurRadialPrecise(bmp, rect(0,0,bmp.Width,bmp.Height), radius, result, nil);519 end;520 521 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;522 ARadius: single): TFilterTask;523 begin524 result := TRadialPreciseBlurTask.Create(ABmp,ABounds,ARadius);525 end;526 527 { This is a clever solution for fast computing of the blur528 effect : it stores an array of vertical sums forming a square529 around the pixel which moves with it. For each new pixel,530 the vertical sums are kept except for the last column of531 the square }532 procedure FilterBlurFast(bmp: TBGRACustomBitmap; ABounds: TRect;533 radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);534 {$IFDEF CPU64}{$DEFINE FASTBLUR_DOUBLE}{$ENDIF}535 type536 TRowSum = record537 sumR,sumG,sumB,rgbDiv,sumA,aDiv: uint32or64;538 end;539 TExtendedRowValue = {$IFDEF FASTBLUR_DOUBLE}double{$ELSE}uint64{$ENDIF};540 TExtendedRowSum = record541 sumR,sumG,sumB,rgbDiv,sumA,aDiv: TExtendedRowValue;542 end;543 544 function ComputeExtendedAverage(sum: TExtendedRowSum): TBGRAPixel;545 {$IFDEF FASTBLUR_DOUBLE}546 var v: uint32or64;547 {$ENDIF}548 begin549 {$IFDEF FASTBLUR_DOUBLE}550 v := round(sum.sumA/sum.aDiv);551 if v > 255 then result.alpha := 255 else result.alpha := v;552 v := round(sum.sumR/sum.rgbDiv);553 if v > 255 then result.red := 255 else result.red := v;554 v := round(sum.sumG/sum.rgbDiv);555 if v > 255 then result.green := 255 else result.green := v;556 v := round(sum.sumB/sum.rgbDiv);557 if v > 255 then result.blue := 255 else result.blue := v;558 {$ELSE}559 result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;560 result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;561 result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;562 result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;563 {$ENDIF}564 end;565 566 function ComputeClampedAverage(sum: TRowSum): TBGRAPixel;567 var v: UInt32or64;568 begin569 v := (sum.sumA+sum.aDiv shr 1) div sum.aDiv;570 if v > 255 then result.alpha := 255 else result.alpha := v;571 v := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;572 if v > 255 then result.red := 255 else result.red := v;573 v := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;574 if v > 255 then result.green := 255 else result.green := v;575 v := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;576 if v > 255 then result.blue := 255 else result.blue := v;577 end;578 579 function ComputeAverage(sum: TRowSum): TBGRAPixel;580 begin581 result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;582 result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;583 result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;584 result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;585 end;586 587 {$I blurfast.inc}588 589 { Normal radial blur compute a blur mask with a GradientFill and590 then posterize to optimize general purpose blur }591 procedure FilterBlurRadialNormal(bmp: TBGRACustomBitmap;592 ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);593 var594 blurShape: TBGRACustomBitmap;595 n: Int32or64;596 p: PBGRAPixel;597 begin598 if radius = 0 then599 begin600 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);601 exit;602 end;603 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);604 blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,605 BGRABlack, gtRadial, pointF(radius, radius), pointF(-0.5, radius), dmSet);606 p := blurShape.Data;607 for n := 0 to blurShape.NbPixels-1 do608 begin609 p^.red := p^.red and $F0;610 p^.green := p^.red;611 p^.blue := p^.red;612 inc(p);613 end;614 FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);615 blurShape.Free;616 end;617 618 { Blur disk creates a disk mask with a FillEllipse }619 procedure FilterBlurDisk(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);620 var621 blurShape: TBGRACustomBitmap;622 begin623 if radius = 0 then624 begin625 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);626 exit;627 end;628 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);629 blurShape.Fill(BGRABlack);630 blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite);631 FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);632 blurShape.Free;633 end;634 635 { Corona blur use a circle as mask }636 procedure FilterBlurCorona(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);637 var638 blurShape: TBGRACustomBitmap;639 begin640 if radius = 0 then641 begin642 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);643 exit;644 end;645 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);646 blurShape.Fill(BGRABlack);647 blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1);648 FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);649 blurShape.Free;650 end;651 652 function FilterBlurBox(bmp: TBGRACustomBitmap; radius: integer; ADestination: TBGRACustomBitmap): TBGRACustomBitmap;653 var task: TBoxBlurTask;654 begin655 task := TBoxBlurTask.Create(bmp, rect(0,0,bmp.Width,bmp.Height), radius);656 task.Destination := ADestination;657 result := task.Execute;658 task.Free;659 end;660 661 procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;662 blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);663 begin664 if radius = 0 then665 begin666 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);667 exit;668 end;669 case blurType of670 rbCorona: FilterBlurCorona(bmp, ABounds, radius, ADestination, ACheckShouldStop);671 rbDisk: FilterBlurDisk(bmp, ABounds, radius, ADestination, ACheckShouldStop);672 rbNormal: FilterBlurRadialNormal(bmp, ABounds, radius, ADestination, ACheckShouldStop);673 rbFast: FilterBlurFast(bmp, ABounds, radius, ADestination, ACheckShouldStop);674 rbPrecise: FilterBlurRadialPrecise(bmp, ABounds, radius / 10, ADestination, ACheckShouldStop);675 rbBox: FilterBlurBox(bmp, radius, ADestination);676 end;677 end;678 679 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;680 blurType: TRadialBlurType): TBGRACustomBitmap;681 begin682 if blurType = rbBox then683 begin684 result := FilterBlurBox(bmp,radius,nil);685 end else686 begin687 result := bmp.NewBitmap(bmp.width,bmp.Height);688 FilterBlurRadial(bmp, rect(0,0,bmp.Width,bmp.height), radius, blurType,result,nil);689 end;690 end;691 692 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;693 ABlurType: TRadialBlurType): TFilterTask;694 begin695 if ABlurType = rbBox then696 result := TBoxBlurTask.Create(ABmp,ABounds,ARadius)697 else698 result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType);699 end;700 701 { This filter draws an antialiased line to make the mask, and702 if the motion blur is oriented, does a GradientFill to orient it }703 procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;704 angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);705 var706 blurShape: TBGRACustomBitmap;707 intRadius: integer;708 dx, dy, d: single;709 begin710 if distance < 1e-6 then711 begin712 ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);713 exit;714 end;715 intRadius := ceil(distance / 2);716 blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);717 d := distance / 2;718 dx := cos(angle * Pi / 180);719 dy := sin(angle * Pi / 180);720 blurShape.Fill(BGRABlack);721 blurShape.DrawLineAntialias(intRadius - dx * d, intRadius - dy *722 d, intRadius + dx * d, intRadius + dy * d, BGRAWhite, 1, True);723 if oriented then724 blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height,725 BGRAPixelTransparent, BGRABlack, gtRadial, pointF(intRadius -726 dx * d, intRadius - dy * d),727 pointF(intRadius + dx * (d + 0.5), intRadius + dy * (d + 0.5)),728 dmFastBlend, False);729 FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);730 blurShape.Free;731 end;732 733 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;734 angle: single; oriented: boolean): TBGRACustomBitmap;735 begin736 result := bmp.NewBitmap(bmp.Width,bmp.Height);737 FilterBlurMotion(bmp,rect(0,0,bmp.Width,bmp.Height),distance,angle,oriented,result,nil);738 end;739 740 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;741 ADistance, AAngle: single; AOriented: boolean): TFilterTask;742 begin743 result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented);744 end;745 746 { General purpose blur : compute pixel sum according to the mask and then747 compute only difference while scanning from the left to the right }748 procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;749 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;750 procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;751 blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;752 procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;753 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;754 procedure FilterBlurMask64(bmp: TBGRACustomBitmap;755 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;756 757 //make sure value is in the range 0..255758 function clampByte(value: Int32or64): byte; inline;759 begin760 if value < 0 then result := 0 else761 if value > 255 then result := 255 else762 result := value;763 end;764 765 905 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; 766 906 useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap; … … 820 960 end; 821 961 822 function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;823 begin824 result := bmp.NewBitmap(bmp.Width,bmp.Height);825 FilterBlur(bmp,rect(0,0,bmp.Width,bmp.Height),blurMask,result,nil);826 end;827 828 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;829 AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean): TFilterTask;830 begin831 result := TCustomBlurTask.Create(ABmp,ABounds,AMask,AMaskIsThreadSafe);832 end;833 834 procedure FilterBlur(bmp: TBGRACustomBitmap;835 ABounds: TRect; blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);836 {$IFDEF CPU64}837 begin838 FilterBlurMask64(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);839 end;840 {$ELSE}841 var842 maskSum: int64;843 i: Int32or64;844 p: PBGRAPixel;845 maskShift: integer;846 begin847 maskSum := 0;848 p := blurMask.data;849 for i := 0 to blurMask.NbPixels-1 do850 begin851 inc(maskSum,p^.red);852 inc(p);853 end;854 maskShift := 0;855 while maskSum > 32768 do856 begin857 inc(maskShift);858 maskSum := maskSum shr 1;859 end;860 //check if sum can be stored in a 32-bit signed integer861 if maskShift = 0 then862 FilterBlurSmallMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop) else863 {$IFDEF CPU32}864 if maskShift < 8 then865 FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift,ABounds,ADestination,ACheckShouldStop) else866 {$ENDIF}867 FilterBlurBigMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);868 end;869 {$ENDIF}870 871 //32-bit blur with shift872 procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;873 blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);874 875 var876 sumR, sumG, sumB, sumA, Adiv, RGBdiv : integer;877 878 function ComputeAverage: TBGRAPixel; inline;879 begin880 result.alpha := (sumA + Adiv shr 1) div Adiv;881 if result.alpha = 0 then882 result := BGRAPixelTransparent883 else884 begin885 result.red := clampByte((sumR + RGBdiv shr 1) div RGBdiv);886 result.green := clampByte((sumG + RGBdiv shr 1) div RGBdiv);887 result.blue := clampByte((sumB + RGBdiv shr 1) div RGBdiv);888 end;889 end;890 891 {$define PARAM_MASKSHIFT}892 {$I blurnormal.inc}893 894 //32-bit blur895 procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;896 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);897 898 var899 sumR, sumG, sumB, sumA, Adiv : integer;900 901 function ComputeAverage: TBGRAPixel; inline;902 begin903 result.alpha := (sumA + Adiv shr 1) div Adiv;904 if result.alpha = 0 then905 result := BGRAPixelTransparent906 else907 begin908 result.red := clampByte((sumR + sumA shr 1) div sumA);909 result.green := clampByte((sumG + sumA shr 1) div sumA);910 result.blue := clampByte((sumB + sumA shr 1) div sumA);911 end;912 end;913 914 {$I blurnormal.inc}915 916 //64-bit blur917 procedure FilterBlurMask64(bmp: TBGRACustomBitmap;918 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);919 920 var921 sumR, sumG, sumB, sumA, Adiv : int64;922 923 function ComputeAverage: TBGRAPixel; inline;924 begin925 result.alpha := (sumA + Adiv shr 1) div Adiv;926 if result.alpha = 0 then927 result := BGRAPixelTransparent928 else929 begin930 result.red := clampByte((sumR + sumA shr 1) div sumA);931 result.green := clampByte((sumG + sumA shr 1) div sumA);932 result.blue := clampByte((sumB + sumA shr 1) div sumA);933 end;934 end;935 936 {$I blurnormal.inc}937 938 //floating point blur939 procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;940 blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);941 942 var943 sumR, sumG, sumB, sumA, Adiv : single;944 945 function ComputeAverage: TBGRAPixel; inline;946 begin947 result.alpha := round(sumA/Adiv);948 if result.alpha = 0 then949 result := BGRAPixelTransparent950 else951 begin952 result.red := clampByte(round(sumR/sumA));953 result.green := clampByte(round(sumG/sumA));954 result.blue := clampByte(round(sumB/sumA));955 end;956 end;957 958 {$I blurnormal.inc}959 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;960 begin961 result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height));962 end;963 964 { Emboss filter computes the difference between each pixel and the surrounding pixels965 in the specified direction. }966 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;967 var968 yb, xb: Int32or64;969 dx, dy: single;970 idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: Int32or64;971 w: array[1..4] of single;972 iw: uint32or64;973 c: array[0..4] of TBGRAPixel;974 975 i: Int32or64;976 sumR, sumG, sumB, sumA, RGBdiv, Adiv: UInt32or64;977 tempPixel, refPixel: TBGRAPixel;978 pdest: PBGRAPixel;979 980 bounds: TRect;981 onHorizBorder: boolean;982 psrc: array[-1..1] of PBGRAPixel;983 begin984 if IsRectEmpty(ABounds) then exit;985 //compute pixel position and weight986 dx := cos(angle * Pi / 180);987 dy := sin(angle * Pi / 180);988 idx1 := floor(dx);989 idy1 := floor(dy);990 idx2 := ceil(dx);991 idy2 := ceil(dy);992 idx3 := idx1;993 idy3 := idy2;994 idx4 := idx2;995 idy4 := idy1;996 997 w[1] := (1 - abs(idx1 - dx)) * (1 - abs(idy1 - dy));998 w[2] := (1 - abs(idx2 - dx)) * (1 - abs(idy2 - dy));999 w[3] := (1 - abs(idx3 - dx)) * (1 - abs(idy3 - dy));1000 w[4] := (1 - abs(idx4 - dx)) * (1 - abs(idy4 - dy));1001 1002 //fill with gray1003 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1004 Result.Fill(BGRA(128, 128, 128, 255));1005 1006 bounds := bmp.GetImageBounds;1007 if not IntersectRect(bounds, bounds, ABounds) then exit;1008 bounds.Left := max(0, bounds.Left - 1);1009 bounds.Top := max(0, bounds.Top - 1);1010 bounds.Right := min(bmp.Width, bounds.Right + 1);1011 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);1012 1013 //loop through destination1014 for yb := bounds.Top to bounds.bottom - 1 do1015 begin1016 pdest := Result.scanline[yb] + bounds.Left;1017 onHorizBorder:= (yb=0) or (yb=bmp.Height-1);1018 psrc[0] := bmp.ScanLine[yb]+bounds.Left;1019 if (yb>0) then psrc[-1] := bmp.ScanLine[yb-1]+bounds.Left else psrc[-1] := nil;1020 if (yb<bmp.Height-1) then psrc[1] := bmp.ScanLine[yb+1]+bounds.Left else psrc[1] := nil;1021 for xb := bounds.Left to bounds.Right - 1 do1022 begin1023 c[0] := psrc[0]^;1024 if onHorizBorder or (xb=0) or (xb=bmp.Width-1) then1025 begin1026 c[1] := bmp.getPixel(xb + idx1, yb + idy1);1027 c[2] := bmp.getPixel(xb + idx2, yb + idy2);1028 c[3] := bmp.getPixel(xb + idx3, yb + idy3);1029 c[4] := bmp.getPixel(xb + idx4, yb + idy4);1030 end else1031 begin1032 c[1] := (psrc[idy1]+idx1)^;1033 c[2] := (psrc[idy2]+idx2)^;1034 c[3] := (psrc[idy3]+idx3)^;1035 c[4] := (psrc[idy4]+idx4)^;1036 end;1037 1038 sumR := 0;1039 sumG := 0;1040 sumB := 0;1041 sumA := 0;1042 Adiv := 0;1043 RGBdiv := 0;1044 1045 //compute sum1046 {$hints off}1047 for i := 1 to 4 do1048 begin1049 tempPixel := c[i];1050 if tempPixel.alpha = 0 then1051 tempPixel := c[0];1052 iw := round(w[i] * tempPixel.alpha);1053 sumR += tempPixel.red * iw;1054 sumG += tempPixel.green * iw;1055 sumB += tempPixel.blue * iw;1056 RGBdiv += iw;1057 sumA += iw;1058 Adiv += round(w[i] * 255);1059 end;1060 {$hints on}1061 1062 //average1063 if (Adiv = 0) or (RGBdiv = 0) then1064 refPixel := c[0]1065 else1066 begin1067 refPixel.red := (sumR + RGBdiv shr 1) div RGBdiv;1068 refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv;1069 refPixel.blue := (sumB + RGBdiv shr 1) div RGBdiv;1070 refPixel.alpha := (sumA * 255 + Adiv shr 1) div Adiv;1071 end;1072 1073 //difference with center pixel1074 {$hints off}1075 tempPixel.red := max(0, min(512 * 255, 65536 + refPixel.red *1076 refPixel.alpha - c[0].red * c[0].alpha)) shr 9;1077 tempPixel.green := max(0, min(512 * 255, 65536 + refPixel.green *1078 refPixel.alpha - c[0].green * c[0].alpha)) shr 9;1079 tempPixel.blue := max(0, min(512 * 255, 65536 + refPixel.blue *1080 refPixel.alpha - c[0].blue * c[0].alpha)) shr 9;1081 {$hints on}1082 tempPixel.alpha := 255;1083 pdest^ := tempPixel;1084 Inc(pdest);1085 inc(psrc[0]);1086 if psrc[-1] <> nil then inc(psrc[-1]);1087 if psrc[1] <> nil then inc(psrc[1]);1088 end;1089 end;1090 Result.InvalidateBitmap;1091 end;1092 1093 { Like general emboss, but with fixed direction and automatic color with transparency }1094 function FilterEmbossHighlight(bmp: TBGRACustomBitmap;1095 FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;1096 var1097 yb, xb: Int32or64;1098 c0,c1,c2,c3,c4,c5,c6: Int32or64;1099 1100 bmpWidth, bmpHeight: Int32or64;1101 slope, h: byte;1102 sum: Int32or64;1103 tempPixel, highlight: TBGRAPixel;1104 pdest, psrcUp, psrc, psrcDown: PBGRAPixel;1105 1106 bounds: TRect;1107 borderColorOverride: boolean;1108 borderColorLevel: Int32or64;1109 1110 currentBorderColor: Int32or64;1111 begin1112 borderColorOverride := DefineBorderColor.alpha <> 0;1113 borderColorLevel := DefineBorderColor.red;1114 1115 bmpWidth := bmp.Width;1116 bmpHeight := bmp.Height;1117 Result := bmp.NewBitmap(bmpWidth, bmpHeight);1118 1119 if borderColorOverride then1120 bounds := bmp.GetImageBounds(cRed, borderColorLevel)1121 else1122 bounds := bmp.GetImageBounds(cRed);1123 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then1124 exit;1125 bounds.Left := max(0, bounds.Left - 1);1126 bounds.Top := max(0, bounds.Top - 1);1127 bounds.Right := min(bmpWidth, bounds.Right + 1);1128 bounds.Bottom := min(bmpHeight, bounds.Bottom + 1);1129 1130 currentBorderColor := borderColorLevel;1131 for yb := bounds.Top to bounds.Bottom - 1 do1132 begin1133 pdest := Result.scanline[yb] + bounds.Left;1134 1135 if yb > 0 then1136 psrcUp := bmp.Scanline[yb - 1] + bounds.Left1137 else1138 psrcUp := nil;1139 psrc := bmp.scanline[yb] + bounds.Left;1140 if yb < bmpHeight - 1 then1141 psrcDown := bmp.scanline[yb + 1] + bounds.Left1142 else1143 psrcDown := nil;1144 1145 for xb := bounds.Left to bounds.Right - 1 do1146 begin1147 c0 := pbyte(psrc)^;1148 if not borderColorOverride then currentBorderColor := c0;1149 if (xb = 0) then1150 begin1151 c1 := currentBorderColor;1152 c2 := currentBorderColor;1153 end1154 else1155 begin1156 if psrcUp <> nil then1157 c1 := pbyte(psrcUp - 1)^1158 else1159 c1 := currentBorderColor;1160 c2 := pbyte(psrc - 1)^;1161 end;1162 if psrcUp <> nil then1163 begin1164 c3 := pbyte(psrcUp)^;1165 Inc(psrcUp);1166 end1167 else1168 c3 := currentBorderColor;1169 1170 if (xb = bmpWidth - 1) then1171 begin1172 c4 := currentBorderColor;1173 c5 := currentBorderColor;1174 end1175 else1176 begin1177 if psrcDown <> nil then1178 c4 := pbyte(psrcDown + 1)^1179 else1180 c4 := currentBorderColor;1181 c5 := pbyte(psrc + 1)^;1182 end;1183 if psrcDown <> nil then1184 begin1185 c6 := pbyte(psrcDown)^;1186 Inc(psrcDown);1187 end1188 else1189 c6 := currentBorderColor;1190 Inc(psrc);1191 1192 sum := c4+c5+c6-c1-c2-c3;1193 sum := 128 + sum div 3;1194 if sum > 255 then1195 slope := 2551196 else1197 if sum < 1 then1198 slope := 11199 else1200 slope := sum;1201 h := c0;1202 1203 tempPixel.red := slope;1204 tempPixel.green := slope;1205 tempPixel.blue := slope;1206 tempPixel.alpha := abs(slope - 128) * 2;1207 1208 if fillSelection then1209 begin1210 highlight := BGRA(h shr 2, h shr 1, h, h shr 1);1211 if tempPixel.red < highlight.red then1212 tempPixel.red := highlight.red;1213 if tempPixel.green < highlight.green then1214 tempPixel.green := highlight.green;1215 if tempPixel.blue < highlight.blue then1216 tempPixel.blue := highlight.blue;1217 if tempPixel.alpha < highlight.alpha then1218 tempPixel.alpha := highlight.alpha;1219 end;1220 1221 pdest^ := tempPixel;1222 Inc(pdest);1223 end;1224 end;1225 Result.InvalidateBitmap;1226 end;1227 1228 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap;1229 FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;1230 var1231 yb, xb: int32or64;1232 c0,c1,c2,c3,c4,c5,c6: int32or64;1233 1234 bmpWidth, bmpHeight: int32or64;1235 slope, h: byte;1236 sum: int32or64;1237 tempPixel, highlight: TBGRAPixel;1238 pdest, psrcUp, psrc, psrcDown: PBGRAPixel;1239 1240 bounds: TRect;1241 borderColorOverride: boolean;1242 borderColorLevel: int32or64;1243 1244 currentBorderColor: int32or64;1245 begin1246 borderColorOverride := DefineBorderColor.alpha <> 0;1247 borderColorLevel := DefineBorderColor.red;1248 1249 bmpWidth := bmp.Width;1250 bmpHeight := bmp.Height;1251 1252 if borderColorOverride then1253 bounds := bmp.GetImageBounds(cRed, borderColorLevel)1254 else1255 bounds := bmp.GetImageBounds(cRed);1256 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then1257 begin1258 Result := bmp.NewBitmap(0, 0);1259 exit;1260 end;1261 bounds.Left := max(0, bounds.Left - 1);1262 bounds.Top := max(0, bounds.Top - 1);1263 bounds.Right := min(bmpWidth, bounds.Right + 1);1264 bounds.Bottom := min(bmpHeight, bounds.Bottom + 1);1265 1266 Result := bmp.NewBitmap(bounds.Right-Bounds.Left+1, bounds.Bottom-Bounds.Top+1);1267 inc(Offset.X, bounds.Left);1268 inc(Offset.Y, bounds.Top);1269 1270 currentBorderColor := borderColorLevel;1271 for yb := bounds.Top to bounds.Bottom - 1 do1272 begin1273 pdest := Result.scanline[yb-Bounds.Top];1274 1275 if yb > 0 then1276 psrcUp := bmp.Scanline[yb - 1] + bounds.Left1277 else1278 psrcUp := nil;1279 psrc := bmp.scanline[yb] + bounds.Left;1280 if yb < bmpHeight - 1 then1281 psrcDown := bmp.scanline[yb + 1] + bounds.Left1282 else1283 psrcDown := nil;1284 1285 for xb := bounds.Left to bounds.Right - 1 do1286 begin1287 c0 := pbyte(psrc)^;1288 if not borderColorOverride then currentBorderColor := c0;1289 if (xb = 0) then1290 begin1291 c1 := currentBorderColor;1292 c2 := currentBorderColor;1293 end1294 else1295 begin1296 if psrcUp <> nil then1297 c1 := pbyte(psrcUp - 1)^1298 else1299 c1 := currentBorderColor;1300 c2 := pbyte(psrc - 1)^;1301 end;1302 if psrcUp <> nil then1303 begin1304 c3 := pbyte(psrcUp)^;1305 Inc(psrcUp);1306 end1307 else1308 c3 := currentBorderColor;1309 1310 if (xb = bmpWidth - 1) then1311 begin1312 c4 := currentBorderColor;1313 c5 := currentBorderColor;1314 end1315 else1316 begin1317 if psrcDown <> nil then1318 c4 := pbyte(psrcDown + 1)^1319 else1320 c4 := currentBorderColor;1321 c5 := pbyte(psrc + 1)^;1322 end;1323 if psrcDown <> nil then1324 begin1325 c6 := pbyte(psrcDown)^;1326 Inc(psrcDown);1327 end1328 else1329 c6 := currentBorderColor;1330 Inc(psrc);1331 1332 sum := c4+c5+c6-c1-c2-c3;1333 sum := 128 + sum div 3;1334 if sum > 255 then1335 slope := 2551336 else1337 if sum < 1 then1338 slope := 11339 else1340 slope := sum;1341 h := c0;1342 1343 tempPixel.red := slope;1344 tempPixel.green := slope;1345 tempPixel.blue := slope;1346 tempPixel.alpha := abs(slope - 128) * 2;1347 1348 if fillSelection then1349 begin1350 highlight := BGRA(h shr 2, h shr 1, h, h shr 1);1351 if tempPixel.red < highlight.red then1352 tempPixel.red := highlight.red;1353 if tempPixel.green < highlight.green then1354 tempPixel.green := highlight.green;1355 if tempPixel.blue < highlight.blue then1356 tempPixel.blue := highlight.blue;1357 if tempPixel.alpha < highlight.alpha then1358 tempPixel.alpha := highlight.alpha;1359 end;1360 1361 pdest^ := tempPixel;1362 Inc(pdest);1363 end;1364 end;1365 Result.InvalidateBitmap;1366 end;1367 1368 function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean1369 ): TBGRACustomBitmap;1370 begin1371 result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel);1372 end;1373 1374 { Normalize compute min-max of specified channel and apply an affine transformation1375 to make it use the full range of values }1376 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;1377 eachChannel: boolean = True): TBGRACustomBitmap;1378 var1379 psrc, pdest: PBGRAPixel;1380 c: TExpandedPixel;1381 xcount,xb,yb: int32or64;1382 minValRed, maxValRed, minValGreen, maxValGreen, minValBlue, maxValBlue,1383 minAlpha, maxAlpha, addValRed, addValGreen, addValBlue, addAlpha: word;1384 factorValRed, factorValGreen, factorValBlue, factorAlpha: int32or64;1385 begin1386 if not IntersectRect(ABounds,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit;1387 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1388 bmp.LoadFromBitmapIfNeeded;1389 maxValRed := 0;1390 minValRed := 65535;1391 maxValGreen := 0;1392 minValGreen := 65535;1393 maxValBlue := 0;1394 minValBlue := 65535;1395 maxAlpha := 0;1396 minAlpha := 65535;1397 xcount := ABounds.Right-ABounds.Left;1398 for yb := ABounds.Top to ABounds.Bottom-1 do1399 begin1400 psrc := bmp.ScanLine[yb]+ABounds.Left;1401 for xb := xcount-1 downto 0 do1402 begin1403 c := GammaExpansion(psrc^);1404 Inc(psrc);1405 if c.red > maxValRed then1406 maxValRed := c.red;1407 if c.green > maxValGreen then1408 maxValGreen := c.green;1409 if c.blue > maxValBlue then1410 maxValBlue := c.blue;1411 if c.red < minValRed then1412 minValRed := c.red;1413 if c.green < minValGreen then1414 minValGreen := c.green;1415 if c.blue < minValBlue then1416 minValBlue := c.blue;1417 1418 if c.alpha > maxAlpha then1419 maxAlpha := c.alpha;1420 if c.alpha < minAlpha then1421 minAlpha := c.alpha;1422 end;1423 end;1424 if not eachChannel then1425 begin1426 minValRed := min(min(minValRed, minValGreen), minValBlue);1427 maxValRed := max(max(maxValRed, maxValGreen), maxValBlue);1428 minValGreen := minValRed;1429 maxValGreen := maxValRed;1430 minValBlue := minValBlue;1431 maxValBlue := maxValBlue;1432 end;1433 if maxValRed > minValRed then1434 begin1435 factorValRed := 268431360 div (maxValRed - minValRed);1436 addValRed := 0;1437 end1438 else1439 begin1440 factorValRed := 0;1441 if minValRed = 0 then1442 addValRed := 01443 else1444 addValRed := 65535;1445 end;1446 if maxValGreen > minValGreen then1447 begin1448 factorValGreen := 268431360 div (maxValGreen - minValGreen);1449 addValGreen := 0;1450 end1451 else1452 begin1453 factorValGreen := 0;1454 if minValGreen = 0 then1455 addValGreen := 01456 else1457 addValGreen := 65535;1458 end;1459 if maxValBlue > minValBlue then1460 begin1461 factorValBlue := 268431360 div (maxValBlue - minValBlue);1462 addValBlue := 0;1463 end1464 else1465 begin1466 factorValBlue := 0;1467 if minValBlue = 0 then1468 addValBlue := 01469 else1470 addValBlue := 65535;1471 end;1472 if maxAlpha > minAlpha then1473 begin1474 factorAlpha := 268431360 div (maxAlpha - minAlpha);1475 addAlpha := 0;1476 end1477 else1478 begin1479 factorAlpha := 0;1480 if minAlpha = 0 then1481 addAlpha := 01482 else1483 addAlpha := 65535;1484 end;1485 1486 for yb := ABounds.Top to ABounds.Bottom-1 do1487 begin1488 psrc := bmp.ScanLine[yb]+ABounds.Left;1489 pdest := Result.ScanLine[yb]+ABounds.Left;1490 for xb := xcount-1 downto 0 do1491 begin1492 c := GammaExpansion(psrc^);1493 Inc(psrc);1494 c.red := ((c.red - minValRed) * factorValRed + 2047) shr 12 + addValRed;1495 c.green := ((c.green - minValGreen) * factorValGreen + 2047) shr 12 + addValGreen;1496 c.blue := ((c.blue - minValBlue) * factorValBlue + 2047) shr 12 + addValBlue;1497 c.alpha := ((c.alpha - minAlpha) * factorAlpha + 2047) shr 12 + addAlpha;1498 pdest^ := GammaCompression(c);1499 Inc(pdest);1500 end;1501 end;1502 Result.InvalidateBitmap;1503 end;1504 1505 { Rotates the image. To do this, loop through the destination and1506 calculates the position in the source bitmap with an affine transformation }1507 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;1508 angle: single; correctBlur: boolean): TBGRACustomBitmap;1509 var1510 bounds: TRect;1511 pdest: PBGRAPixel;1512 xsrc, ysrc: single;1513 savexysrc, pt: TPointF;1514 dx, dy: single;1515 xb, yb: int32or64;1516 minx, miny, maxx, maxy: single;1517 rf : TResampleFilter;1518 1519 function RotatePos(x, y: single): TPointF;1520 var1521 px, py: single;1522 begin1523 px := x - origin.x;1524 py := y - origin.y;1525 Result := PointF(origin.x + px * dx + py * dy, origin.y - px * dy + py * dx);1526 end;1527 1528 begin1529 bounds := bmp.GetImageBounds;1530 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then1531 begin1532 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1533 exit;1534 end;1535 1536 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1537 if correctBlur then rf := rfHalfCosine else rf := rfLinear;1538 1539 //compute new bounding rectangle1540 dx := cos(angle * Pi / 180);1541 dy := -sin(angle * Pi / 180);1542 pt := RotatePos(bounds.left, bounds.top);1543 minx := pt.x;1544 miny := pt.y;1545 maxx := pt.x;1546 maxy := pt.y;1547 pt := RotatePos(bounds.Right - 1, bounds.top);1548 if pt.x < minx then1549 minx := pt.x1550 else1551 if pt.x > maxx then1552 maxx := pt.x;1553 if pt.y < miny then1554 miny := pt.y1555 else1556 if pt.y > maxy then1557 maxy := pt.y;1558 pt := RotatePos(bounds.Right - 1, bounds.bottom - 1);1559 if pt.x < minx then1560 minx := pt.x1561 else1562 if pt.x > maxx then1563 maxx := pt.x;1564 if pt.y < miny then1565 miny := pt.y1566 else1567 if pt.y > maxy then1568 maxy := pt.y;1569 pt := RotatePos(bounds.left, bounds.bottom - 1);1570 if pt.x < minx then1571 minx := pt.x1572 else1573 if pt.x > maxx then1574 maxx := pt.x;1575 if pt.y < miny then1576 miny := pt.y1577 else1578 if pt.y > maxy then1579 maxy := pt.y;1580 1581 bounds.left := max(0, floor(minx));1582 bounds.top := max(0, floor(miny));1583 bounds.right := min(bmp.Width, ceil(maxx) + 1);1584 bounds.bottom := min(bmp.Height, ceil(maxy) + 1);1585 1586 //reciproqual1587 dy := -dy;1588 pt := RotatePos(bounds.left, bounds.top);1589 xsrc := pt.x;1590 ysrc := pt.y;1591 for yb := bounds.Top to bounds.bottom - 1 do1592 begin1593 pdest := Result.scanline[yb] + bounds.left;1594 savexysrc := pointf(xsrc, ysrc);1595 for xb := bounds.left to bounds.right - 1 do1596 begin1597 pdest^ := bmp.GetPixel(xsrc, ysrc, rf);1598 Inc(pdest);1599 xsrc += dx;1600 ysrc -= dy;1601 end;1602 xsrc := savexysrc.x + dy;1603 ysrc := savexysrc.y + dx;1604 end;1605 Result.InvalidateBitmap;1606 end;1607 1608 { Filter grayscale applies BGRAToGrayscale function to all pixels }1609 procedure FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);1610 var1611 pdest, psrc: PBGRAPixel;1612 xb, yb: int32or64;1613 1614 begin1615 if IsRectEmpty(ABounds) then exit;1616 1617 for yb := ABounds.Top to ABounds.bottom - 1 do1618 begin1619 if Assigned(ACheckShouldStop) and ACheckShouldStop(yb) then break;1620 pdest := ADestination.scanline[yb] + ABounds.left;1621 psrc := bmp.scanline[yb] + ABounds.left;1622 for xb := ABounds.left to ABounds.right - 1 do1623 begin1624 pdest^ := BGRAToGrayscale(psrc^);1625 Inc(pdest);1626 Inc(psrc);1627 end;1628 end;1629 ADestination.InvalidateBitmap;1630 end;1631 1632 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;1633 begin1634 result := FilterGrayscale(bmp, rect(0,0,bmp.width,bmp.Height));1635 end;1636 1637 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;1638 begin1639 result := bmp.NewBitmap(bmp.Width,bmp.Height);1640 FilterGrayscale(bmp,ABounds,result,nil);1641 end;1642 1643 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect1644 ): TFilterTask;1645 begin1646 result := TGrayscaleTask.Create(bmp,ABounds);1647 end;1648 1649 { Filter contour compute a grayscale image, then for each pixel1650 calculates the difference with surrounding pixels (in intensity and alpha)1651 and draw black pixels when there is a difference }1652 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;1653 var1654 yb, xb: int32or64;1655 c: array[0..8] of TBGRAPixel;1656 1657 i, bmpWidth, bmpHeight: int32or64;1658 slope: byte;1659 sum: int32or64;1660 tempPixel: TBGRAPixel;1661 pdest, psrcUp, psrc, psrcDown: PBGRAPixel;1662 1663 bounds: TRect;1664 gray: TBGRACustomBitmap;1665 begin1666 bmpWidth := bmp.Width;1667 bmpHeight := bmp.Height;1668 Result := bmp.NewBitmap(bmpWidth, bmpHeight);1669 gray := bmp.FilterGrayscale;1670 1671 bounds := rect(0, 0, bmp.Width, bmp.Height);1672 for yb := bounds.Top to bounds.Bottom - 1 do1673 begin1674 pdest := Result.scanline[yb] + bounds.Left;1675 1676 if yb > 0 then1677 psrcUp := gray.Scanline[yb - 1] + bounds.Left1678 else1679 psrcUp := nil;1680 psrc := gray.scanline[yb] + bounds.Left;1681 if yb < bmpHeight - 1 then1682 psrcDown := gray.scanline[yb + 1] + bounds.Left1683 else1684 psrcDown := nil;1685 1686 for xb := bounds.Left to bounds.Right - 1 do1687 begin1688 c[0] := psrc^;1689 if (xb = 0) then1690 begin1691 c[1] := c[0];1692 c[2] := c[0];1693 c[4] := c[0];1694 end1695 else1696 begin1697 if psrcUp <> nil then1698 c[1] := (psrcUp - 1)^1699 else1700 c[1] := c[0];1701 c[2] := (psrc - 1)^;1702 if psrcDown <> nil then1703 c[4] := (psrcDown - 1)^1704 else1705 c[4] := c[0];1706 end;1707 if psrcUp <> nil then1708 begin1709 c[3] := psrcUp^;1710 Inc(psrcUp);1711 end1712 else1713 c[3] := c[0];1714 1715 if (xb = bmpWidth - 1) then1716 begin1717 c[5] := c[0];1718 c[6] := c[0];1719 c[8] := c[0];1720 end1721 else1722 begin1723 if psrcDown <> nil then1724 c[5] := (psrcDown + 1)^1725 else1726 c[5] := c[0];1727 c[6] := (psrc + 1)^;1728 if psrcUp <> nil then1729 c[8] := psrcUp^1730 else //+1 before1731 c[8] := c[0];1732 end;1733 if psrcDown <> nil then1734 begin1735 c[7] := psrcDown^;1736 Inc(psrcDown);1737 end1738 else1739 c[7] := c[0];1740 Inc(psrc);1741 1742 sum := 0;1743 for i := 1 to 4 do1744 sum += abs(c[i].red - c[i + 4].red) + abs(c[i].alpha - c[i + 4].alpha);1745 1746 if sum > 255 then1747 slope := 2551748 else1749 if sum < 0 then1750 slope := 01751 else1752 slope := sum;1753 1754 tempPixel.red := 255 - slope;1755 tempPixel.green := 255 - slope;1756 tempPixel.blue := 255 - slope;1757 tempPixel.alpha := 255;1758 pdest^ := tempPixel;1759 Inc(pdest);1760 end;1761 end;1762 Result.InvalidateBitmap;1763 gray.Free;1764 end;1765 1766 { Compute the distance for each pixel to the center of the bitmap,1767 calculate the corresponding angle with arcsin, use this angle1768 to determine a distance from the center in the source bitmap }1769 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;1770 var1771 cx, cy, x, y, len, fact: single;1772 xb, yb: int32or64;1773 mask: TBGRACustomBitmap;1774 begin1775 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1776 cx := bmp.Width / 2 - 0.5;1777 cy := bmp.Height / 2 - 0.5;1778 for yb := 0 to Result.Height - 1 do1779 for xb := 0 to Result.Width - 1 do1780 begin1781 x := (xb - cx) / (cx + 0.5);1782 y := (yb - cy) / (cy + 0.5);1783 len := sqrt(sqr(x) + sqr(y));1784 if (len <= 1) then1785 begin1786 if (len > 0) then1787 begin1788 fact := 1 / len * arcsin(len) / (Pi / 2);1789 x *= fact;1790 y *= fact;1791 end;1792 Result.setpixel(xb, yb, bmp.Getpixel(x * cx + cx, y * cy + cy));1793 end;1794 end;1795 mask := bmp.NewBitmap(bmp.Width, bmp.Height);1796 Mask.Fill(BGRABlack);1797 Mask.FillEllipseAntialias(cx, cy, cx, cy, BGRAWhite);1798 Result.ApplyMask(mask);1799 Mask.Free;1800 end;1801 1802 { Applies twirl scanner. See TBGRATwirlScanner }1803 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;1804 var twirl: TBGRATwirlScanner;1805 begin1806 twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);1807 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1808 result.FillRect(ABounds, twirl, dmSet);1809 twirl.free;1810 end;1811 1812 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint;1813 ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;1814 begin1815 result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent);1816 end;1817 1818 { Compute the distance for each pixel to the vertical axis of the bitmap,1819 calculate the corresponding angle with arcsin, use this angle1820 to determine a distance from the vertical axis in the source bitmap }1821 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;1822 var1823 cx, cy, x, y, len, fact: single;1824 xb, yb: int32or64;1825 begin1826 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1827 cx := bmp.Width / 2 - 0.5;1828 cy := bmp.Height / 2 - 0.5;1829 for yb := 0 to Result.Height - 1 do1830 for xb := 0 to Result.Width - 1 do1831 begin1832 x := (xb - cx) / (cx + 0.5);1833 y := (yb - cy) / (cy + 0.5);1834 len := abs(x);1835 if (len <= 1) then1836 begin1837 if (len > 0) then1838 begin1839 fact := 1 / len * arcsin(len) / (Pi / 2);1840 x *= fact;1841 end;1842 Result.setpixel(xb, yb, bmp.Getpixel(x * cx + cx, y * cy + cy));1843 end;1844 end;1845 end;1846 1847 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;1848 const resampleGap=0.6;1849 var1850 cy, x1, x2, y1, y2, z1, z2, h: single;1851 yb: int32or64;1852 resampledBmp: TBGRACustomBitmap;1853 resampledBmpWidth: int32or64;1854 resampledFactor,newResampleFactor: single;1855 sub,resampledSub: TBGRACustomBitmap;1856 partRect: TRect;1857 resampleSizeY : int32or64;1858 begin1859 resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch);1860 resampledBmpWidth := resampledBmp.Width;1861 resampledFactor := 2;1862 Result := bmp.NewBitmap(bmp.Width, bmp.Height*2);1863 cy := result.Height / 2 - 0.5;1864 h := 1;1865 for yb := 0 to ((Result.Height-1) div 2) do1866 begin1867 y1 := (cy - (yb-0.5)) / (cy+0.5);1868 y2 := (cy - (yb+0.5)) / (cy+0.5);1869 if y2 <= 0 then continue;1870 z1 := h/y1;1871 z2 := h/y2;1872 newResampleFactor := 1/(z2-z1)*1.5;1873 1874 x1 := (z1+1)/2;1875 x2 := (z2+1)/2;1876 if newResampleFactor <= resampledFactor*resampleGap then1877 begin1878 resampledFactor := newResampleFactor;1879 if resampledBmp <> bmp then resampledBmp.Free;1880 if (x2-x1 >= 1) then resampleSizeY := 1 else1881 resampleSizeY := round(1+((x2-x1)-1)/(1/bmp.Height-1)*(bmp.Height-1));1882 resampledBmp := bmp.Resample(max(1,round(bmp.Width*resampledFactor)),resampleSizeY,rmSimpleStretch);1883 resampledBmpWidth := resampledBmp.Width;1884 end;1885 1886 partRect := Rect(round(-resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x1*resampledBmp.Height),1887 round(resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x2*resampledBmp.Height)+1);1888 if x2-x1 > 1 then1889 begin1890 partRect.Top := 0;1891 partRect.Bottom := 1;1892 end;1893 sub := resampledBmp.GetPart(partRect);1894 if sub <> nil then1895 begin1896 resampledSub := sub.Resample(bmp.Width,1,rmFineResample);1897 result.PutImage(0,yb,resampledSub,dmSet);1898 result.PutImage(0,Result.Height-1-yb,resampledSub,dmSet);1899 resampledSub.free;1900 sub.free;1901 end;1902 end;1903 if resampledBmp <> bmp then resampledBmp.Free;1904 1905 if result.Height <> bmp.Height then1906 begin1907 resampledBmp := result.Resample(bmp.Width,bmp.Height,rmSimpleStretch);1908 result.free;1909 result := resampledBmp;1910 end;1911 end;1912 1913 { For each component, sort values to get the median }1914 function FilterMedian(bmp: TBGRACustomBitmap;1915 Option: TMedianOption): TBGRACustomBitmap;1916 1917 function ComparePixLt(p1, p2: TBGRAPixel): boolean;1918 begin1919 if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then1920 Result := (int32or64(p1.red) shl 8) + (int32or64(p1.green) shl 16) +1921 int32or64(p1.blue) < (int32or64(p2.red) shl 8) + (int32or64(p2.green) shl 16) +1922 int32or64(p2.blue)1923 else1924 Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue);1925 end;1926 1927 const1928 nbpix = 9;1929 var1930 yb, xb: int32or64;1931 dx, dy, n, i, j, k: int32or64;1932 a_pixels: array[0..nbpix - 1] of TBGRAPixel;1933 tempPixel, refPixel: TBGRAPixel;1934 tempValue: byte;1935 sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64;1936 tempAlpha: word;1937 bounds: TRect;1938 pdest: PBGRAPixel;1939 begin1940 Result := bmp.NewBitmap(bmp.Width, bmp.Height);1941 1942 bounds := bmp.GetImageBounds;1943 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then1944 exit;1945 bounds.Left := max(0, bounds.Left - 1);1946 bounds.Top := max(0, bounds.Top - 1);1947 bounds.Right := min(bmp.Width, bounds.Right + 1);1948 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);1949 1950 for yb := bounds.Top to bounds.bottom - 1 do1951 begin1952 pdest := Result.scanline[yb] + bounds.left;1953 for xb := bounds.left to bounds.right - 1 do1954 begin1955 n := 0;1956 for dy := -1 to 1 do1957 for dx := -1 to 1 do1958 begin1959 a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);1960 if a_pixels[n].alpha = 0 then1961 a_pixels[n] := BGRAPixelTransparent;1962 Inc(n);1963 end;1964 for i := 1 to n - 1 do1965 begin1966 j := i;1967 while (j > 1) and (a_pixels[j].alpha < a_pixels[j - 1].alpha) do1968 begin1969 tempValue := a_pixels[j].alpha;1970 a_pixels[j].alpha := a_pixels[j - 1].alpha;1971 a_pixels[j - 1].alpha := tempValue;1972 Dec(j);1973 end;1974 j := i;1975 while (j > 1) and (a_pixels[j].red < a_pixels[j - 1].red) do1976 begin1977 tempValue := a_pixels[j].red;1978 a_pixels[j].red := a_pixels[j - 1].red;1979 a_pixels[j - 1].red := tempValue;1980 Dec(j);1981 end;1982 j := i;1983 while (j > 1) and (a_pixels[j].green < a_pixels[j - 1].green) do1984 begin1985 tempValue := a_pixels[j].green;1986 a_pixels[j].green := a_pixels[j - 1].green;1987 a_pixels[j - 1].green := tempValue;1988 Dec(j);1989 end;1990 j := i;1991 while (j > 1) and (a_pixels[j].blue < a_pixels[j - 1].blue) do1992 begin1993 tempValue := a_pixels[j].blue;1994 a_pixels[j].blue := a_pixels[j - 1].blue;1995 a_pixels[j - 1].blue := tempValue;1996 Dec(j);1997 end;1998 end;1999 2000 refPixel := a_pixels[n div 2];2001 2002 if option in [moLowSmooth, moMediumSmooth, moHighSmooth] then2003 begin2004 sumR := 0;2005 sumG := 0;2006 sumB := 0;2007 sumA := 0;2008 BGRAdiv := 0;2009 nbA := 0;2010 2011 case option of2012 moHighSmooth, moMediumSmooth:2013 begin2014 j := 2;2015 k := 2;2016 end;2017 else2018 begin2019 j := 1;2020 k := 1;2021 end;2022 end;2023 2024 {$hints off}2025 for i := -k to j do2026 begin2027 tempPixel := a_pixels[n div 2 + i];2028 tempAlpha := tempPixel.alpha;2029 if (option = moMediumSmooth) and ((i = -k) or (i = j)) then2030 tempAlpha := tempAlpha div 2;2031 2032 sumR += tempPixel.red * tempAlpha;2033 sumG += tempPixel.green * tempAlpha;2034 sumB += tempPixel.blue * tempAlpha;2035 BGRAdiv += tempAlpha;2036 2037 sumA += tempAlpha;2038 Inc(nbA);2039 end;2040 {$hints on}2041 if option = moMediumSmooth then2042 Dec(nbA);2043 2044 if (BGRAdiv = 0) then2045 refPixel := BGRAPixelTransparent2046 else2047 begin2048 refPixel.red := round(sumR / BGRAdiv);2049 refPixel.green := round(sumG / BGRAdiv);2050 refPixel.blue := round(sumB / BGRAdiv);2051 refPixel.alpha := round(sumA / nbA);2052 end;2053 end;2054 2055 pdest^ := refPixel;2056 Inc(pdest);2057 end;2058 end;2059 end;2060 2061 constructor TBoxBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;2062 radius: integer);2063 begin2064 FSource := bmp;2065 FBounds := ABounds;2066 FRadius := radius;2067 end;2068 2069 procedure TBoxBlurTask.DoExecute;2070 type2071 TVertical = record red,green,blue,alpha,count: NativeUint; end;2072 PVertical = ^TVertical;2073 var2074 verticals: PVertical;2075 left,right,width,height: NativeInt;2076 delta: PtrInt;2077 2078 procedure PrepareVerticals;2079 var2080 xb,yb: NativeInt;2081 psrc,p: PBGRAPixel;2082 pvert : PVertical;2083 begin2084 fillchar(verticals^, width*sizeof(TVertical), 0);2085 psrc := FSource.ScanLine[FBounds.Top];2086 pvert := verticals;2087 for xb := left to right-1 do2088 begin2089 p := psrc+xb;2090 for yb := 0 to FRadius-1 do2091 begin2092 if yb = height then break;2093 if p^.alpha <> 0 then2094 begin2095 pvert^.red += p^.red * p^.alpha;2096 pvert^.green += p^.green * p^.alpha;2097 pvert^.blue += p^.blue * p^.alpha;2098 pvert^.alpha += p^.alpha;2099 end;2100 inc(pvert^.count);2101 PByte(p) += delta;2102 end;2103 inc(pvert);2104 end;2105 end;2106 2107 procedure NextVerticals(y: integer);2108 var2109 psrc1,psrc2: PBGRAPixel;2110 pvert : PVertical;2111 xb: NativeInt;2112 begin2113 pvert := verticals;2114 if y-FRadius-1 >= 0 then2115 psrc1 := FSource.ScanLine[y-FRadius-1]2116 else2117 psrc1 := nil;2118 if y+FRadius < FSource.Height then2119 psrc2 := FSource.ScanLine[y+FRadius]2120 else2121 psrc2 := nil;2122 for xb := width-1 downto 0 do2123 begin2124 if psrc1 <> nil then2125 begin2126 if psrc1^.alpha <> 0 then2127 begin2128 {$HINTS OFF}2129 pvert^.red -= psrc1^.red * psrc1^.alpha;2130 pvert^.green -= psrc1^.green * psrc1^.alpha;2131 pvert^.blue -= psrc1^.blue * psrc1^.alpha;2132 pvert^.alpha -= psrc1^.alpha;2133 {$HINTS ON}2134 end;2135 dec(pvert^.count);2136 inc(psrc1);2137 end;2138 if psrc2 <> nil then2139 begin2140 if psrc2^.alpha <> 0 then2141 begin2142 pvert^.red += psrc2^.red * psrc2^.alpha;2143 pvert^.green += psrc2^.green * psrc2^.alpha;2144 pvert^.blue += psrc2^.blue * psrc2^.alpha;2145 pvert^.alpha += psrc2^.alpha;2146 end;2147 inc(pvert^.count);2148 inc(psrc2);2149 end;2150 inc(pvert);2151 end;2152 end;2153 2154 procedure MainLoop;2155 var2156 xb,yb,xdest: NativeInt;2157 pdest: PBGRAPixel;2158 pvert : PVertical;2159 sumRed,sumGreen,sumBlue,sumAlpha,sumCount: NativeUInt;2160 begin2161 for yb := FBounds.Top to FBounds.Bottom-1 do2162 begin2163 NextVerticals(yb);2164 if GetShouldStop(yb) then exit;2165 pdest := Destination.ScanLine[yb]+left;2166 sumRed := 0;2167 sumGreen := 0;2168 sumBlue := 0;2169 sumAlpha := 0;2170 sumCount := 0;2171 pvert := verticals;2172 for xb := 0 to FRadius-1 do2173 begin2174 if xb = width then break;2175 sumRed += pvert^.red;2176 sumGreen += pvert^.green;2177 sumBlue += pvert^.blue;2178 sumAlpha += pvert^.alpha;2179 sumCount += pvert^.count;2180 inc(pvert);2181 end;2182 for xdest := 0 to width-1 do2183 begin2184 if xdest-FRadius-1 >= 0 then2185 begin2186 pvert := verticals+(xdest-FRadius-1);2187 sumRed -= pvert^.red;2188 sumGreen -= pvert^.green;2189 sumBlue -= pvert^.blue;2190 sumAlpha -= pvert^.alpha;2191 sumCount -= pvert^.count;2192 end;2193 if xdest+FRadius < width then2194 begin2195 pvert := verticals+(xdest+FRadius);2196 sumRed += pvert^.red;2197 sumGreen += pvert^.green;2198 sumBlue += pvert^.blue;2199 sumAlpha += pvert^.alpha;2200 sumCount += pvert^.count;2201 end;2202 if (sumCount > 0) and (sumAlpha >= (sumCount+1) shr 1) then2203 begin2204 pdest^.red := (sumRed+(sumAlpha shr 1)) div sumAlpha;2205 pdest^.green := (sumGreen+(sumAlpha shr 1)) div sumAlpha;2206 pdest^.blue := (sumBlue+(sumAlpha shr 1)) div sumAlpha;2207 pdest^.alpha := (sumAlpha+(sumCount shr 1)) div sumCount;2208 end else2209 pdest^ := BGRAPixelTransparent;2210 inc(pdest);2211 end;2212 end;2213 end;2214 2215 begin2216 if (FBounds.Right <= FBounds.Left) or (FBounds.Bottom <= FBounds.Top) or (FRadius <= 0) then exit;2217 left := FBounds.left;2218 right := FBounds.right;2219 width := right-left;2220 height := FBounds.bottom-FBounds.top;2221 delta := FSource.Width*SizeOf(TBGRAPixel);2222 if FSource.LineOrder = riloBottomToTop then delta := -delta;2223 2224 getmem(verticals, width*sizeof(TVertical));2225 try2226 PrepareVerticals;2227 MainLoop;2228 finally2229 freemem(verticals);2230 end;2231 end;2232 2233 constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect);2234 begin2235 FSource := bmp;2236 FBounds := ABounds;2237 end;2238 2239 procedure TGrayscaleTask.DoExecute;2240 begin2241 FilterGrayscale(FSource,FBounds,Destination,@GetShouldStop);2242 end;2243 2244 { TCustomBlurTask }2245 2246 constructor TCustomBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;2247 AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean);2248 begin2249 FSource := bmp;2250 FBounds := ABounds;2251 if AMaskIsThreadSafe then2252 begin2253 FMask := AMask;2254 FMaskOwned := false;2255 end else2256 begin2257 FMask := AMask.Duplicate;2258 FMaskOwned := true;2259 end;2260 end;2261 2262 destructor TCustomBlurTask.Destroy;2263 begin2264 If FMaskOwned then FreeAndNil(FMask);2265 inherited Destroy;2266 end;2267 2268 procedure TCustomBlurTask.DoExecute;2269 begin2270 FilterBlur(FSource,FBounds,FMask,Destination,@GetShouldStop);2271 end;2272 2273 constructor TMotionBlurTask.Create(ABmp: TBGRACustomBitmap; ABounds: TRect;2274 ADistance, AAngle: single; AOriented: boolean);2275 begin2276 FSource := ABmp;2277 FBounds := ABounds;2278 FDistance := ADistance;2279 FAngle := AAngle;2280 FOriented:= AOriented;2281 end;2282 2283 procedure TMotionBlurTask.DoExecute;2284 begin2285 FilterBlurMotion(FSource,FBounds,FDistance,FAngle,FOriented,Destination,@GetShouldStop);2286 end;2287 2288 constructor TRadialPreciseBlurTask.Create(bmp: TBGRACustomBitmap;2289 ABounds: TRect; radius: single);2290 begin2291 FSource := bmp;2292 FBounds := ABounds;2293 FRadius := radius;2294 end;2295 2296 procedure TRadialPreciseBlurTask.DoExecute;2297 begin2298 FilterBlurRadialPrecise(FSource,FBounds,FRadius,Destination,@GetShouldStop);2299 end;2300 2301 { TRadialBlurTask }2302 2303 constructor TRadialBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;2304 radius: integer; blurType: TRadialBlurType);2305 begin2306 FSource := bmp;2307 FBounds := ABounds;2308 FRadius := radius;2309 FBlurType:= blurType;2310 end;2311 2312 procedure TRadialBlurTask.DoExecute;2313 begin2314 FilterBlurRadial(FSource,FBounds,FRadius,FBlurType,Destination,@GetShouldStop);2315 end;2316 2317 { TFilterTask }2318 2319 function TFilterTask.GetShouldStop(ACurrentY: integer): boolean;2320 begin2321 FCurrentY:= ACurrentY;2322 if Assigned(FCheckShouldStop) then2323 result := FCheckShouldStop(ACurrentY)2324 else2325 result := false;2326 end;2327 2328 function TFilterTask.Execute: TBGRACustomBitmap;2329 var DestinationOwned: boolean;2330 begin2331 FCurrentY := 0;2332 if Destination = nil then2333 begin2334 FDestination := FSource.NewBitmap(FSource.Width,FSource.Height);2335 DestinationOwned:= true;2336 end else2337 DestinationOwned:= false;2338 try2339 DoExecute;2340 result := Destination;2341 FDestination := nil;2342 except2343 on ex: exception do2344 begin2345 if DestinationOwned then FreeAndNil(FDestination);2346 raise ex;2347 end;2348 end;2349 end;2350 2351 procedure TFilterTask.SetDestination(AValue: TBGRACustomBitmap);2352 begin2353 if FDestination <> nil then2354 raise exception.Create('Destination is already defined');2355 FDestination := AValue;2356 end;2357 2358 962 end. 2359 963 -
GraphicTest/Packages/bgrabitmap/bgrafreetype.pas
r472 r494 11 11 to draw text like TBGRABitmap.TextOut will use the chosen renderer. 12 12 13 >> Note that you need to define dthe default FreeType font collection14 >> using LazFreeTypeFontCollectionunit.13 >> Note that you need to define the default FreeType font collection 14 >> using EasyLazFreeType unit. 15 15 16 16 To set the effects, keep a variable containing … … 26 26 interface 27 27 28 {$i bgrabitmap.inc} 29 28 30 uses 29 Types, Classes, SysUtils, Graphics, BGRABitmapTypes, EasyLazFreeType, FPimage, BGRAText, BGRATextFX, BGRAPhongTypes, LCLVersion; 31 Types, Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, EasyLazFreeType, FPimage, 32 BGRACustomTextFX, BGRAPhongTypes; 30 33 31 34 type … … 57 60 ShadowRadius: integer; 58 61 ShadowOffset: TPoint; 62 ShadowQuality: TRadialBlurType; 59 63 60 64 OutlineColor: TBGRAPixel; … … 101 105 ShadowRadius: integer; 102 106 ShadowOffset: TPoint; 107 ShadowQuality: TRadialBlurType; 103 108 104 109 OutlineColor: TBGRAPixel; … … 110 115 procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload; 111 116 procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload; 112 function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRATextEffect; 117 { If this code does not compile, you probably have an older version of Lazarus. To fix the problem, 118 go into "bgrabitmap.inc" and comment the compiler directives } 119 {$IFDEF BGRABITMAP_USE_LCL12} 120 procedure DrawTextWordBreak(AText: string; AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload; 121 procedure DrawTextRect(AText: string; AFont: TFreeTypeRenderableFont; X1,Y1,X2,Y2: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload; 122 {$ENDIF} 123 {$IFDEF BGRABITMAP_USE_LCL15} 124 procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); override; overload; 125 procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload; 126 procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload; 127 {$ENDIF} 128 function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect; 113 129 destructor Destroy; override; 114 130 end; … … 117 133 implementation 118 134 119 uses LCLType,BGRABlend, Math;135 uses BGRABlend, Math; 120 136 121 137 { TBGRAFreeTypeFontRenderer } … … 133 149 result.ShadowRadius := ShadowRadius; 134 150 result.ShadowVisible := ShadowVisible; 151 result.ShadowQuality := ShadowQuality; 135 152 result.ClearTypeRGBOrder := FontQuality <> fqFineClearTypeBGR; 136 153 result.Destination := ASurface; … … 159 176 procedure TBGRAFreeTypeFontRenderer.UpdateFont; 160 177 var fts: TFreeTypeStyles; 178 filename: string; 161 179 begin 162 180 fts := []; … … 164 182 if fsItalic in FontStyle then fts += [ftsItalic]; 165 183 try 166 {$IF (lcl_fullversion>=1010000)} 167 FFont.SetNameAndStyle(FontName,fts); 184 filename := FontName; 185 {$IFDEF BGRABITMAP_USE_LCL12} 186 FFont.SetNameAndStyle(filename,fts); 168 187 {$ELSE} 169 FFont.Name := FontName;188 FFont.Name := filename; 170 189 FFont.Style := fts; 171 190 {$ENDIF} … … 202 221 end; 203 222 FFont.Hinted := FontHinted; 204 {$IF (lcl_fullversion>=1010000)}205 FFont.StrikeOutDecoration := fsStrikeOut in FontStyle;206 FFont.UnderlineDecoration := fsUnderline in FontStyle;223 {$IFDEF BGRABITMAP_USE_LCL12} 224 FFont.StrikeOutDecoration := fsStrikeOut in FontStyle; 225 FFont.UnderlineDecoration := fsUnderline in FontStyle; 207 226 {$ENDIF} 208 227 end; … … 220 239 ShadowOffset := Point(5,5); 221 240 ShadowRadius := 5; 241 ShadowQuality:= rbFast; 222 242 end; 223 243 … … 303 323 end; 304 324 case style.Layout of 305 {$IF (lcl_fullversion>=1010000)}306 tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end;325 {$IFDEF BGRABITMAP_USE_LCL12} 326 tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end; 307 327 {$ENDIF} 308 328 tlBottom: begin ARect.top := y; align += [ftaBottom]; end; … … 310 330 end; 311 331 try 312 {$IF (lcl_fullversion>=1010000)}313 if style.Wordbreak then314 GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align)315 else332 {$IFDEF BGRABITMAP_USE_LCL12} 333 if style.Wordbreak then 334 GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align) 335 else 316 336 {$ENDIF} 317 337 begin … … 345 365 function TBGRAFreeTypeFontRenderer.TextSize(s: string): TSize; 346 366 begin 367 UpdateFont; 347 368 result.cx := round(FFont.TextWidth(s)); 348 369 result.cy := round(FFont.LineFullHeight); … … 457 478 ClearTypeRGBOrder:= true; 458 479 ShaderActive := true; 480 ShadowQuality:= rbFast; 459 481 end; 460 482 461 483 procedure TBGRAFreeTypeDrawer.DrawText(AText: string; 462 484 AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor); 463 var fx: TBGRA TextEffect;485 var fx: TBGRACustomTextEffect; 464 486 procedure DoOutline; 465 487 begin … … 476 498 begin 477 499 fx := CreateTextEffect(AText, AFont); 500 fx.ShadowQuality := ShadowQuality; 478 501 y -= AFont.Ascent; 479 502 if ShadowActuallyVisible then fx.DrawShadow(Destination, round(x+ShadowOffset.X),round(y+ShadowOffset.Y), ShadowRadius, ShadowColor); … … 518 541 end; 519 542 543 {$IFDEF BGRABITMAP_USE_LCL12} 544 procedure TBGRAFreeTypeDrawer.DrawTextWordBreak(AText: string; 545 AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel; 546 AAlign: TFreeTypeAlignments); 547 begin 548 DrawTextWordBreak(AText,AFont,x,y,AMaxWidth,BGRAToFPColor(AColor),AAlign); 549 end; 550 551 procedure TBGRAFreeTypeDrawer.DrawTextRect(AText: string; 552 AFont: TFreeTypeRenderableFont; X1, Y1, X2, Y2: Single; AColor: TBGRAPixel; 553 AAlign: TFreeTypeAlignments); 554 begin 555 DrawTextRect(AText,AFont,X1,Y1,X2,Y2,BGRAToFPColor(AColor),AAlign); 556 end; 557 {$ENDIF} 558 559 {$IFDEF BGRABITMAP_USE_LCL15} 560 procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer; 561 AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor); 562 var f: TFreeTypeFont; 563 begin 564 if not (AFont is TFreeTypeFont) then exit; 565 f := TFreeTypeFont(Afont); 566 FColor := FPColorToBGRA(AColor); 567 if AFont.ClearType then 568 f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectlyClearType) 569 else 570 f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectly); 571 end; 572 573 procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer; 574 AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel); 575 begin 576 DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor)); 577 end; 578 579 procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer; 580 AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel; 581 AAlign: TFreeTypeAlignments); 582 begin 583 DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor), AAlign); 584 end; 585 {$ENDIF} 586 520 587 function TBGRAFreeTypeDrawer.CreateTextEffect(AText: string; 521 AFont: TFreeTypeRenderableFont): TBGRA TextEffect;588 AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect; 522 589 var 523 590 mask: TBGRACustomBitmap; … … 545 612 AFont.ClearType := tempClearType; 546 613 mask.ConvertToLinearRGB; 547 result := TBGRA TextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert));614 result := TBGRACustomTextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert)); 548 615 finally 549 616 FInCreateTextEffect:= false; -
GraphicTest/Packages/bgrabitmap/bgragradients.pas
r472 r494 2 2 3 3 {$mode objfpc}{$H+} 4 4 {$i bgrabitmap.inc} 5 5 {$i bgrasse.inc} 6 6 … … 10 10 11 11 uses 12 Classes, Graphics, BGRABitmapTypes, BGRABitmap, BGRABlend, BGRAPhongTypes, BGRASSE;13 14 { Creates a bitmap with the specified text horizontally centered and with a shadow }12 Classes, BGRAGraphics, BGRABitmapTypes, BGRABitmap, BGRABlend, BGRAPhongTypes, BGRASSE; 13 14 {$IFDEF BGRABITMAP_USE_LCL}{ Creates a bitmap with the specified text horizontally centered and with a shadow } 15 15 function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel; 16 16 AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True): TBGRABitmap; 17 {$ENDIF} 17 18 18 19 {----------------------------------------------------------------------} … … 176 177 implementation 177 178 178 uses GraphType, Types, SysUtils, BGRATextFX; {GraphType unit used by phongdraw.inc}179 180 function TextShadow(AWidth, AHeight: Integer; AText: String;179 uses Types, SysUtils{$IFDEF BGRABITMAP_USE_LCL}, BGRATextFX{$ENDIF}; {GraphType unit used by phongdraw.inc} 180 181 {$IFDEF BGRABITMAP_USE_LCL}function TextShadow(AWidth, AHeight: Integer; AText: String; 181 182 AFontHeight: Integer; ATextColor, AShadowColor: TBGRAPixel; AOffSetX, 182 183 AOffSetY: Integer; ARadius: Integer; AFontStyle: TFontStyles; … … 184 185 begin 185 186 result := BGRATextFX.TextShadow(AWidth,AHeight,AText,AFontHeight,ATextColor,AShadowColor,AOffsetX,AOffsetY,ARadius,AFontStyle,AFontName,AShowText) as TBGRABitmap; 186 end; 187 end;{$ENDIF} 187 188 188 189 function nGradientInfo(StartColor, StopColor: TBGRAPixel; … … 671 672 //antialiased border 672 673 mask := TBGRABitmap.Create(width,height,BGRABlack); 673 mask.FillPolyAntialias([PointF( rx,-0.5),PointF(0,height-0.5),PointF(width-0.5,height-0.5)],BGRAWhite);674 mask.FillPolyAntialias([PointF(width/2,-0.5),PointF(0,height-0.5),PointF(width-0.5,height-0.5)],BGRAWhite); 674 675 result.ApplyMask(mask); 675 676 mask.Free; -
GraphicTest/Packages/bgrabitmap/bgragradientscanner.pas
r472 r494 16 16 private 17 17 FColor1,FColor2: TBGRAPixel; 18 ec1,ec2: TExpandedPixel; 18 19 public 19 20 constructor Create(Color1,Color2: TBGRAPixel); 20 21 function GetColorAt(position: integer): TBGRAPixel; override; 21 22 function GetColorAtF(position: single): TBGRAPixel; override; 23 function GetExpandedColorAt(position: integer): TExpandedPixel; override; 24 function GetExpandedColorAtF(position: single): TExpandedPixel; override; 22 25 function GetAverageColor: TBGRAPixel; override; 23 26 function GetMonochrome: boolean; override; … … 35 38 function GetColorAtF(position: single): TBGRAPixel; override; 36 39 function GetAverageColor: TBGRAPixel; override; 40 function GetExpandedColorAt(position: integer): TExpandedPixel; override; 41 function GetExpandedColorAtF(position: single): TExpandedPixel; override; 42 function GetAverageExpandedColor: TExpandedPixel; override; 37 43 function GetMonochrome: boolean; override; 38 44 end; … … 46 52 private 47 53 FColor1,FColor2: TBGRAPixel; 54 ec1,ec2: TExpandedPixel; 48 55 hsla1,hsla2: THSLAPixel; 49 56 hue1,hue2: longword; 50 57 FOptions: THueGradientOptions; 51 58 procedure Init(c1,c2: THSLAPixel; AOptions: THueGradientOptions); 59 function GetColorNoBoundCheck(position: integer): THSLAPixel; 52 60 public 53 61 constructor Create(Color1,Color2: TBGRAPixel; options: THueGradientOptions); overload; … … 57 65 function GetColorAtF(position: single): TBGRAPixel; override; 58 66 function GetAverageColor: TBGRAPixel; override; 67 function GetExpandedColorAt(position: integer): TExpandedPixel; override; 68 function GetExpandedColorAtF(position: single): TExpandedPixel; override; 69 function GetAverageExpandedColor: TExpandedPixel; override; 59 70 function GetMonochrome: boolean; override; 60 71 end; 72 73 TGradientInterpolationFunction = function(t: single): single of object; 61 74 62 75 { TBGRAMultiGradient } … … 69 82 FEColors: array of TExpandedPixel; 70 83 FCycle: Boolean; 84 FInterpolationFunction: TGradientInterpolationFunction; 71 85 procedure Init(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection, ACycle: boolean); 72 86 public 73 87 GammaCorrection: boolean; 88 function CosineInterpolation(t: single): single; 89 function HalfCosineInterpolation(t: single): single; 74 90 constructor Create(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean = false); 75 91 function GetColorAt(position: integer): TBGRAPixel; override; 92 function GetExpandedColorAt(position: integer): TExpandedPixel; override; 76 93 function GetAverageColor: TBGRAPixel; override; 77 94 function GetMonochrome: boolean; override; 95 property InterpolationFunction: TGradientInterpolationFunction read FInterpolationFunction write FInterpolationFunction; 78 96 end; 79 97 … … 88 106 len,aFactor,aFactorF: single; 89 107 mergedColor: TBGRAPixel; 108 mergedExpandedColor: TExpandedPixel; 90 109 FGradient: TBGRACustomGradient; 91 110 FGradientOwner: boolean; 92 111 FHorizColor: TBGRAPixel; 112 FHorizExpandedColor: TExpandedPixel; 93 113 FVertical: boolean; 94 114 FDotProduct,FDotProductPerp: Single; … … 96 116 procedure InitScanInline(x,y: integer); 97 117 function ScanNextInline: TBGRAPixel; inline; 118 function ScanNextExpandedInline: TExpandedPixel; inline; 98 119 public 99 120 constructor Create(c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; … … 103 124 procedure ScanMoveTo(X, Y: Integer); override; 104 125 function ScanNextPixel: TBGRAPixel; override; 126 function ScanNextExpandedPixel: TExpandedPixel; override; 105 127 function ScanAt(X, Y: Single): TBGRAPixel; override; 128 function ScanAtExpanded(X, Y: Single): TExpandedPixel; override; 106 129 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override; 107 130 function IsScanPutPixelsDefined: boolean; override; … … 140 163 function ScanAt(X,Y: Single): TBGRAPixel; override; 141 164 function ScanNextPixel: TBGRAPixel; override; 165 function ScanNextExpandedPixel: TExpandedPixel; override; 142 166 end; 143 167 … … 151 175 FScanNext : TScanNextPixelFunction; 152 176 FScanAt : TScanAtFunction; 177 FMemMask: packed array of TBGRAPixel; 153 178 public 154 179 constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ASolidColor: TBGRAPixel); … … 172 197 FMaskScanAt,FTextureScanAt : TScanAtFunction; 173 198 FGlobalOpacity: Byte; 199 FMemMask, FMemTex: packed array of TBGRAPixel; 174 200 public 175 201 constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255); … … 190 216 FScanNext : TScanNextPixelFunction; 191 217 FScanAt : TScanAtFunction; 218 FMemTex: packed array of TBGRAPixel; 192 219 public 193 220 constructor Create(ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255); … … 202 229 implementation 203 230 204 uses BGRABlend ;231 uses BGRABlend, Math; 205 232 206 233 { TBGRAConstantScanner } … … 247 274 FColor1 := HSLAToBGRA(c1); 248 275 FColor2 := HSLAToBGRA(c2); 276 ec1 := GammaExpansion(FColor1); 277 ec2 := GammaExpansion(FColor2); 249 278 FOptions:= AOptions; 250 279 if (hgoLightnessCorrection in AOptions) then … … 276 305 end; 277 306 307 function TBGRAHueGradient.GetColorNoBoundCheck(position: integer): THSLAPixel; 308 var b,b2: LongWord; 309 begin 310 b := position shr 2; 311 b2 := 16384-b; 312 result.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff; 313 result.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14; 314 result.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14; 315 result.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14; 316 if hgoLightnessCorrection in FOptions then 317 begin 318 if not (hgoHueCorrection in FOptions) then 319 result.hue := HtoG(result.hue); 320 end else 321 begin 322 if hgoHueCorrection in FOptions then 323 result.hue := GtoH(result.hue); 324 end; 325 end; 326 278 327 constructor TBGRAHueGradient.Create(Color1, Color2: TBGRAPixel;options: THueGradientOptions); 279 328 begin … … 293 342 294 343 function TBGRAHueGradient.GetColorAt(position: integer): TBGRAPixel; 295 var b,b2: cardinal; 296 interm: THSLAPixel; 344 var interm: THSLAPixel; 297 345 begin 298 346 if hgoRepeat in FOptions then … … 317 365 end; 318 366 end; 319 b := position shr 2; 320 b2 := 16384-b; 321 interm.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff; 322 interm.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14; 323 interm.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14; 324 interm.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14; 367 interm := GetColorNoBoundCheck(position); 325 368 if hgoLightnessCorrection in FOptions then 326 begin 327 if not (hgoHueCorrection in FOptions) then 328 interm.hue := HtoG(interm.hue); 329 result := GSBAToBGRA(interm); 330 end else 331 begin 332 if hgoHueCorrection in FOptions then 333 interm.hue := GtoH(interm.hue); 369 result := GSBAToBGRA(interm) 370 else 334 371 result := HSLAToBGRA(interm); 335 end;336 372 end; 337 373 338 374 function TBGRAHueGradient.GetColorAtF(position: single): TBGRAPixel; 339 var b,b2: cardinal; 340 interm: THSLAPixel; 375 var interm: THSLAPixel; 341 376 begin 342 377 if hgoRepeat in FOptions then … … 361 396 end; 362 397 end; 363 b := round(position*16384); 364 b2 := 16384-b; 365 interm.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff; 366 interm.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14; 367 interm.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14; 368 interm.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14; 398 interm := GetColorNoBoundCheck(round(position*65536)); 369 399 if hgoLightnessCorrection in FOptions then 370 begin 371 if not (hgoHueCorrection in FOptions) then 372 interm.hue := HtoG(interm.hue); 373 result := GSBAToBGRA(interm); 374 end else 375 begin 376 if hgoHueCorrection in FOptions then 377 interm.hue := GtoH(interm.hue); 400 result := GSBAToBGRA(interm) 401 else 378 402 result := HSLAToBGRA(interm); 379 end;380 403 end; 381 404 … … 383 406 begin 384 407 Result:= GetColorAt(32768); 408 end; 409 410 function TBGRAHueGradient.GetExpandedColorAt(position: integer): TExpandedPixel; 411 var interm: THSLAPixel; 412 begin 413 if hgoRepeat in FOptions then 414 begin 415 position := position and $ffff; 416 if position = 0 then 417 begin 418 result := ec1; 419 exit; 420 end; 421 end else 422 begin 423 if position <= 0 then 424 begin 425 result := ec1; 426 exit 427 end else 428 if position >= 65536 then 429 begin 430 result := ec2; 431 exit 432 end; 433 end; 434 interm := GetColorNoBoundCheck(position); 435 if hgoLightnessCorrection in FOptions then 436 result := GSBAToExpanded(interm) 437 else 438 result := HSLAToExpanded(interm); 439 end; 440 441 function TBGRAHueGradient.GetExpandedColorAtF(position: single): TExpandedPixel; 442 var interm: THSLAPixel; 443 begin 444 if hgoRepeat in FOptions then 445 begin 446 position := frac(position); 447 if position = 0 then 448 begin 449 result := ec1; 450 exit; 451 end; 452 end else 453 begin 454 if position <= 0 then 455 begin 456 result := ec1; 457 exit; 458 end else 459 if position >= 1 then 460 begin 461 result := ec2; 462 exit 463 end; 464 end; 465 interm := GetColorNoBoundCheck(round(position*65536)); 466 if hgoLightnessCorrection in FOptions then 467 result := GSBAToExpanded(interm) 468 else 469 result := HSLAToExpanded(interm); 470 end; 471 472 function TBGRAHueGradient.GetAverageExpandedColor: TExpandedPixel; 473 begin 474 Result:= GetExpandedColorAt(32768); 385 475 end; 386 476 … … 417 507 end; 418 508 509 function TBGRAMultiGradient.CosineInterpolation(t: single): single; 510 begin 511 result := (1-cos(t*Pi))*0.5; 512 end; 513 514 function TBGRAMultiGradient.HalfCosineInterpolation(t: single): single; 515 begin 516 result := (1-cos(t*Pi))*0.25 + t*0.5; 517 end; 518 419 519 constructor TBGRAMultiGradient.Create(Colors: array of TBGRAPixel; 420 520 Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean); … … 424 524 425 525 function TBGRAMultiGradient.GetColorAt(position: integer): TBGRAPixel; 426 var i: integer;526 var i: NativeInt; 427 527 ec: TExpandedPixel; 528 curPos,posDiff: NativeInt; 428 529 begin 429 530 if FCycle then … … 435 536 begin 436 537 i := 0; 437 while (i < high(FPositions) ) and (position >FPositions[i+1]) do538 while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do 438 539 inc(i); 439 540 440 if Position = FPositions[i +1] then441 result := FColors[i +1]541 if Position = FPositions[i] then 542 result := FColors[i] 442 543 else 443 if GammaCorrection then 444 begin 445 ec.red := FEColors[i].red + (position-FPositions[i])*(FEColors[i+1].red-FEColors[i].red) div (FPositions[i+1]-FPositions[i]); 446 ec.green := FEColors[i].green + (position-FPositions[i])*(FEColors[i+1].green-FEColors[i].green) div (FPositions[i+1]-FPositions[i]); 447 ec.blue := FEColors[i].blue + (position-FPositions[i])*(FEColors[i+1].blue-FEColors[i].blue) div (FPositions[i+1]-FPositions[i]); 448 ec.alpha := FEColors[i].alpha + (position-FPositions[i])*(FEColors[i+1].alpha-FEColors[i].alpha) div (FPositions[i+1]-FPositions[i]); 449 result := GammaCompression(ec); 450 end else 451 begin 452 result.red := FColors[i].red + (position-FPositions[i])*(FColors[i+1].red-FColors[i].red) div (FPositions[i+1]-FPositions[i]); 453 result.green := FColors[i].green + (position-FPositions[i])*(FColors[i+1].green-FColors[i].green) div (FPositions[i+1]-FPositions[i]); 454 result.blue := FColors[i].blue + (position-FPositions[i])*(FColors[i+1].blue-FColors[i].blue) div (FPositions[i+1]-FPositions[i]); 455 result.alpha := FColors[i].alpha + (position-FPositions[i])*(FColors[i+1].alpha-FColors[i].alpha) div (FPositions[i+1]-FPositions[i]); 544 begin 545 curPos := position-FPositions[i]; 546 posDiff := FPositions[i+1]-FPositions[i]; 547 if FInterpolationFunction <> nil then 548 begin 549 curPos := round(FInterpolationFunction(curPos/posDiff)*65536); 550 posDiff := 65536; 551 end; 552 if GammaCorrection then 553 begin 554 if FEColors[i+1].red < FEColors[i].red then 555 ec.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else 556 ec.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff); 557 if FEColors[i+1].green < FEColors[i].green then 558 ec.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else 559 ec.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff); 560 if FEColors[i+1].blue < FEColors[i].blue then 561 ec.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else 562 ec.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff); 563 if FEColors[i+1].alpha < FEColors[i].alpha then 564 ec.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else 565 ec.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff); 566 result := GammaCompression(ec); 567 end else 568 begin 569 result.red := FColors[i].red + (curPos)*(FColors[i+1].red-FColors[i].red) div (posDiff); 570 result.green := FColors[i].green + (curPos)*(FColors[i+1].green-FColors[i].green) div (posDiff); 571 result.blue := FColors[i].blue + (curPos)*(FColors[i+1].blue-FColors[i].blue) div (posDiff); 572 result.alpha := FColors[i].alpha + (curPos)*(FColors[i+1].alpha-FColors[i].alpha) div (posDiff); 573 end; 574 end; 575 end; 576 end; 577 578 function TBGRAMultiGradient.GetExpandedColorAt(position: integer 579 ): TExpandedPixel; 580 var i: NativeInt; 581 curPos,posDiff: NativeInt; 582 rw,gw,bw: NativeUInt; 583 begin 584 if FCycle then 585 position := (position-FPositions[0]) mod (FPositions[high(FPositions)] - FPositions[0]) + FPositions[0]; 586 if position <= FPositions[0] then 587 result := FEColors[0] else 588 if position >= FPositions[high(FPositions)] then 589 result := FEColors[high(FColors)] else 590 begin 591 i := 0; 592 while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do 593 inc(i); 594 595 if Position = FPositions[i] then 596 result := FEColors[i] 597 else 598 begin 599 curPos := position-FPositions[i]; 600 posDiff := FPositions[i+1]-FPositions[i]; 601 if FInterpolationFunction <> nil then 602 begin 603 curPos := round(FInterpolationFunction(curPos/posDiff)*65536); 604 posDiff := 65536; 605 end; 606 if GammaCorrection then 607 begin 608 if FEColors[i+1].red < FEColors[i].red then 609 result.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else 610 result.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff); 611 if FEColors[i+1].green < FEColors[i].green then 612 result.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else 613 result.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff); 614 if FEColors[i+1].blue < FEColors[i].blue then 615 result.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else 616 result.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff); 617 if FEColors[i+1].alpha < FEColors[i].alpha then 618 result.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else 619 result.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff); 620 end else 621 begin 622 rw := NativeInt(FColors[i].red shl 8) + (((curPos) shl 8)*(FColors[i+1].red-FColors[i].red)) div (posDiff); 623 gw := NativeInt(FColors[i].green shl 8) + (((curPos) shl 8)*(FColors[i+1].green-FColors[i].green)) div (posDiff); 624 bw := NativeInt(FColors[i].blue shl 8) + (((curPos) shl 8)*(FColors[i+1].blue-FColors[i].blue)) div (posDiff); 625 626 if rw >= $ff00 then result.red := $ffff 627 else result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8; 628 if gw >= $ff00 then result.green := $ffff 629 else result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8; 630 if bw >= $ff00 then result.blue := $ffff 631 else result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8; 632 result.alpha := NativeInt(FColors[i].alpha shl 8) + (((curPos) shl 8)*(FColors[i+1].alpha-FColors[i].alpha)) div (posDiff); 633 result.alpha := result.alpha + (result.alpha shr 8); 634 end; 456 635 end; 457 636 end; … … 544 723 end; 545 724 725 function TBGRASimpleGradientWithGammaCorrection.GetExpandedColorAt( 726 position: integer): TExpandedPixel; 727 var b,b2: cardinal; 728 begin 729 if position <= 0 then 730 result := ec1 else 731 if position >= 65536 then 732 result := ec2 else 733 begin 734 b := position; 735 b2 := 65536-b; 736 result.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16; 737 result.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16; 738 result.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16; 739 result.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16; 740 end; 741 end; 742 743 function TBGRASimpleGradientWithGammaCorrection.GetExpandedColorAtF( 744 position: single): TExpandedPixel; 745 var b,b2: cardinal; 746 begin 747 if position <= 0 then 748 result := ec1 else 749 if position >= 1 then 750 result := ec2 else 751 begin 752 b := round(position*65536); 753 b2 := 65536-b; 754 result.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16; 755 result.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16; 756 result.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16; 757 result.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16; 758 end; 759 end; 760 761 function TBGRASimpleGradientWithGammaCorrection.GetAverageExpandedColor: TExpandedPixel; 762 begin 763 result := MergeBGRA(ec1,ec2); 764 end; 765 546 766 function TBGRASimpleGradientWithGammaCorrection.GetMonochrome: boolean; 547 767 begin … … 556 776 FColor1 := Color1; 557 777 FColor2 := Color2; 778 ec1 := GammaExpansion(Color1); 779 ec2 := GammaExpansion(Color2); 558 780 end; 559 781 … … 577 799 578 800 function TBGRASimpleGradientWithoutGammaCorrection.GetColorAtF(position: single): TBGRAPixel; 579 var b,b2: cardinal;580 801 begin 581 802 if position <= 0 then … … 583 804 if position >= 1 then 584 805 result := FColor2 else 585 begin 586 b := round(position*1024); 806 result := GetColorAt(round(position*65536)); 807 end; 808 809 function TBGRASimpleGradientWithoutGammaCorrection.GetExpandedColorAt( 810 position: integer): TExpandedPixel; 811 var b,b2: cardinal; 812 rw,gw,bw: word; 813 begin 814 if position <= 0 then 815 result := ec1 else 816 if position >= 65536 then 817 result := ec2 else 818 begin 819 b := position shr 6; 587 820 b2 := 1024-b; 588 result.red := (FColor1.red * b2 + FColor2.red * b + 511) shr 10; 589 result.green := (FColor1.green * b2 + FColor2.green * b + 511) shr 10; 590 result.blue := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 10; 591 result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 10; 592 end; 821 rw := (FColor1.red * b2 + FColor2.red * b + 511) shr 2; 822 gw := (FColor1.green * b2 + FColor2.green * b + 511) shr 2; 823 bw := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 2; 824 825 result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8; 826 result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8; 827 result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8; 828 result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 2; 829 end; 830 end; 831 832 function TBGRASimpleGradientWithoutGammaCorrection.GetExpandedColorAtF( 833 position: single): TExpandedPixel; 834 begin 835 if position <= 0 then 836 result := ec1 else 837 if position >= 1 then 838 result := ec2 else 839 result := GetExpandedColorAt(round(position*65536)); 593 840 end; 594 841 … … 675 922 end; 676 923 924 function TBGRAGradientTriangleScanner.ScanNextExpandedPixel: TExpandedPixel; 925 var r,g,b,a: int64; 926 begin 927 r := round(FCurColor[1]); 928 g := round(FCurColor[2]); 929 b := round(FCurColor[3]); 930 a := round(FCurColor[4]); 931 if r > 65535 then r := 65535 else 932 if r < 0 then r := 0; 933 if g > 65535 then g := 65535 else 934 if g < 0 then g := 0; 935 if b > 65535 then b := 65535 else 936 if b < 0 then b := 0; 937 if a > 65535 then a := 65535 else 938 if a < 0 then a := 0; 939 result.red := r; 940 result.green := g; 941 result.blue := b; 942 result.alpha := a; 943 FCurColor += FStep; 944 end; 945 677 946 { TBGRAGradientScanner } 678 947 … … 704 973 FVertical := (((gtype =gtLinear) or (gtype=gtReflected)) and (o1.x=o2.x)) or FGradient.Monochrome; 705 974 mergedColor := FGradient.GetAverageColor; 975 mergedExpandedColor := FGradient.GetAverageExpandedColor; 706 976 end; 707 977 … … 763 1033 end; 764 1034 1035 function TBGRAGradientScanner.ScanNextExpandedInline: TExpandedPixel; 1036 var 1037 a,a2: single; 1038 ai: integer; 1039 begin 1040 if FGradientType >= gtDiamond then 1041 begin 1042 if FGradientType = gtRadial then 1043 begin 1044 a := sqrt(sqr(FDotProduct) + sqr(FDotProductPerp)); 1045 FDotProduct += u.x; 1046 FDotProductPerp += u.y; 1047 end else 1048 begin 1049 a := abs(FDotProduct); 1050 a2 := abs(FDotProductPerp); 1051 if a2 > a then a := a2; 1052 FDotProduct += u.x; 1053 FDotProductPerp += u.y; 1054 end; 1055 end else 1056 if FGradientType = gtReflected then 1057 begin 1058 a := abs(FDotProduct); 1059 FDotProduct += u.x; 1060 end else 1061 begin 1062 a := FDotProduct; 1063 FDotProduct += u.x; 1064 end; 1065 1066 if FSinus then 1067 begin 1068 a *= aFactor; 1069 if a <= low(int64) then 1070 result := FGradient.GetAverageExpandedColor 1071 else 1072 if a >= high(int64) then 1073 result := FGradient.GetAverageExpandedColor 1074 else 1075 begin 1076 ai := Sin65536(round(a)); 1077 result := FGradient.GetExpandedColorAt(ai); 1078 end; 1079 end else 1080 result := FGradient.GetExpandedColorAtF(a*aFactorF); 1081 end; 1082 765 1083 constructor TBGRAGradientScanner.Create(c1, c2: TBGRAPixel; 766 1084 gtype: TGradientType; o1, o2: TPointF; gammaColorCorrection: boolean; … … 814 1132 InitScanInline(X,Y); 815 1133 if FVertical then 1134 begin 816 1135 FHorizColor := ScanNextInline; 1136 FHorizExpandedColor := ScanNextExpandedInline; 1137 end; 817 1138 end; 818 1139 … … 823 1144 else 824 1145 result := ScanNextInline; 1146 end; 1147 1148 function TBGRAGradientScanner.ScanNextExpandedPixel: TExpandedPixel; 1149 begin 1150 if FVertical then 1151 result := FHorizExpandedColor 1152 else 1153 result := ScanNextExpandedInline; 825 1154 end; 826 1155 … … 853 1182 begin 854 1183 a := a*aFactor; 855 if a <= low(int64) then 856 result := FGradient.GetAverageColor 857 else 858 if a >= high(int64) then 859 result := FGradient.GetAverageColor 1184 if (a <= low(int64)) or (a >= high(int64)) then 1185 result := mergedColor 860 1186 else 861 1187 begin … … 865 1191 end else 866 1192 result := FGradient.GetColorAtF(a*aFactorF); 1193 end; 1194 1195 function TBGRAGradientScanner.ScanAtExpanded(X, Y: Single): TExpandedPixel; 1196 var p: TPointF; 1197 a,a2: single; 1198 ai: integer; 1199 begin 1200 if len = 0 then 1201 begin 1202 result := mergedExpandedColor; 1203 exit; 1204 end; 1205 1206 p.x := X - FOrigin1.x; 1207 p.y := Y - FOrigin1.y; 1208 case FGradientType of 1209 gtLinear: a := p.x * u.x + p.y * u.y; 1210 gtReflected: a := abs(p.x * u.x + p.y * u.y); 1211 gtDiamond: 1212 begin 1213 a := abs(p.x * u.x + p.y * u.y); 1214 a2 := abs(p.x * u.y - p.y * u.x); 1215 if a2 > a then a := a2; 1216 end; 1217 gtRadial: a := sqrt(sqr(p.x * u.x + p.y * u.y) + sqr(p.x * u.y - p.y * u.x)); 1218 end; 1219 1220 if FSinus then 1221 begin 1222 a := a*aFactor; 1223 if (a <= low(int64)) or (a >= high(int64)) then 1224 result := mergedExpandedColor 1225 else 1226 begin 1227 ai := Sin65536(round(a)); 1228 result := FGradient.GetExpandedColorAt(ai); 1229 end; 1230 end else 1231 result := FGradient.GetExpandedColorAtF(a*aFactorF); 867 1232 end; 868 1233 … … 961 1326 var c: TBGRAPixel; 962 1327 alpha: byte; 963 MemMask, pmask, MemTex, ptex: pbgrapixel;1328 pmask, ptex: pbgrapixel; 964 1329 965 1330 function GetNext: TBGRAPixel; inline; … … 982 1347 983 1348 begin 984 getmem(MemMask, count*sizeof(TBGRAPixel));985 ScannerPutPixels(FMask,MemMask,count,dmSet);986 getmem(MemTex, count*sizeof(TBGRAPixel));987 ScannerPutPixels(FTexture, MemTex,count,dmSet);988 989 pmask := MemMask;990 ptex := MemTex;1349 if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count)); 1350 if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count)); 1351 ScannerPutPixels(FMask,@FMemMask[0],count,dmSet); 1352 ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet); 1353 1354 pmask := @FMemMask[0]; 1355 ptex := @FMemTex[0]; 991 1356 992 1357 if FGlobalOpacity <> 255 then … … 1071 1436 end; 1072 1437 end; 1073 1074 freemem(MemMask);1075 freemem(MemTex);1076 1438 end; 1077 1439 … … 1125 1487 var c: TBGRAPixel; 1126 1488 alpha: byte; 1127 MemMask,pmask: pbgrapixel;1489 pmask: pbgrapixel; 1128 1490 1129 1491 function GetNext: TBGRAPixel; inline; … … 1136 1498 1137 1499 begin 1138 getmem(MemMask, count*sizeof(TBGRAPixel));1139 ScannerPutPixels(FMask, MemMask,count,dmSet);1140 1141 pmask := MemMask;1500 if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count)); 1501 ScannerPutPixels(FMask,@FMemMask[0],count,dmSet); 1502 1503 pmask := @FMemMask[0]; 1142 1504 1143 1505 case mode of … … 1179 1541 end; 1180 1542 end; 1181 1182 freemem(MemMask);1183 1543 end; 1184 1544 … … 1229 1589 mode: TDrawMode); 1230 1590 var c: TBGRAPixel; 1231 MemTex,ptex: pbgrapixel;1591 ptex: pbgrapixel; 1232 1592 1233 1593 function GetNext: TBGRAPixel; inline; … … 1239 1599 1240 1600 begin 1241 getmem(MemTex, count*sizeof(TBGRAPixel));1242 ScannerPutPixels(FTexture, MemTex,count,dmSet);1243 1244 ptex := MemTex;1601 if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count)); 1602 ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet); 1603 1604 ptex := @FMemTex[0]; 1245 1605 1246 1606 case mode of … … 1282 1642 end; 1283 1643 end; 1284 1285 freemem(MemTex);1286 1644 end; 1287 1645 -
GraphicTest/Packages/bgrabitmap/bgragrayscalemask.pas
r472 r494 39 39 procedure DownSamplePutImageGrayscale(source: TBGRACustomBitmap; dest: TGrayscaleMask; ADestRect: TRect); 40 40 41 procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x, 42 y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; 43 texture: IBGRAScanner; RGBOrder: boolean); 44 41 45 implementation 42 46 43 uses GraphType, BGRABlend; 47 uses BGRABlend; 48 49 procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x, 50 y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; 51 texture: IBGRAScanner; RGBOrder: boolean); 52 var delta: NativeInt; 53 begin 54 delta := mask.Width; 55 BGRABlend.BGRAFillClearTypeMaskPtr(dest,x,y,xThird,mask.ScanLine[0],1,delta,mask.Width,mask.Height,color,texture,RGBOrder); 56 end; 44 57 45 58 { TGrayscaleMask } … … 77 90 pdest := FData; 78 91 Case AChannel of 79 cAlpha: ofs := 3;80 cRed: ofs := 2;81 cGreen: ofs := 1;92 cAlpha: ofs := TBGRAPixel_AlphaByteOffset; 93 cRed: ofs := TBGRAPixel_RedByteOffset; 94 cGreen: ofs := TBGRAPixel_GreenByteOffset; 82 95 else 83 ofs := 0;96 ofs := TBGRAPixel_BlueByteOffset; 84 97 end; 85 98 for y := 0 to FHeight-1 do -
GraphicTest/Packages/bgrabitmap/bgragtkbitmap.pas
r472 r494 28 28 29 29 uses 30 Classes, SysUtils, BGRA DefaultBitmap, Graphics,30 Classes, SysUtils, BGRALCLBitmap, Graphics, 31 31 GraphType; 32 32 … … 34 34 { TBGRAGtkBitmap } 35 35 36 TBGRAGtkBitmap = class(TBGRA DefaultBitmap)36 TBGRAGtkBitmap = class(TBGRALCLBitmap) 37 37 private 38 38 FPixBuf: Pointer; 39 { procedure SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;40 ACanvas: TCanvas; ARect: TRect);}41 39 procedure DrawTransparent(ACanvas: TCanvas; Rect: TRect); 42 40 procedure DrawOpaque(ACanvas: TCanvas; Rect: TRect); … … 57 55 implementation 58 56 59 uses BGRABitmapTypes, LCLType,57 uses BGRABitmapTypes, BGRADefaultBitmap, LCLType, 60 58 LCLIntf, IntfGraphics, 61 59 {$IFDEF LCLgtk2} … … 70 68 type TGtkDeviceContext = TGtk2DeviceContext; 71 69 {$ENDIF} 72 73 {procedure TBGRAGtkBitmap.SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;74 ACanvas: TCanvas; ARect: TRect);75 var76 background, temp: TBGRACustomBitmap;77 w, h: integer;78 79 begin80 w := ARect.Right - ARect.Left;81 h := ARect.Bottom - ARect.Top;82 background := NewBitmap(w, h);83 background.GetImageFromCanvas(ACanvas, ARect.Left, ARect.Top);84 if (ABitmap.Width = w) and (ABitmap.Height = h) then85 background.PutImage(0, 0, ABitmap, dmDrawWithTransparency)86 else87 begin88 temp := ABitmap.Resample(w, h, rmSimpleStretch);89 background.PutImage(0, 0, temp, dmDrawWithTransparency);90 temp.Free;91 end;92 background.Draw(ACanvas, ARect.Left, ARect.Top, True);93 background.Free;94 end;}95 70 96 71 procedure TBGRAGtkBitmap.ReallocData; … … 141 116 end; 142 117 143 SwapRedBlue;118 If not TBGRAPixel_RGBAOrder then SwapRedBlue; 144 119 145 120 P := Rect.TopLeft; … … 152 127 GDK_RGB_DITHER_NORMAL,0,0); 153 128 154 SwapRedBlue;129 If not TBGRAPixel_RGBAOrder then SwapRedBlue; 155 130 end; 156 131 … … 252 227 LPtoDP(dest, pos, 1); 253 228 If ALineOrder = riloBottomToTop then VerticalFlip; 254 SwapRedBlue;229 If not TBGRAPixel_RGBAOrder then SwapRedBlue; 255 230 gdk_draw_rgb_32_image(TGtkDeviceContext(dest).Drawable, 256 231 TGtkDeviceContext(Dest).GC, pos.x,pos.y, 257 232 AWidth,AHeight, GDK_RGB_DITHER_NORMAL, 258 233 AData, AWidth*sizeof(TBGRAPixel)); 259 SwapRedBlue;234 If not TBGRAPixel_RGBAOrder then SwapRedBlue; 260 235 If ALineOrder = riloBottomToTop then VerticalFlip; 261 236 end; … … 297 272 TGtkDeviceContext(CanvasSource.Handle).Drawable, 298 273 nil, P.X,P.Y,0,0,Width,Height); 299 SwapRedBlue;274 If not TBGRAPixel_RGBAOrder then SwapRedBlue; 300 275 InvalidateBitmap; 301 276 end; -
GraphicTest/Packages/bgrabitmap/bgralayers.pas
r472 r494 6 6 7 7 uses 8 Graphics, Classes, SysUtils, Types, BGRABitmapTypes, BGRABitmap;8 BGRAGraphics, Classes, SysUtils, Types, BGRABitmapTypes, BGRABitmap; 9 9 10 10 type … … 209 209 implementation 210 210 211 uses LCLProc;211 uses BGRAUTF8; 212 212 213 213 var -
GraphicTest/Packages/bgrabitmap/bgramatrix3d.pas
r472 r494 4 4 5 5 {$i bgrasse.inc} 6 {$ifdef BGRASSE_AVAILABLE} 6 7 {$ifdef CPUI386} 7 8 {$asmmode intel} 8 {$endif} 9 {$ENDIF} 10 {$ifdef cpux86_64} 11 {$asmmode intel} 12 {$ENDIF} 9 13 10 14 interface 11 15 12 16 uses 13 BGRABitmapTypes, BGRASSE; 17 BGRABitmapTypes, BGRASSE, 18 BGRATransform; 14 19 15 20 type 16 21 TMatrix3D = packed array[1..3,1..4] of single; 22 TMatrix4D = packed array[1..4,1..4] of single; 17 23 TProjection3D = packed record 18 24 Zoom, Center: TPointF; 19 25 end; 26 TComputeProjectionFunc = function(AViewCoord: TPoint3D_128): TPointF of object; 20 27 21 28 operator*(const A: TMatrix3D; const M: TPoint3D): TPoint3D; … … 35 42 function MatrixRotateZ(angle: single): TMatrix3D; 36 43 44 operator *(const A, B: TMatrix4D): TMatrix4D; 45 function MatrixIdentity4D: TMatrix4D; 46 function AffineMatrixToMatrix4D(AValue: TAffineMatrix): TMatrix4D; 47 37 48 {$IFDEF BGRASSE_AVAILABLE} 38 49 procedure Matrix3D_SSE_Load(const A: TMatrix3D); … … 45 56 implementation 46 57 47 procedure multiplyVect Inline(const A : TMatrix3D; const vx,vy,vz,vt: single; out outx,outy,outz: single);58 procedure multiplyVect3(const A : TMatrix3D; const vx,vy,vz,vt: single; out outx,outy,outz: single); 48 59 begin 49 60 outx := vx * A[1,1] + vy * A[1,2] + vz * A[1,3] + vt * A[1,4]; … … 52 63 end; 53 64 65 procedure multiplyVect4(const A : TMatrix4D; const vx,vy,vz,vt: single; out outx,outy,outz,outt: single); 66 begin 67 outx := vx * A[1,1] + vy * A[1,2] + vz * A[1,3] + vt * A[1,4]; 68 outy := vx * A[2,1] + vy * A[2,2] + vz * A[2,3] + vt * A[2,4]; 69 outz := vx * A[3,1] + vy * A[3,2] + vz * A[3,3] + vt * A[3,4]; 70 outt := vx * A[4,1] + vy * A[4,2] + vz * A[4,3] + vt * A[4,4]; 71 end; 72 54 73 operator*(const A: TMatrix3D; const M: TPoint3D): TPoint3D; 55 74 begin … … 57 76 result.y := M.x * A[2,1] + M.y * A[2,2] + M.z * A[2,3] + A[2,4]; 58 77 result.z := M.x * A[3,1] + M.y * A[3,2] + M.z * A[3,3] + A[3,4]; 78 end; 79 80 operator*(const A, B: TMatrix4D): TMatrix4D; 81 begin 82 multiplyVect4(A, B[1,1],B[2,1],B[3,1],B[4,1], result[1,1],result[2,1],result[3,1],result[4,1]); 83 multiplyVect4(A, B[1,2],B[2,2],B[3,2],B[4,2], result[1,2],result[2,2],result[3,2],result[4,2]); 84 multiplyVect4(A, B[1,3],B[2,3],B[3,3],B[4,3], result[1,3],result[2,3],result[3,3],result[4,3]); 85 multiplyVect4(A, B[1,4],B[2,4],B[3,4],B[4,4], result[1,4],result[2,4],result[3,4],result[4,4]); 86 end; 87 88 function MatrixIdentity4D: TMatrix4D; 89 begin 90 result[1,1] := 1; result[2,1] := 0; result[3,1] := 0; result[4,1] := 0; 91 result[1,2] := 0; result[2,2] := 1; result[3,2] := 0; result[4,2] := 0; 92 result[1,3] := 0; result[2,3] := 0; result[3,3] := 1; result[4,3] := 0; 93 result[1,4] := 0; result[2,4] := 0; result[3,4] := 0; result[4,4] := 1; 94 end; 95 96 function AffineMatrixToMatrix4D(AValue: TAffineMatrix): TMatrix4D; 97 begin 98 result[1,1] := AValue[1,1]; result[2,1] := AValue[1,2]; result[3,1] := 0; result[4,1] := AValue[1,3]; 99 result[1,2] := AValue[2,1]; result[2,2] := AValue[2,2]; result[3,2] := 0; result[4,2] := AValue[2,3]; 100 result[1,3] := 0; result[2,3] := 0; result[3,3] := 1; result[4,3] := 0; 101 result[1,4] := 0; result[2,4] := 0; result[3,4] := 0; result[4,4] := 1; 59 102 end; 60 103 … … 391 434 392 435 operator*(constref A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128; 393 {$IFDEF CPUI386}var oldMt: single; {$ENDIF}394 begin 395 {$IFDEF CPUI386}436 {$IFDEF BGRASSE_AVAILABLE}var oldMt: single; {$ENDIF} 437 begin 438 {$IFDEF BGRASSE_AVAILABLE} 396 439 if UseSSE then 397 440 begin … … 490 533 function MultiplyVect3DWithoutTranslation(constref A: TMatrix3D; constref M: TPoint3D_128): TPoint3D_128; 491 534 begin 492 {$IFDEF CPUI386}535 {$IFDEF BGRASSE_AVAILABLE} 493 536 if UseSSE then 494 537 begin … … 583 626 operator*(A,B: TMatrix3D): TMatrix3D; 584 627 begin 585 multiplyVect Inline(A, B[1,1],B[2,1],B[3,1],0, result[1,1],result[2,1],result[3,1]);586 multiplyVect Inline(A, B[1,2],B[2,2],B[3,2],0, result[1,2],result[2,2],result[3,2]);587 multiplyVect Inline(A, B[1,3],B[2,3],B[3,3],0, result[1,3],result[2,3],result[3,3]);588 multiplyVect Inline(A, B[1,4],B[2,4],B[3,4],1, result[1,4],result[2,4],result[3,4]);628 multiplyVect3(A, B[1,1],B[2,1],B[3,1],0, result[1,1],result[2,1],result[3,1]); 629 multiplyVect3(A, B[1,2],B[2,2],B[3,2],0, result[1,2],result[2,2],result[3,2]); 630 multiplyVect3(A, B[1,3],B[2,3],B[3,3],0, result[1,3],result[2,3],result[3,3]); 631 multiplyVect3(A, B[1,4],B[2,4],B[3,4],1, result[1,4],result[2,4],result[3,4]); 589 632 end; 590 633 -
GraphicTest/Packages/bgrabitmap/bgraopenraster.pas
r472 r494 84 84 implementation 85 85 86 uses Graphics, XMLRead, XMLWrite, FPReadPNG, dialogs, BGRABitmapTypes, zstream, lazutf8classes,86 uses XMLRead, XMLWrite, FPReadPNG, BGRABitmapTypes, zstream, BGRAUTF8, 87 87 UnzipperExt; 88 88 … … 132 132 133 133 function TFPReaderOpenRaster.InternalCheck(Stream: TStream): boolean; 134 var {%h-}magic: packed array[0..3] of byte;134 var magic: packed array[0..3] of byte; 135 135 OldPos,BytesRead: Int64; 136 136 doc : TBGRAOpenRasterDocument; … … 139 139 if Stream=nil then exit; 140 140 oldPos := stream.Position; 141 BytesRead := Stream.Read({%h-}magic,sizeof(magic)); 141 {$PUSH}{$HINTS OFF} 142 BytesRead := Stream.Read(magic,sizeof(magic)); 143 {$POP} 142 144 stream.Position:= OldPos; 143 145 if BytesRead<>sizeof(magic) then exit; … … 303 305 BlendOperation[idx] := boOverlay else 304 306 if opstr = 'svg:soft-light' then 305 BlendOperation[idx] := boS oftLight else307 BlendOperation[idx] := boSvgSoftLight else 306 308 if opstr = 'svg:hard-light' then 307 309 BlendOperation[idx] := boHardLight else … … 320 322 if opstr = 'krita:divide' then 321 323 BlendOperation[idx] := boDivide else 324 if opstr = 'bgra:soft-light' then 325 BlendOperation[idx] := boSoftLight else 322 326 if opstr = 'bgra:nice-glow' then 323 327 BlendOperation[idx] := boNiceGlow else … … 331 335 BlendOperation[idx] := boXor else 332 336 begin 333 messagedlg('Unknown blend operation : ' + attr.NodeValue,mtInformation,[mbOk],0); 337 //messagedlg('Unknown blend operation : ' + attr.NodeValue,mtInformation,[mbOk],0); 338 BlendOperation[idx] := boTransparent; 334 339 end; 335 340 end; … … 408 413 boMultiply: strval := 'svg:multiply'; 409 414 boOverlay, boDarkOverlay: strval := 'svg:overlay'; 410 boSoftLight: strval := ' svg:soft-light';415 boSoftLight: strval := 'bgra:soft-light'; 411 416 boHardLight: strval := 'svg:hard-light'; 412 417 boDifference,boLinearDifference: strval := 'svg:difference'; … … 420 425 boLinearNegation,boNegation: strval := 'bgra:negation'; 421 426 boXor: strval := 'bgra:xor'; 427 boSvgSoftLight: strval := 'svg:soft-light'; 422 428 else strval := 'svg:src-over'; 423 429 end; -
GraphicTest/Packages/bgrabitmap/bgrapaintnet.pas
r472 r494 80 80 implementation 81 81 82 uses zstream, Math, graphtype, Graphics, lazutf8classes, FileUtil;82 uses zstream, Math, BGRAUTF8; 83 83 84 84 {$hints off} … … 285 285 Stream.Position:= Stream.Position + XmlHeaderSize; 286 286 {$hints off} 287 stream.Read (CompressionFormat, sizeof(CompressionFormat));287 stream.ReadBuffer(CompressionFormat, sizeof(CompressionFormat)); 288 288 {$hints on} 289 289 CompressionFormat := LEToN(CompressionFormat); … … 327 327 begin 328 328 {$hints off} 329 LayerData[i].Read (b, 1);329 LayerData[i].ReadBuffer(b, 1); 330 330 {$hints on} 331 331 Result += IntToHex(b, 2) + ' '; … … 418 418 layerData[layer].Position := 0; 419 419 layerData[layer].Read(Result.Data^, LayerData[layer].Size); 420 if TBGRAPixel_RGBAOrder then result.SwapRedBlue; 420 421 Result.InvalidateBitmap; 421 422 … … 476 477 begin 477 478 {$hints off} 478 src.Read (CompressionFlag, 1);479 src.ReadBuffer(CompressionFlag, 1); 479 480 {$hints on} 480 481 if CompressionFlag = 1 then -
GraphicTest/Packages/bgrabitmap/bgrapalette.pas
r472 r494 24 24 25 25 type 26 TBGRAIndexedPaletteEntry = packed record 27 Color: TBGRAPixel; 28 Index: UInt32; 29 end; 30 PBGRAIndexedPaletteEntry = ^TBGRAIndexedPaletteEntry; 26 31 TBGRAWeightedPaletteEntry = packed record 27 32 Color: TBGRAPixel; … … 31 36 ArrayOfWeightedColor = array of TBGRAWeightedPaletteEntry; 32 37 38 TBGRAPixelComparer = function (p1,p2 : PBGRAPixel): boolean; 39 33 40 { TBGRACustomPalette } 34 41 35 42 TBGRACustomPalette = class 43 private 44 function GetDominantColor: TBGRAPixel; 36 45 protected 37 46 function GetCount: integer; virtual; abstract; … … 44 53 procedure AssignTo(AImage: TFPCustomImage); overload; 45 54 procedure AssignTo(APalette: TFPPalette); overload; 55 property DominantColor: TBGRAPixel read GetDominantColor; 46 56 property Count: integer read GetCount; 47 57 property Color[AIndex: integer]: TBGRAPixel read GetColorByIndex; … … 86 96 public 87 97 constructor Create(ABitmap: TBGRACustomBitmap); virtual; overload; 98 constructor Create(APalette: TBGRACustomPalette); virtual; overload; 99 constructor Create(AColors: ArrayOfTBGRAPixel); virtual; overload; 100 constructor Create(AColors: ArrayOfWeightedColor); virtual; overload; 88 101 function AddColor(AValue: TBGRAPixel): boolean; virtual; 102 procedure AddColors(ABitmap: TBGRACustomBitmap); virtual; overload; 103 procedure AddColors(APalette: TBGRACustomPalette); virtual; overload; 89 104 function RemoveColor(AValue: TBGRAPixel): boolean; virtual; 90 105 procedure LoadFromFile(AFilenameUTF8: string); virtual; … … 97 112 end; 98 113 114 { TBGRAIndexedPalette } 115 116 TBGRAIndexedPalette = class(TBGRAPalette) 117 private 118 FCurrentIndex: UInt32; 119 protected 120 procedure NeedArray; override; 121 function CreateEntry(AColor: TBGRAPixel): PBGRAPixel; override; 122 procedure FreeEntry(AEntry: PBGRAPixel); override; 123 public 124 function RemoveColor({%H-}AValue: TBGRAPixel): boolean; override; 125 function IndexOfColor(AValue: TBGRAPixel): integer; override; 126 procedure Clear; override; 127 end; 128 99 129 { TBGRAWeightedPalette } 100 130 … … 107 137 procedure IncludePixel(PPixel: PBGRAPixel); override; 108 138 public 139 constructor Create(AColors: ArrayOfWeightedColor); override; 109 140 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override; 110 141 function IncColor(AValue: TBGRAPixel; out NewWeight: UInt32): boolean; … … 127 158 TBGRACustomApproxPalette = class(TBGRACustomPalette) 128 159 private 129 function FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel; 160 function FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel; inline; 161 function FindNearestColorIndexIgnoreAlpha(AValue: TBGRAPixel): integer; inline; 162 protected 163 function GetWeightByIndex({%H-}AIndex: Integer): UInt32; virtual; 130 164 public 131 165 function FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel; overload; 132 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; virtual; abstract; 133 function FindNearestColorIndex(AValue: TBGRAPixel): integer; virtual; abstract; 134 end; 166 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; virtual; abstract; overload; 167 function FindNearestColorIndex(AValue: TBGRAPixel; AIgnoreAlpha: boolean): integer; overload; 168 function FindNearestColorIndex(AValue: TBGRAPixel): integer; virtual; abstract; overload; 169 property Weight[AIndex: Integer]: UInt32 read GetWeightByIndex; 170 end; 171 172 { TBGRA16BitPalette } 173 174 TBGRA16BitPalette = class(TBGRACustomApproxPalette) 175 protected 176 function GetCount: integer; override; 177 function GetColorByIndex(AIndex: integer): TBGRAPixel; override; 178 public 179 function ContainsColor(AValue: TBGRAPixel): boolean; override; 180 function IndexOfColor(AValue: TBGRAPixel): integer; override; 181 function GetAsArrayOfColor: ArrayOfTBGRAPixel; override; 182 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override; 183 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override; 184 function FindNearestColorIndex(AValue: TBGRAPixel): integer; override; 185 end; 186 187 { TBGRACustomColorQuantizer } 188 189 TBGRACustomColorQuantizer = class 190 protected 191 function GetDominantColor: TBGRAPixel; virtual; 192 function GetPalette: TBGRACustomApproxPalette; virtual; abstract; 193 function GetSourceColor(AIndex: integer): TBGRAPixel; virtual; abstract; 194 function GetSourceColorCount: Integer; virtual; abstract; 195 function GetReductionColorCount: integer; virtual; abstract; 196 procedure SetReductionColorCount(AValue: Integer); virtual; abstract; 197 public 198 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); virtual; abstract; overload; 199 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); virtual; abstract; overload; 200 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); virtual; abstract; overload; 201 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); virtual; abstract; overload; 202 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); virtual; abstract; overload; 203 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap); overload; 204 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; virtual; abstract; overload; 205 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; overload; 206 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string); overload; 207 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string; AFormat: TBGRAImageFormat); overload; 208 procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); virtual; abstract; 209 function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; overload; 210 function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): Pointer; overload; 211 function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm; 212 ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; virtual; abstract; overload; 213 property SourceColorCount: Integer read GetSourceColorCount; 214 property SourceColor[AIndex: integer]: TBGRAPixel read GetSourceColor; 215 property ReductionColorCount: Integer read GetReductionColorCount write SetReductionColorCount; 216 property ReducedPalette: TBGRACustomApproxPalette read GetPalette; 217 property DominantColor: TBGRAPixel read GetDominantColor; 218 end; 219 220 TBGRAColorQuantizerAny = class of TBGRACustomColorQuantizer; 221 222 var 223 BGRAColorQuantizerFactory: TBGRAColorQuantizerAny; 135 224 136 225 function BGRARequiredBitDepth(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption): integer; overload; … … 146 235 function BGRARegisteredPaletteFormatFilter(AAllSupportedDescription: string) : string; 147 236 237 procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex, 238 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 239 240 procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex, 241 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 242 243 procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex, 244 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 245 246 procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex, 247 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 248 148 249 implementation 149 250 150 uses lazutf8classes, bufstream; 251 uses BGRAUTF8, bufstream; 252 253 function IsDWordGreater(p1, p2: PBGRAPixel): boolean; 254 begin 255 result := DWord(p1^) > DWord(p2^); 256 end; 257 258 const 259 InsertionSortLimit = 10; 260 261 procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex, 262 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 263 var i,j,insertPos: NativeInt; 264 compared: TBGRAWeightedPaletteEntry; 265 begin 266 if AComparer = nil then AComparer := @IsDWordGreater; 267 for i := AMinIndex+1 to AMaxIndex do 268 begin 269 insertPos := i; 270 compared := AColors[i]; 271 while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1].Color,@compared.Color) do 272 dec(insertPos); 273 if insertPos <> i then 274 begin 275 for j := i downto insertPos+1 do 276 AColors[j] := AColors[j-1]; 277 AColors[insertPos] := compared; 278 end; 279 end; 280 end; 281 282 procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex, 283 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 284 var Pivot: TBGRAPixel; 285 CurMin,CurMax,i : NativeInt; 286 287 procedure Swap(a,b: NativeInt); 288 var Temp: TBGRAWeightedPaletteEntry; 289 begin 290 if a = b then exit; 291 Temp := AColors[a]; 292 AColors[a] := AColors[b]; 293 AColors[b] := Temp; 294 end; 295 begin 296 if AComparer = nil then AComparer := @IsDWordGreater; 297 if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then 298 begin 299 ArrayOfWeightedColor_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer); 300 exit; 301 end; 302 Pivot := AColors[(AMinIndex+AMaxIndex) shr 1].Color; 303 CurMin := AMinIndex; 304 CurMax := AMaxIndex; 305 i := CurMin; 306 while i < CurMax do 307 begin 308 if AComparer(@AColors[i].Color, @Pivot) then 309 begin 310 Swap(i, CurMax); 311 dec(CurMax); 312 end else 313 begin 314 if AComparer(@Pivot, @AColors[i].Color) then 315 begin 316 Swap(i, CurMin); 317 inc(CurMin); 318 end; 319 inc(i); 320 end; 321 end; 322 if AComparer(@Pivot, @AColors[i].Color) then 323 begin 324 Swap(i, CurMin); 325 inc(CurMin); 326 end; 327 if CurMin > AMinIndex then ArrayOfWeightedColor_QuickSort(AColors,AMinIndex,CurMin,AComparer); 328 if CurMax < AMaxIndex then ArrayOfWeightedColor_QuickSort(AColors,CurMax,AMaxIndex,AComparer); 329 end; 330 331 procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex, 332 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 333 var i,j,insertPos: NativeInt; 334 compared: TBGRAPixel; 335 begin 336 if AComparer = nil then AComparer := @IsDWordGreater; 337 for i := AMinIndex+1 to AMaxIndex do 338 begin 339 insertPos := i; 340 compared := AColors[i]; 341 while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1],@compared) do 342 dec(insertPos); 343 if insertPos <> i then 344 begin 345 for j := i downto insertPos+1 do 346 AColors[j] := AColors[j-1]; 347 AColors[insertPos] := compared; 348 end; 349 end; 350 end; 351 352 procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex, 353 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil); 354 var Pivot: TBGRAPixel; 355 CurMin,CurMax,i : NativeInt; 356 357 procedure Swap(a,b: NativeInt); 358 var Temp: TBGRAPixel; 359 begin 360 if a = b then exit; 361 Temp := AColors[a]; 362 AColors[a] := AColors[b]; 363 AColors[b] := Temp; 364 end; 365 begin 366 if AComparer = nil then AComparer := @IsDWordGreater; 367 if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then 368 begin 369 ArrayOfTBGRAPixel_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer); 370 exit; 371 end; 372 Pivot := AColors[(AMinIndex+AMaxIndex) shr 1]; 373 CurMin := AMinIndex; 374 CurMax := AMaxIndex; 375 i := CurMin; 376 while i < CurMax do 377 begin 378 if AComparer(@AColors[i], @Pivot) then 379 begin 380 Swap(i, CurMax); 381 dec(CurMax); 382 end else 383 begin 384 if AComparer(@Pivot, @AColors[i]) then 385 begin 386 Swap(i, CurMin); 387 inc(CurMin); 388 end; 389 inc(i); 390 end; 391 end; 392 if AComparer(@Pivot, @AColors[i]) then 393 begin 394 Swap(i, CurMin); 395 inc(CurMin); 396 end; 397 if CurMin > AMinIndex then ArrayOfTBGRAPixel_QuickSort(AColors,AMinIndex,CurMin,AComparer); 398 if CurMax < AMaxIndex then ArrayOfTBGRAPixel_QuickSort(AColors,CurMax,AMaxIndex,AComparer); 399 end; 151 400 152 401 {$i paletteformats.inc} … … 233 482 end; 234 483 484 { TBGRA16BitPalette } 485 486 function TBGRA16BitPalette.GetCount: integer; 487 begin 488 result := 65537; 489 end; 490 491 function TBGRA16BitPalette.GetColorByIndex(AIndex: integer): TBGRAPixel; 492 begin 493 if (AIndex >= 65536) or (AIndex < 0) then 494 result := BGRAPixelTransparent 495 else 496 result := Color16BitToBGRA(AIndex); 497 end; 498 499 function TBGRA16BitPalette.ContainsColor(AValue: TBGRAPixel): boolean; 500 begin 501 if AValue.alpha = 0 then 502 result := true 503 else 504 result := (AValue.alpha = 255) and (FindNearestColor(AValue)=AValue); 505 end; 506 507 function TBGRA16BitPalette.IndexOfColor(AValue: TBGRAPixel): integer; 508 var idx: integer; 509 begin 510 if AValue.Alpha = 0 then 511 result := 65536 512 else 513 begin 514 idx := BGRAToColor16Bit(AValue); 515 if Color16BitToBGRA(idx)=AValue then 516 result := idx 517 else 518 result := -1; 519 end; 520 end; 521 522 function TBGRA16BitPalette.GetAsArrayOfColor: ArrayOfTBGRAPixel; 523 begin 524 result := nil; 525 raise exception.Create('Palette too big'); 526 end; 527 528 function TBGRA16BitPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor; 529 begin 530 result := nil; 531 raise exception.Create('Palette too big'); 532 end; 533 534 function TBGRA16BitPalette.FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; 535 begin 536 if AValue.alpha = 0 then result := BGRAPixelTransparent 537 else 538 result := GetColorByIndex(BGRAToColor16Bit(AValue)); 539 end; 540 541 function TBGRA16BitPalette.FindNearestColorIndex(AValue: TBGRAPixel): integer; 542 begin 543 result := BGRAToColor16Bit(AValue); 544 end; 545 546 { TBGRAIndexedPalette } 547 548 procedure TBGRAIndexedPalette.NeedArray; 549 var Node: TAvgLvlTreeNode; 550 n: UInt32; 551 begin 552 n := Count; 553 if UInt32(length(FArray)) <> n then 554 begin 555 setLength(FArray,n); 556 for Node in FTree do 557 with PBGRAIndexedPaletteEntry(Node.Data)^ do 558 begin 559 if Index < n then //index is unsigned so always >= 0 560 FArray[Index] := @Color; 561 end; 562 end; 563 end; 564 565 function TBGRAIndexedPalette.CreateEntry(AColor: TBGRAPixel): PBGRAPixel; 566 begin 567 result := PBGRAPixel(GetMem(sizeOf(TBGRAIndexedPaletteEntry))); 568 result^ := AColor; 569 PBGRAIndexedPaletteEntry(result)^.Index := FCurrentIndex; 570 Inc(FCurrentIndex); 571 end; 572 573 procedure TBGRAIndexedPalette.FreeEntry(AEntry: PBGRAPixel); 574 begin 575 FreeMem(AEntry); 576 end; 577 578 function TBGRAIndexedPalette.RemoveColor(AValue: TBGRAPixel): boolean; 579 begin 580 Result:= false; 581 raise exception.Create('It is not possible to remove a color from an indexed palette'); 582 end; 583 584 function TBGRAIndexedPalette.IndexOfColor(AValue: TBGRAPixel): integer; 585 Var Node: TAvgLvlTreeNode; 586 begin 587 Node := FTree.Find(@AValue); 588 if Assigned(Node) then 589 result := PBGRAIndexedPaletteEntry(Node.Data)^.Index 590 else 591 result := -1; 592 end; 593 594 procedure TBGRAIndexedPalette.Clear; 595 begin 596 inherited Clear; 597 FCurrentIndex := 0; 598 end; 599 600 { TBGRACustomColorQuantizer } 601 602 function TBGRACustomColorQuantizer.GetDominantColor: TBGRAPixel; 603 begin 604 result := ReducedPalette.DominantColor; 605 end; 606 607 procedure TBGRACustomColorQuantizer.ApplyDitheringInplace( 608 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap); 609 begin 610 ApplyDitheringInplace(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height)); 611 end; 612 613 function TBGRACustomColorQuantizer.GetDitheredBitmap( 614 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap 615 ): TBGRACustomBitmap; 616 begin 617 result := GetDitheredBitmap(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height)); 618 end; 619 620 procedure TBGRACustomColorQuantizer.SaveBitmapToFile( 621 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 622 AFilenameUTF8: string); 623 begin 624 SaveBitmapToFile(AAlgorithm, ABitmap, AFilenameUTF8, SuggestImageFormat(AFilenameUTF8)); 625 end; 626 627 procedure TBGRACustomColorQuantizer.SaveBitmapToFile( 628 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 629 AFilenameUTF8: string; AFormat: TBGRAImageFormat); 630 var 631 stream: TFileStreamUTF8; 632 begin 633 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmCreate); 634 try 635 SaveBitmapToStream(AAlgorithm, ABitmap, stream, AFormat); 636 finally 637 stream.Free; 638 end; 639 end; 640 641 function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData( 642 ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; 643 out AScanlineSize: PtrInt): Pointer; 644 begin 645 result := GetDitheredBitmapIndexedData(ABitDepth, 646 {$IFDEF ENDIAN_LITTLE}riboLSBFirst{$ELSE}riboMSBFirst{$endif}, 647 AAlgorithm, ABitmap, AScanlineSize); 648 end; 649 650 function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData( 651 ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; 652 ABitmap: TBGRACustomBitmap): Pointer; 653 var dummy: PtrInt; 654 begin 655 result := GetDitheredBitmapIndexedData(ABitDepth, AAlgorithm, ABitmap, dummy); 656 end; 657 235 658 { TBGRACustomPalette } 659 660 function TBGRACustomPalette.GetDominantColor: TBGRAPixel; 661 var 662 w: ArrayOfWeightedColor; 663 i: Integer; 664 maxWeight, totalWeight: UInt32; 665 begin 666 result := BGRAWhite; 667 maxWeight := 0; 668 w := GetAsArrayOfWeightedColor; 669 totalWeight:= 0; 670 for i := 0 to high(w) do 671 inc(totalWeight, w[i].Weight); 672 for i := 0 to high(w) do 673 if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).saturation > 16000) then 674 begin 675 maxWeight:= w[i].Weight; 676 result := w[i].Color; 677 end; 678 if maxWeight > totalWeight div 20 then exit; 679 for i := 0 to high(w) do 680 if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).lightness < 56000) and (BGRAToGSBA(w[i].Color).lightness > 16000) then 681 begin 682 maxWeight:= w[i].Weight; 683 result := w[i].Color; 684 end; 685 if maxWeight > 0 then exit; 686 for i := 0 to high(w) do 687 if (w[i].Weight > maxWeight) then 688 begin 689 maxWeight:= w[i].Weight; 690 result := w[i].Color; 691 end; 692 end; 236 693 237 694 procedure TBGRACustomPalette.AssignTo(AImage: TFPCustomImage); … … 265 722 end; 266 723 724 function TBGRACustomApproxPalette.FindNearestColorIndexIgnoreAlpha( 725 AValue: TBGRAPixel): integer; 726 const AlphaMask : DWord = {$IFDEF ENDIAN_LITTLE}$ff000000{$ELSE}$000000ff{$endif}; 727 begin 728 if AValue.alpha = 0 then 729 result := -1 730 else 731 begin 732 result := FindNearestColorIndex(TBGRAPixel(DWord(AValue) or AlphaMask)); 733 end; 734 end; 735 736 function TBGRACustomApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32; 737 begin 738 result := 1; 739 end; 740 267 741 function TBGRACustomApproxPalette.FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel; 268 742 begin … … 273 747 end; 274 748 749 function TBGRACustomApproxPalette.FindNearestColorIndex(AValue: TBGRAPixel; 750 AIgnoreAlpha: boolean): integer; 751 begin 752 if AIgnoreAlpha then 753 result := FindNearestColorIndexIgnoreAlpha(AValue) 754 else 755 result := FindNearestColorIndex(AValue); 756 end; 757 275 758 { TBGRAWeightedPalette } 276 759 277 function TBGRAWeightedPalette.GetWeightByIndex(AIndex: integer): UInt32;760 function TBGRAWeightedPalette.GetWeightByIndex(AIndex: Integer): UInt32; 278 761 begin 279 762 NeedArray; … … 288 771 begin 289 772 IncColor(PPixel^,dummy); 773 end; 774 775 constructor TBGRAWeightedPalette.Create(AColors: ArrayOfWeightedColor); 776 var 777 i: Integer; 778 begin 779 inherited Create; 780 for i := 0 to high(AColors) do 781 with AColors[i] do IncColor(Color,Weight); 290 782 end; 291 783 … … 645 1137 end; 646 1138 1139 constructor TBGRAPalette.Create(APalette: TBGRACustomPalette); 1140 begin 1141 inherited Create; 1142 AddColors(APalette); 1143 end; 1144 1145 constructor TBGRAPalette.Create(AColors: ArrayOfTBGRAPixel); 1146 var 1147 i: Integer; 1148 begin 1149 inherited Create; 1150 for i := 0 to high(AColors) do 1151 AddColor(AColors[i]); 1152 end; 1153 1154 constructor TBGRAPalette.Create(AColors: ArrayOfWeightedColor); 1155 var 1156 i: Integer; 1157 begin 1158 inherited Create; 1159 for i := 0 to high(AColors) do 1160 AddColor(AColors[i].Color); 1161 end; 1162 647 1163 function TBGRAPalette.AddColor(AValue: TBGRAPixel): boolean; 648 1164 Var Node: TAvgLvlTreeNode; … … 668 1184 AddLastColor(Entry); 669 1185 end; 1186 end; 1187 1188 procedure TBGRAPalette.AddColors(ABitmap: TBGRACustomBitmap); 1189 var p: PBGRAPixel; 1190 n: integer; 1191 begin 1192 n := ABitmap.NbPixels; 1193 p := ABitmap.Data; 1194 while n > 0 do 1195 begin 1196 AddColor(p^); 1197 inc(p); 1198 dec(n); 1199 end; 1200 end; 1201 1202 procedure TBGRAPalette.AddColors(APalette: TBGRACustomPalette); 1203 var i: NativeInt; 1204 begin 1205 for i := 0 to APalette.Count- 1 do 1206 AddColor(APalette.Color[i]); 670 1207 end; 671 1208 -
GraphicTest/Packages/bgrabitmap/bgrapath.pas
r472 r494 4 4 5 5 interface 6 7 //todo: tangent interpolation 6 8 7 9 { There are different conventions for angles. … … 39 41 40 42 type 41 TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, peQuadraticBezierTo, peCubicBezierTo, peArc); 42 PBGRAPathElementType = ^TBGRAPathElementType; 43 TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, 44 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, 45 peClosedSpline); 46 47 TBGRAPathDrawProc = procedure(const APoints: array of TPointF; AClosed: boolean; AData: Pointer) of object; 48 TBGRAPathFillProc = procedure(const APoints: array of TPointF; AData: pointer) of object; 49 50 TBGRAPath = class; 51 52 { TBGRAPathCursor } 53 54 TBGRAPathCursor = class(TBGRACustomPathCursor) 55 protected 56 FPath: TBGRAPath; 57 FDataPos: IntPtr; 58 FAcceptedDeviation: single; 59 FPathLength: single; 60 FPathLengthComputed: boolean; 61 FBounds: TRectF; 62 FBoundsComputed: boolean; 63 FArcPos: Single; 64 65 FStartCoordinate: TPointF; 66 FEndCoordinate: TPointF; 67 FLoopClosedShapes,FLoopPath: boolean; 68 69 FCurrentElementType: TBGRAPathElementType; 70 FCurrentElement: Pointer; 71 FCurrentElementArcPos, 72 FCurrentElementArcPosScale: single; 73 FCurrentElementStartCoord, 74 FCurrentElementEndCoord: TPointF; 75 FCurrentElementLength: single; 76 FCurrentElementPoints: array of TPointF; 77 FCurrentSegment: NativeInt; 78 FCurrentSegmentPos: single; 79 function GoToNextElement(ACanJump: boolean): boolean; 80 function GoToPreviousElement(ACanJump: boolean): boolean; 81 procedure MoveToEndOfElement; 82 procedure MoveForwardInElement(ADistance: single); 83 procedure MoveBackwardInElement(ADistance: single); 84 function NeedPolygonalApprox: boolean; 85 procedure OnPathFree; virtual; 86 87 function GetLoopClosedShapes: boolean; override; 88 function GetLoopPath: boolean; override; 89 function GetStartCoordinate: TPointF; override; 90 procedure SetLoopClosedShapes(AValue: boolean); override; 91 procedure SetLoopPath(AValue: boolean); override; 92 93 function GetArcPos: single; override; 94 function GetCurrentTangent: TPointF; override; 95 procedure SetArcPos(AValue: single); override; 96 function GetBounds: TRectF; override; 97 function GetPathLength: single; override; 98 procedure PrepareCurrentElement; virtual; 99 function GetCurrentCoord: TPointF; override; 100 function GetPath: TBGRAPath; virtual; 101 public 102 constructor Create(APath: TBGRAPath; AAcceptedDeviation: single = 0.1); 103 function MoveForward(ADistance: single; ACanJump: boolean = true): single; override; 104 function MoveBackward(ADistance: single; ACanJump: boolean = true): single; override; 105 destructor Destroy; override; 106 property CurrentCoordinate: TPointF read GetCurrentCoord; 107 property CurrentTangent: TPointF read GetCurrentTangent; 108 property Position: single read GetArcPos write SetArcPos; 109 property PathLength: single read GetPathLength; 110 property Path: TBGRAPath read GetPath; 111 property Bounds: TRectF read GetBounds; 112 property StartCoordinate: TPointF read GetStartCoordinate; 113 property LoopClosedShapes: boolean read GetLoopClosedShapes write SetLoopClosedShapes; 114 property LoopPath: boolean read GetLoopPath write SetLoopPath; 115 property AcceptedDeviation: single read FAcceptedDeviation; 116 end; 43 117 44 118 { TBGRAPath } 45 119 46 120 TBGRAPath = class(IBGRAPath) 47 private48 function GetSvgString: string;49 procedure SetSvgString(const AValue: string);50 121 protected 51 FData: pbyte; 52 FDataSize: integer; 53 FDataPos: integer; 54 FLastElementType: TBGRAPathElementType; 55 FLastCoord, 56 FStartCoord: TPointF; 57 FExpectedControlPoint: TPointF; 122 FData: PByte; 123 FDataCapacity: PtrInt; 124 FDataPos: PtrInt; 125 FLastSubPathElementType, FLastStoredElementType: TBGRAPathElementType; 126 FLastMoveToDataPos: PtrInt; 127 FLastCoord,FLastTransformedCoord, 128 FSubPathStartCoord, FSubPathTransformedStartCoord: TPointF; 129 FExpectedTransformedControlPoint: TPointF; 58 130 FMatrix: TAffineMatrix; //this matrix must have a base of vectors 59 131 //orthogonal, of same length and with positive 60 132 //orientation in order to preserve arcs 61 133 FScale,FAngleRadCW: single; 134 FCursors: array of TBGRAPathCursor; 135 FInternalDrawOffset: TPointF; 136 procedure OnModify; 137 procedure OnMatrixChange; 62 138 procedure NeedSpace(count: integer); 63 procedure StoreCoord(const pt: TPointF); 64 function ReadCoord: TPointF; 65 procedure StoreElementType(value: TBGRAPathElementType); 66 function ReadElementType: TBGRAPathElementType; 67 function ReadArcDef: TArcDef; 68 procedure RewindFloat; 139 function AllocateElement(AElementType: TBGRAPathElementType; 140 AExtraBytes: PtrInt = 0): Pointer; 69 141 procedure Init; 142 procedure DoClear; 143 function CheckElementType(AElementType: TBGRAPathElementType): boolean; 144 function GoToNextElement(var APos: PtrInt): boolean; 145 function GoToPreviousElement(var APos: PtrInt): boolean; 146 function PeekNextElement(APos: PtrInt): TBGRAPathElementType; 147 function GetElementStartCoord(APos: PtrInt): TPointF; 148 function GetElementEndCoord(APos: PtrInt): TPointF; 149 function GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single; 150 procedure GetElementAt(APos: PtrInt; 151 out AElementType: TBGRAPathElementType; out AElement: pointer); 152 function GetSvgString: string; virtual; 153 procedure SetSvgString(const AValue: string); virtual; 154 procedure RegisterCursor(ACursor: TBGRAPathCursor); 155 procedure UnregisterCursor(ACursor: TBGRAPathCursor); 156 function SetLastCoord(ACoord: TPointF): TPointF; inline; 157 procedure ClearLastCoord; 158 procedure BezierCurveFromTransformed(tcp1, cp2, pt:TPointF); 159 procedure QuadraticCurveFromTransformed(tcp, pt: TPointF); 160 function LastCoordDefined: boolean; inline; 161 function GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF; 162 function getPoints: ArrayOfTPointF; 163 function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; 164 function getCursor: TBGRACustomPathCursor; 165 procedure InternalDraw(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 166 procedure BitmapDrawSubPathProc(const APoints: array of TPointF; AClosed: boolean; AData: pointer); 167 function CorrectAcceptedDeviation(AAcceptedDeviation: single; const AMatrix: TAffineMatrix): single; 70 168 public 71 169 constructor Create; overload; 72 170 constructor Create(ASvgString: string); overload; 171 constructor Create(const APoints: ArrayOfTPointF); overload; 172 constructor Create(APath: IBGRAPath); overload; 73 173 destructor Destroy; override; 74 174 procedure beginPath; 175 procedure beginSubPath; 75 176 procedure closePath; 76 177 procedure translate(x,y: single); … … 85 186 procedure moveTo(const pt: TPointF); overload; 86 187 procedure lineTo(const pt: TPointF); overload; 188 procedure polyline(const pts: array of TPointF); 87 189 procedure polylineTo(const pts: array of TPointF); 190 procedure polygon(const pts: array of TPointF); 88 191 procedure quadraticCurveTo(cpx,cpy,x,y: single); overload; 89 192 procedure quadraticCurveTo(const cp,pt: TPointF); overload; 90 193 procedure quadraticCurve(const curve: TQuadraticBezierCurve); overload; 194 procedure quadraticCurve(p1,cp,p2: TPointF); overload; 91 195 procedure smoothQuadraticCurveTo(x,y: single); overload; 92 196 procedure smoothQuadraticCurveTo(const pt: TPointF); overload; … … 94 198 procedure bezierCurveTo(const cp1,cp2,pt: TPointF); overload; 95 199 procedure bezierCurve(const curve: TCubicBezierCurve); overload; 200 procedure bezierCurve(p1,cp1,cp2,p2: TPointF); overload; 96 201 procedure smoothBezierCurveTo(cp2x,cp2y,x,y: single); overload; 97 202 procedure smoothBezierCurveTo(const cp2,pt: TPointF); overload; … … 105 210 procedure arcTo(const p1,p2: TPointF; radius: single); overload; 106 211 procedure arc(const arcDef: TArcDef); overload; 107 procedure arc(cx, cy, rx,ry ,xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;212 procedure arc(cx, cy, rx,ry: single; xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload; 108 213 procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload; 109 214 procedure arcTo(rx,ry, xAngleRadCW: single; largeArc, anticlockwise: boolean; x,y:single); … … 111 216 procedure addPath(const AValue: string); overload; 112 217 procedure addPath(source: IBGRAPath); overload; 218 procedure openedSpline(const pts: array of TPointF; style: TSplineStyle); 219 procedure closedSpline(const pts: array of TPointF; style: TSplineStyle); 113 220 property SvgString: string read GetSvgString write SetSvgString; 221 function ComputeLength(AAcceptedDeviation: single = 0.1): single; 222 function ToPoints(AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 223 function ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 224 function IsEmpty: boolean; 225 function GetBounds(AAcceptedDeviation: single = 0.1): TRectF; 226 procedure SetPoints(const APoints: ArrayOfTPointF); 227 procedure stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 228 procedure stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 229 procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 230 procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 231 procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1); 232 procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1); 233 procedure stroke(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil); 234 procedure fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 235 procedure fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 236 procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 237 procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 238 procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1); 239 procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1); 240 procedure fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil); 241 function CreateCursor(AAcceptedDeviation: single = 0.1): TBGRAPathCursor; 242 procedure Fit(ARect: TRectF; AAcceptedDeviation: single = 0.1); 243 procedure FitInto(ADest: TBGRAPath; ARect: TRectF; AAcceptedDeviation: single = 0.1); 114 244 protected 115 245 function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND}; … … 121 251 122 252 function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single; 123 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload; 124 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload; 125 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; overload; 126 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; overload; 127 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle): ArrayOfTPointF; 128 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25): ArrayOfTPointF; 253 function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 254 function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 255 function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 256 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 257 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 258 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 259 function ClosedSplineStartPoint(const points: array of TPointF; Style: TSplineStyle): TPointF; 129 260 130 261 { Compute points to draw an antialiased ellipse } … … 147 278 uses Math, BGRAResample, SysUtils; 148 279 280 type 281 TStrokeData = record 282 Bitmap: TBGRACustomBitmap; 283 Texture: IBGRAScanner; 284 Color: TBGRAPixel; 285 Width: Single; 286 end; 287 288 PPathElementHeader = ^TPathElementHeader; 289 TPathElementHeader = record 290 ElementType: TBGRAPathElementType; 291 PreviousElementType: TBGRAPathElementType; 292 end; 293 PMoveToElement = ^TMoveToElement; 294 TMoveToElement = record 295 StartCoordinate: TPointF; 296 LoopDataPos: PtrInt; //if the path is closed 297 end; 298 PClosePathElement = ^TClosePathElement; 299 TClosePathElement = type TMoveToElement; 300 PQuadraticBezierToElement = ^TQuadraticBezierToElement; 301 TQuadraticBezierToElement = record 302 ControlPoint, Destination: TPointF; 303 end; 304 PCubicBezierToElement = ^TCubicBezierToElement; 305 TCubicBezierToElement = record 306 ControlPoint1, ControlPoint2, Destination: TPointF; 307 end; 308 PArcElement = ^TArcElement; 309 TArcElement = TArcDef; 310 311 PSplineElement = ^TSplineElement; 312 TSplineElement = record 313 SplineStyle: TSplineStyle; 314 NbControlPoints: integer; 315 end; 316 317 const 318 PathElementSize : array[TBGRAPathElementType] of PtrInt = 319 (0, Sizeof(TMoveToElement), Sizeof(TClosePathElement), sizeof(TPointF), 320 sizeof(TQuadraticBezierToElement), sizeof(TCubicBezierToElement), 321 sizeof(TArcElement), sizeof(TSplineElement)+sizeof(integer), 322 sizeof(TSplineElement)+sizeof(integer)); 323 149 324 function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single; 150 325 var … … 160 335 end; 161 336 162 function ComputeCurveP recision(pt1, pt2, pt3, pt4: TPointF): integer;337 function ComputeCurvePartPrecision(pt1, pt2, pt3, pt4: TPointF; AAcceptedDeviation: single = 0.1): integer; 163 338 var 164 339 len: single; … … 167 342 len := max(len, sqr(pt3.x - pt2.x) + sqr(pt3.y - pt2.y)); 168 343 len := max(len, sqr(pt3.x - pt4.x) + sqr(pt3.y - pt4.y)); 169 Result := round(sqrt(sqrt(len) ) * 2);344 Result := round(sqrt(sqrt(len)/AAcceptedDeviation) * 0.9); 170 345 if Result<=0 then Result:=1; 171 346 end; 172 347 173 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload; 174 var 175 t,f1,f2,f3,f4: single; 176 i,nb: Integer; 177 begin 178 nb := ComputeCurvePrecision(curve.p1,curve.c1,curve.c2,curve.p2); 179 if nb <= 1 then nb := 2; 180 setlength(result,nb); 181 result[0] := curve.p1; 182 result[nb-1] := curve.p2; 183 for i := 1 to nb-2 do 184 begin 185 t := i/(nb-1); 186 f1 := (1-t); 187 f2 := f1*f1; 188 f1 *= f2; 189 f2 *= t*3; 190 f4 := t*t; 191 f3 := f4*(1-t)*3; 192 f4 *= t; 193 194 result[i] := PointF(f1*curve.p1.x + f2*curve.c1.x + 195 f3*curve.c2.x + f4*curve.p2.x, 196 f1*curve.p1.y + f2*curve.c1.y + 197 f3*curve.c2.y + f4*curve.p2.y); 198 end; 199 end; 200 201 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload; 202 var 203 t,f1,f2,f3: single; 204 i,nb: Integer; 205 begin 206 nb := ComputeCurvePrecision(curve.p1,curve.c,curve.c,curve.p2); 207 if nb <= 1 then nb := 2; 208 setlength(result,nb); 209 result[0] := curve.p1; 210 result[nb-1] := curve.p2; 211 for i := 1 to nb-2 do 212 begin 213 t := i/(nb-1); 214 f1 := (1-t); 215 f3 := t; 216 f2 := f1*f3*2; 217 f1 *= f1; 218 f3 *= f3; 219 result[i] := PointF(f1*curve.p1.x + f2*curve.c.x + f3*curve.p2.x, 220 f1*curve.p1.y + f2*curve.c.y + f3*curve.p2.y); 221 end; 222 end; 223 224 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; 348 function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 349 begin 350 result := curve.ToPoints(AAcceptedDeviation); 351 end; 352 353 function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload; 354 begin 355 result := curve.ToPoints(AAcceptedDeviation); 356 end; 357 358 function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 225 359 var 226 360 curves: array of array of TPointF; … … 250 384 setlength(curves, length(spline)); 251 385 for i := 0 to high(spline) do 252 curves[i] := ComputeBezierCurve(spline[i] );386 curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation); 253 387 nb := length(curves[0]); 254 388 lastPt := curves[0][high(curves[0])]; … … 271 405 end; 272 406 273 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve 274 ): ArrayOfTPointF;407 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve; 408 AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 275 409 var 276 410 curves: array of array of TPointF; … … 300 434 setlength(curves, length(spline)); 301 435 for i := 0 to high(spline) do 302 curves[i] := ComputeBezierCurve(spline[i] );436 curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation); 303 437 nb := length(curves[0]); 304 438 lastPt := curves[0][high(curves[0])]; … … 321 455 end; 322 456 323 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle ): ArrayOfTPointF;457 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 324 458 var 325 459 i, j, nb, idx, pre: integer; … … 344 478 ptNext := points[(i + 1) mod length(points)]; 345 479 ptNext2 := points[(i + 2) mod length(points)]; 346 nb += ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);480 nb += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 347 481 end; 348 482 349 483 kernel := CreateInterpolator(style); 350 484 setlength(Result, nb); 485 idx := 0; 351 486 for i := 0 to high(points) do 352 487 begin … … 355 490 ptNext := points[(i + 1) mod length(points)]; 356 491 ptNext2 := points[(i + 2) mod length(points)]; 357 pre := ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);492 pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 358 493 if i=0 then 359 begin 360 j := 0; 361 idx := 0; 362 end else j := 1; 494 j := 0 495 else 496 j := 1; 363 497 while j <= pre do 364 498 begin … … 373 507 end; 374 508 375 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single ): ArrayOfTPointF;509 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; 376 510 var 377 511 i, j, nb, idx, pre: integer; … … 403 537 else 404 538 ptNext2 := points[i + 2]; 405 nb += ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);539 nb += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 406 540 end; 407 541 … … 430 564 else 431 565 ptNext2 := points[i + 2]; 432 pre := ComputeCurveP recision(ptPrev2, ptPrev, ptNext, ptNext2);566 pre := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation); 433 567 if i=0 then 434 568 begin … … 447 581 if Style in[ssInsideWithEnds,ssCrossingWithEnds] then 448 582 result[idx] := points[high(points)]; 583 end; 584 585 function ClosedSplineStartPoint(const points: array of TPointF; 586 Style: TSplineStyle): TPointF; 587 var 588 kernel: TWideKernelFilter; 589 ptPrev2: TPointF; 590 ptPrev: TPointF; 591 ptNext: TPointF; 592 ptNext2: TPointF; 593 begin 594 if length(points) = 0 then 595 result := EmptyPointF 596 else 597 if length(points)<=2 then 598 result := points[0] 599 else 600 begin 601 kernel := CreateInterpolator(style); 602 ptPrev2 := points[high(points)]; 603 ptPrev := points[0]; 604 ptNext := points[1]; 605 ptNext2 := points[2]; 606 result := ptPrev2*kernel.Interpolation(1) + ptPrev*kernel.Interpolation(0) + 607 ptNext*kernel.Interpolation(-1) + ptNext2*kernel.Interpolation(-2); 608 kernel.free; 609 end; 449 610 end; 450 611 … … 707 868 end; 708 869 870 { TBGRAPathCursor } 871 872 function TBGRAPathCursor.GetCurrentCoord: TPointF; 873 begin 874 case FCurrentElementType of 875 peNone: result := EmptyPointF; 876 peMoveTo,peLineTo,peCloseSubPath: 877 if FCurrentElementLength <= 0 then 878 result := FCurrentElementStartCoord 879 else 880 result := FCurrentElementStartCoord + (FCurrentElementEndCoord-FCurrentElementStartCoord)*(FCurrentElementArcPos/FCurrentElementLength); 881 peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline: 882 begin 883 NeedPolygonalApprox; 884 if FCurrentSegment >= high(FCurrentElementPoints) then 885 result := FCurrentElementEndCoord 886 else 887 result := FCurrentElementPoints[FCurrentSegment]+ 888 (FCurrentElementPoints[FCurrentSegment+1]- 889 FCurrentElementPoints[FCurrentSegment])*FCurrentSegmentPos; 890 end; 891 else 892 raise Exception.Create('Unknown element type'); 893 end; 894 end; 895 896 function TBGRAPathCursor.GetPath: TBGRAPath; 897 begin 898 if not Assigned(FPath) then 899 raise exception.Create('Path does not exist'); 900 result := FPath; 901 end; 902 903 procedure TBGRAPathCursor.MoveToEndOfElement; 904 begin 905 FCurrentElementArcPos := FCurrentElementLength; 906 if not NeedPolygonalApprox then exit; 907 if length(FCurrentElementPoints) > 1 then 908 begin 909 FCurrentSegment := high(FCurrentElementPoints)-1; 910 FCurrentSegmentPos := 1; 911 end else 912 begin 913 FCurrentSegment := high(FCurrentElementPoints); 914 FCurrentSegmentPos := 0; 915 end; 916 end; 917 918 procedure TBGRAPathCursor.MoveForwardInElement(ADistance: single); 919 var segLen,rightSpace,remaining: single; 920 begin 921 if not NeedPolygonalApprox then exit; 922 ADistance *= FCurrentElementArcPosScale; 923 remaining := ADistance; 924 while remaining > 0 do 925 begin 926 if FCurrentSegment < high(FCurrentElementPoints) then 927 segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment]) 928 else 929 segLen := 0; 930 rightSpace := segLen*(1-FCurrentSegmentPos); 931 if (segLen > 0) and (remaining <= rightSpace) then 932 begin 933 FCurrentSegmentPos += remaining/segLen; 934 exit; 935 end else 936 begin 937 remaining -= rightSpace; 938 if FCurrentSegment < high(FCurrentElementPoints)-1 then 939 begin 940 inc(FCurrentSegment); 941 FCurrentSegmentPos := 0; 942 end else 943 begin 944 FCurrentSegmentPos := 1; 945 exit; 946 end; 947 end; 948 end; 949 end; 950 951 procedure TBGRAPathCursor.MoveBackwardInElement(ADistance: single); 952 var 953 segLen,leftSpace,remaining: Single; 954 begin 955 if not NeedPolygonalApprox then exit; 956 ADistance *= FCurrentElementArcPosScale; 957 remaining := ADistance; 958 while remaining > 0 do 959 begin 960 if FCurrentSegment < high(FCurrentElementPoints) then 961 segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment]) 962 else 963 segLen := 0; 964 leftSpace := segLen*FCurrentSegmentPos; 965 if (segLen > 0) and (remaining <= leftSpace) then 966 begin 967 FCurrentSegmentPos -= remaining/segLen; 968 exit; 969 end else 970 begin 971 remaining -= leftSpace; 972 if FCurrentSegment > 0 then 973 begin 974 dec(FCurrentSegment); 975 FCurrentSegmentPos := 1; 976 end else 977 begin 978 FCurrentSegmentPos := 0; 979 exit; 980 end; 981 end; 982 end; 983 end; 984 985 function TBGRAPathCursor.NeedPolygonalApprox: boolean; 986 begin 987 if not (FCurrentElementType in[peQuadraticBezierTo,peCubicBezierTo,peArc, 988 peOpenedSpline,peClosedSpline]) 989 then 990 begin 991 result := false; 992 exit; 993 end; 994 result := true; 995 if FCurrentElementPoints = nil then 996 begin 997 FCurrentElementPoints := Path.GetPolygonalApprox(FDataPos, FAcceptedDeviation, True); 998 if FCurrentElementType = peQuadraticBezierTo then 999 begin 1000 if FCurrentElementLength <> 0 then 1001 FCurrentElementArcPosScale := PolylineLen(FCurrentElementPoints)/FCurrentElementLength; 1002 end; 1003 end; 1004 end; 1005 1006 function TBGRAPathCursor.GetArcPos: single; 1007 var pos: PtrInt; 1008 begin 1009 if FArcPos = EmptySingle then 1010 begin 1011 FArcPos := FCurrentElementArcPos; 1012 pos := FDataPos; 1013 while Path.GoToPreviousElement(pos) do 1014 FArcPos += Path.GetElementLength(pos, FAcceptedDeviation); 1015 end; 1016 result := FArcPos; 1017 end; 1018 1019 function TBGRAPathCursor.GetCurrentTangent: TPointF; 1020 var idxStart,idxEnd: integer; 1021 seg: TPointF; 1022 begin 1023 while FCurrentElementLength <= 0 do 1024 begin 1025 if not GoToNextElement(False) then 1026 begin 1027 result := EmptyPointF; 1028 exit; 1029 end; 1030 end; 1031 case FCurrentElementType of 1032 peMoveTo,peLineTo,peCloseSubPath: 1033 result := (FCurrentElementEndCoord-FCurrentElementStartCoord)*(1/FCurrentElementLength); 1034 peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline: 1035 begin 1036 NeedPolygonalApprox; 1037 idxStart := FCurrentSegment; 1038 if idxStart >= high(FCurrentElementPoints) then 1039 idxStart:= high(FCurrentElementPoints)-1; 1040 idxEnd := idxStart+1; 1041 if idxStart < 0 then 1042 begin 1043 result := EmptyPointF; 1044 exit; 1045 end; 1046 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1047 while (seg.x = 0) and (seg.y = 0) and (idxEnd < high(FCurrentElementPoints)) do 1048 begin 1049 inc(idxEnd); 1050 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1051 end; 1052 while (seg.x = 0) and (seg.y = 0) and (idxStart > 0) do 1053 begin 1054 dec(idxStart); 1055 seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart]; 1056 end; 1057 if (seg.x = 0) and (seg.y = 0) then 1058 result := EmptyPointF 1059 else 1060 result := seg*(1/VectLen(seg)); 1061 end; 1062 else result := EmptyPointF; 1063 end; 1064 end; 1065 1066 procedure TBGRAPathCursor.SetArcPos(AValue: single); 1067 var oldLoopClosedShapes,oldLoopPath: boolean; 1068 begin 1069 if GetArcPos=AValue then Exit; 1070 if (AValue > PathLength) and (PathLength <> 0) then 1071 AValue := AValue - trunc(AValue/PathLength)*PathLength 1072 else if (AValue < 0) then 1073 AValue := AValue + (trunc(-AValue/PathLength)+1)*PathLength; 1074 oldLoopClosedShapes:= LoopClosedShapes; 1075 oldLoopPath:= LoopPath; 1076 LoopClosedShapes:= false; 1077 LoopPath:= false; 1078 MoveForward(AValue-GetArcPos, True); 1079 LoopClosedShapes:= oldLoopClosedShapes; 1080 LoopPath:= oldLoopPath; 1081 end; 1082 1083 function TBGRAPathCursor.GetPathLength: single; 1084 begin 1085 if not FPathLengthComputed then 1086 begin 1087 FPathLength := Path.ComputeLength(FAcceptedDeviation); 1088 FPathLengthComputed := true; 1089 end; 1090 result := FPathLength; 1091 end; 1092 1093 procedure TBGRAPathCursor.OnPathFree; 1094 begin 1095 FPath := nil; 1096 end; 1097 1098 function TBGRAPathCursor.GetLoopClosedShapes: boolean; 1099 begin 1100 result := FLoopClosedShapes; 1101 end; 1102 1103 function TBGRAPathCursor.GetLoopPath: boolean; 1104 begin 1105 result := FLoopPath; 1106 end; 1107 1108 function TBGRAPathCursor.GetStartCoordinate: TPointF; 1109 begin 1110 result := FStartCoordinate; 1111 end; 1112 1113 procedure TBGRAPathCursor.SetLoopClosedShapes(AValue: boolean); 1114 begin 1115 FLoopClosedShapes := AValue; 1116 end; 1117 1118 procedure TBGRAPathCursor.SetLoopPath(AValue: boolean); 1119 begin 1120 FLoopPath := AValue; 1121 end; 1122 1123 procedure TBGRAPathCursor.PrepareCurrentElement; 1124 begin 1125 Path.GetElementAt(FDataPos, FCurrentElementType, FCurrentElement); 1126 FCurrentElementLength := 0; 1127 FCurrentElementArcPos := 0; 1128 FCurrentElementPoints := nil; 1129 FCurrentSegment := 0; 1130 FCurrentSegmentPos := 0; 1131 FCurrentElementArcPosScale := 1; 1132 if FCurrentElementType = peNone then 1133 begin 1134 FCurrentElementStartCoord := EmptyPointF; 1135 FCurrentElementEndCoord := EmptyPointF; 1136 end 1137 else 1138 begin 1139 FCurrentElementStartCoord := Path.GetElementStartCoord(FDataPos); 1140 case FCurrentElementType of 1141 peLineTo, peCloseSubPath: 1142 begin 1143 FCurrentElementEndCoord := PPointF(FCurrentElement)^; 1144 FCurrentElementLength := VectLen(FCurrentElementEndCoord - FCurrentElementStartCoord); 1145 end; 1146 peQuadraticBezierTo: with PQuadraticBezierToElement(FCurrentElement)^ do 1147 begin 1148 FCurrentElementEndCoord := Destination; 1149 FCurrentElementLength := BGRABitmapTypes.BezierCurve(FCurrentElementStartCoord,ControlPoint,Destination).ComputeLength; 1150 end; 1151 peCubicBezierTo,peArc,peOpenedSpline,peClosedSpline: 1152 begin 1153 NeedPolygonalApprox; 1154 FCurrentElementEndCoord := FCurrentElementPoints[high(FCurrentElementPoints)]; 1155 FCurrentElementLength := PolylineLen(FCurrentElementPoints); 1156 end; 1157 else 1158 FCurrentElementEndCoord := FCurrentElementStartCoord; 1159 end; 1160 end; 1161 end; 1162 1163 function TBGRAPathCursor.GetBounds: TRectF; 1164 begin 1165 if not FBoundsComputed then 1166 begin 1167 FBounds:= Path.GetBounds(FAcceptedDeviation); 1168 FBoundsComputed := true; 1169 end; 1170 result := FBounds; 1171 end; 1172 1173 function TBGRAPathCursor.GoToNextElement(ACanJump: boolean): boolean; 1174 begin 1175 if (FCurrentElementType = peCloseSubPath) and 1176 (PClosePathElement(FCurrentElement)^.LoopDataPos <> -1) and 1177 ( FLoopClosedShapes or 1178 (FLoopPath and (PClosePathElement(FCurrentElement)^.LoopDataPos = 0)) 1179 ) then 1180 begin 1181 if PClosePathElement(FCurrentElement)^.LoopDataPos <> FDataPos then 1182 begin 1183 result := true; 1184 FDataPos := PClosePathElement(FCurrentElement)^.LoopDataPos; 1185 FArcPos := EmptySingle; 1186 PrepareCurrentElement; 1187 end else 1188 result := false; 1189 end; 1190 if not ACanJump and ((FCurrentElementType = peCloseSubPath) 1191 or (Path.PeekNextElement(FDataPos) = peMoveTo)) then 1192 begin 1193 result := false; 1194 exit; 1195 end; 1196 if Path.GoToNextElement(FDataPos) then 1197 begin 1198 result := true; 1199 PrepareCurrentElement; 1200 end 1201 else 1202 begin 1203 if ACanJump and FLoopPath and (FDataPos > 0) then 1204 begin 1205 result := true; 1206 FDataPos := 0; 1207 FArcPos := EmptySingle; 1208 PrepareCurrentElement; 1209 end else 1210 result := false; 1211 end; 1212 end; 1213 1214 function TBGRAPathCursor.GoToPreviousElement(ACanJump: boolean): boolean; 1215 var lastElemPos: IntPtr; 1216 begin 1217 if (FCurrentElementType = peMoveTo) and (PMoveToElement(FCurrentElement)^.LoopDataPos <> -1) and 1218 ( FLoopClosedShapes or 1219 (FLoopPath and (FDataPos = 0)) 1220 ) then 1221 with PMoveToElement(FCurrentElement)^ do 1222 begin 1223 if LoopDataPos <> -1 then 1224 begin 1225 result := true; 1226 FDataPos := LoopDataPos; 1227 FArcPos := EmptySingle; 1228 PrepareCurrentElement; 1229 end; 1230 end; 1231 if not ACanJump and (FCurrentElementType = peMoveTo) then 1232 begin 1233 result := false; 1234 exit; 1235 end; 1236 if Path.GoToPreviousElement(FDataPos) then 1237 begin 1238 result := true; 1239 PrepareCurrentElement; 1240 end 1241 else 1242 begin 1243 if FLoopPath then 1244 begin 1245 lastElemPos := FPath.FDataPos; 1246 if (lastElemPos > 0) and FPath.GoToPreviousElement(lastElemPos) then 1247 begin 1248 if lastElemPos > 0 then 1249 begin 1250 result := true; 1251 FDataPos := lastElemPos; 1252 PrepareCurrentElement; 1253 FArcPos := EmptySingle; 1254 exit; 1255 end; 1256 end; 1257 end; 1258 result := false; 1259 end; 1260 end; 1261 1262 constructor TBGRAPathCursor.Create(APath: TBGRAPath; AAcceptedDeviation: single); 1263 begin 1264 FPath := APath; 1265 FPathLengthComputed := false; 1266 FBoundsComputed:= false; 1267 FDataPos := 0; 1268 FArcPos:= 0; 1269 FAcceptedDeviation:= AAcceptedDeviation; 1270 Path.RegisterCursor(self); 1271 PrepareCurrentElement; 1272 1273 FStartCoordinate := FCurrentElementStartCoord; 1274 if isEmptyPointF(FStartCoordinate) then 1275 raise exception.Create('Path does not has a starting coordinate'); 1276 FEndCoordinate := Path.FLastTransformedCoord; 1277 if isEmptyPointF(FEndCoordinate) then 1278 raise exception.Create('Path does not has an ending coordinate'); 1279 end; 1280 1281 function TBGRAPathCursor.MoveForward(ADistance: single; ACanJump: boolean): single; 1282 var newArcPos,step,remaining: single; 1283 begin 1284 if ADistance < 0 then 1285 begin 1286 result := -MoveBackward(-ADistance, ACanJump); 1287 exit; 1288 end; 1289 result := 0; 1290 remaining := ADistance; 1291 while remaining > 0 do 1292 begin 1293 newArcPos := FCurrentElementArcPos + remaining; 1294 if newArcPos > FCurrentElementLength then 1295 begin 1296 step := FCurrentElementLength - FCurrentElementArcPos; 1297 result += step; 1298 remaining -= step; 1299 if not GoToNextElement(ACanJump) then 1300 begin 1301 MoveForwardInElement(step); 1302 FCurrentElementArcPos := FCurrentElementLength; 1303 FArcPos := PathLength; 1304 exit; 1305 end; 1306 end else 1307 begin 1308 MoveForwardInElement(remaining); 1309 FCurrentElementArcPos := newArcPos; 1310 result := ADistance; 1311 break; 1312 end; 1313 end; 1314 if FArcPos <> EmptySingle then 1315 FArcPos += result; 1316 end; 1317 1318 function TBGRAPathCursor.MoveBackward(ADistance: single; ACanJump: boolean = true): single; 1319 var 1320 remaining: Single; 1321 newArcPos: Single; 1322 step: Single; 1323 begin 1324 if ADistance = 0 then 1325 begin 1326 result := 0; 1327 exit; 1328 end; 1329 if ADistance < 0 then 1330 begin 1331 result := -MoveForward(-ADistance, ACanJump); 1332 exit; 1333 end; 1334 result := 0; 1335 remaining := ADistance; 1336 while remaining > 0 do 1337 begin 1338 newArcPos := FCurrentElementArcPos - remaining; 1339 if newArcPos < 0 then 1340 begin 1341 step := FCurrentElementArcPos; 1342 result += step; 1343 remaining -= step; 1344 if not GoToPreviousElement(ACanJump) then 1345 begin 1346 MoveBackwardInElement(step); 1347 FCurrentElementArcPos := 0; 1348 FArcPos := 0; 1349 exit; 1350 end else 1351 MoveToEndOfElement; 1352 end else 1353 begin 1354 MoveBackwardInElement(remaining); 1355 FCurrentElementArcPos := newArcPos; 1356 result := ADistance; 1357 break; 1358 end; 1359 end; 1360 if FArcPos <> EmptySingle then 1361 FArcPos -= result; 1362 end; 1363 1364 destructor TBGRAPathCursor.Destroy; 1365 begin 1366 if Assigned(FPath) then 1367 begin 1368 FPath.UnregisterCursor(self); 1369 end; 1370 inherited Destroy; 1371 end; 1372 709 1373 { TBGRAPath } 1374 1375 function TBGRAPath.ComputeLength(AAcceptedDeviation: single): single; 1376 var pos: PtrInt; 1377 begin 1378 pos := 0; 1379 result := 0; 1380 repeat 1381 result += GetElementLength(pos, AAcceptedDeviation); 1382 until not GoToNextElement(pos); 1383 end; 1384 1385 function TBGRAPath.ToPoints(AAcceptedDeviation: single): ArrayOfTPointF; 1386 var sub: array of ArrayOfTPointF; 1387 temp: ArrayOfTPointF; 1388 nbSub,nbPts,curPt,curSub: NativeInt; 1389 startPos,pos: PtrInt; 1390 elemType: TBGRAPathElementType; 1391 elem: pointer; 1392 begin 1393 pos := 0; 1394 nbSub := 0; 1395 repeat 1396 GetElementAt(pos, elemType, elem); 1397 if elem = nil then break; 1398 case elemType of 1399 peMoveTo,peLineTo,peCloseSubPath: begin 1400 inc(nbSub); 1401 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1402 GoToNextElement(pos); 1403 end; 1404 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub); 1405 end; 1406 until not GoToNextElement(pos); 1407 1408 pos := 0; 1409 setlength(sub, nbSub); 1410 curSub := 0; 1411 repeat 1412 GetElementAt(pos, elemType, elem); 1413 if elem = nil then break; 1414 case elemType of 1415 peMoveTo,peLineTo,peCloseSubPath: begin 1416 startPos := pos; 1417 if (elemType = peMoveTo) and (curSub > 0) then 1418 nbPts := 2 1419 else 1420 nbPts := 1; 1421 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1422 begin 1423 GoToNextElement(pos); 1424 inc(nbPts); 1425 end; 1426 setlength(temp, nbPts); 1427 pos := startPos; 1428 if (elemType = peMoveTo) and (curSub > 0) then 1429 begin 1430 temp[0] := EmptyPointF; 1431 temp[1] := PPointF(elem)^; 1432 curPt := 2; 1433 end else 1434 begin 1435 temp[0] := PPointF(elem)^; 1436 curPt := 1; 1437 end; 1438 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1439 begin 1440 GoToNextElement(pos); 1441 GetElementAt(pos, elemType, elem); 1442 temp[curPt] := PPointF(elem)^; 1443 inc(curPt); 1444 end; 1445 sub[curSub] := temp; 1446 inc(curSub); 1447 temp := nil; 1448 end; 1449 peQuadraticBezierTo,peCubicBezierTo,peArc, 1450 peOpenedSpline, peClosedSpline: 1451 begin 1452 sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1453 inc(curSub); 1454 end; 1455 end; 1456 until not GoToNextElement(pos) or (curSub = nbSub); 1457 result := ConcatPointsF(sub); 1458 end; 1459 1460 function TBGRAPath.ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single): ArrayOfTPointF; 1461 begin 1462 AAcceptedDeviation:= CorrectAcceptedDeviation(AAcceptedDeviation,AMatrix); 1463 result := ToPoints(AAcceptedDeviation); 1464 if not IsAffineMatrixIdentity(AMatrix) then 1465 result := AMatrix*result; 1466 end; 1467 1468 function TBGRAPath.IsEmpty: boolean; 1469 begin 1470 result := FDataPos = 0; 1471 end; 1472 1473 function TBGRAPath.GetBounds(AAcceptedDeviation: single): TRectF; 1474 var empty: boolean; 1475 pos: PtrInt; 1476 elemType: TBGRAPathElementType; 1477 elem: pointer; 1478 temp: array of TPointF; 1479 i: integer; 1480 1481 procedure Include(pt: TPointF); 1482 begin 1483 if empty then 1484 begin 1485 result.TopLeft := pt; 1486 result.BottomRight := pt; 1487 empty := false; 1488 end else 1489 begin 1490 if pt.x < result.Left then result.Left := pt.x 1491 else if pt.x > result.Right then result.Right := pt.x; 1492 if pt.y < result.Top then result.Top := pt.y 1493 else if pt.y > result.Bottom then result.Bottom := pt.y; 1494 end; 1495 end; 1496 1497 procedure IncludeRect(r: TRectF); 1498 begin 1499 Include(r.TopLeft); 1500 Include(r.BottomRight); 1501 end; 1502 1503 begin 1504 empty := true; 1505 result := RectF(0,0,0,0); 1506 pos := 0; 1507 repeat 1508 GetElementAt(pos, elemType, elem); 1509 if elem = nil then break; 1510 case elemType of 1511 peMoveTo,peLineTo,peCloseSubPath: begin 1512 Include(PPointF(elem)^); 1513 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1514 begin 1515 GoToNextElement(pos); 1516 GetElementAt(pos, elemType, elem); 1517 Include(PPointF(elem)^); 1518 end; 1519 end; 1520 peCubicBezierTo: 1521 with PCubicBezierToElement(elem)^ do 1522 IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint1,ControlPoint2,Destination).GetBounds); 1523 peQuadraticBezierTo: 1524 with PQuadraticBezierToElement(elem)^ do 1525 IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint,Destination).GetBounds); 1526 peArc, peOpenedSpline, peClosedSpline: 1527 begin 1528 temp := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1529 for i := 0 to high(temp) do 1530 Include(temp[i]); 1531 end; 1532 end; 1533 until not GoToNextElement(pos); 1534 if empty then raise exception.Create('Path is empty'); 1535 end; 1536 1537 procedure TBGRAPath.SetPoints(const APoints: ArrayOfTPointF); 1538 var i: integer; 1539 nextIsMoveTo: boolean; 1540 startPoint: TPointF; 1541 begin 1542 beginPath; 1543 if length(APoints) = 0 then exit; 1544 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(APoints)); 1545 nextIsMoveTo:= true; 1546 startPoint := EmptyPointF; 1547 for i := 0 to high(APoints) do 1548 begin 1549 if isEmptyPointF(APoints[i]) then 1550 nextIsMoveTo:= true 1551 else 1552 if nextIsMoveTo then 1553 begin 1554 startPoint := APoints[i]; 1555 moveTo(startPoint); 1556 nextIsMoveTo:= false; 1557 end 1558 else 1559 begin 1560 with APoints[i] do 1561 if (x = startPoint.x) and (y = startPoint.y) then 1562 closePath 1563 else 1564 lineTo(APoints[i]); 1565 end; 1566 end; 1567 end; 1568 1569 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; 1570 AWidth: single; AAcceptedDeviation: single); 1571 begin 1572 stroke(ABitmap,AffineMatrixIdentity,AColor,AWidth,AAcceptedDeviation); 1573 end; 1574 1575 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; 1576 AWidth: single; AAcceptedDeviation: single); 1577 begin 1578 stroke(ABitmap,AffineMatrixIdentity,ATexture,AWidth,AAcceptedDeviation); 1579 end; 1580 1581 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single; 1582 AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single); 1583 begin 1584 stroke(ABitmap,AffineMatrixTranslation(x,y),AColor,AWidth,AAcceptedDeviation); 1585 end; 1586 1587 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single; 1588 ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single); 1589 begin 1590 stroke(ABitmap,AffineMatrixTranslation(x,y),ATexture,AWidth,AAcceptedDeviation); 1591 end; 1592 1593 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1594 AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single); 1595 var data: TStrokeData; 1596 begin 1597 data.Bitmap := ABitmap; 1598 data.Texture := nil; 1599 data.Color := AColor; 1600 data.Width := AWidth; 1601 InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data); 1602 end; 1603 1604 procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1605 ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single); 1606 var data: TStrokeData; 1607 begin 1608 data.Bitmap := ABitmap; 1609 data.Texture := ATexture; 1610 data.Color := BGRAPixelTransparent; 1611 data.Width := AWidth; 1612 InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data); 1613 end; 1614 1615 procedure TBGRAPath.stroke(ADrawProc: TBGRAPathDrawProc; 1616 const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 1617 begin 1618 InternalDraw(ADrawProc,AMatrix,AAcceptedDeviation,AData); 1619 end; 1620 1621 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; 1622 AAcceptedDeviation: single); 1623 begin 1624 fill(ABitmap,AffineMatrixIdentity,AColor,AAcceptedDeviation); 1625 end; 1626 1627 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; 1628 AAcceptedDeviation: single); 1629 begin 1630 fill(ABitmap,AffineMatrixIdentity,ATexture,AAcceptedDeviation); 1631 end; 1632 1633 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single; 1634 AColor: TBGRAPixel; AAcceptedDeviation: single); 1635 begin 1636 fill(ABitmap,AffineMatrixTranslation(x,y),AColor,AAcceptedDeviation); 1637 end; 1638 1639 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single; 1640 ATexture: IBGRAScanner; AAcceptedDeviation: single); 1641 begin 1642 fill(ABitmap,AffineMatrixTranslation(x,y),ATexture,AAcceptedDeviation); 1643 end; 1644 1645 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1646 AColor: TBGRAPixel; AAcceptedDeviation: single); 1647 begin 1648 ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), AColor); 1649 end; 1650 1651 procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; 1652 ATexture: IBGRAScanner; AAcceptedDeviation: single); 1653 begin 1654 ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), ATexture); 1655 end; 1656 1657 procedure TBGRAPath.fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix; 1658 AAcceptedDeviation: single; AData: pointer); 1659 begin 1660 AFillProc(ToPoints(AMatrix,AAcceptedDeviation), AData); 1661 end; 1662 1663 function TBGRAPath.CreateCursor(AAcceptedDeviation: single): TBGRAPathCursor; 1664 begin 1665 result := TBGRAPathCursor.Create(self, AAcceptedDeviation); 1666 end; 1667 1668 procedure TBGRAPath.Fit(ARect: TRectF; AAcceptedDeviation: single); 1669 var 1670 temp: TBGRAPath; 1671 begin 1672 temp := TBGRAPath.Create; 1673 copyTo(temp); 1674 temp.FitInto(self, ARect, AAcceptedDeviation); 1675 temp.Free; 1676 end; 1677 1678 procedure TBGRAPath.FitInto(ADest: TBGRAPath; ARect: TRectF; 1679 AAcceptedDeviation: single); 1680 var bounds: TRectF; 1681 zoomX,zoomY: single; 1682 begin 1683 bounds := GetBounds(AAcceptedDeviation); 1684 ADest.beginPath; 1685 ADest.translate((ARect.Left+ARect.Right)*0.5, (ARect.Bottom+ARect.Top)*0.5); 1686 if bounds.Right-bounds.Left <> 0 then 1687 begin 1688 zoomX := (ARect.Right-ARect.Left)/(bounds.Right-bounds.Left); 1689 if bounds.Bottom-bounds.Top > 0 then 1690 begin 1691 zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top); 1692 if zoomY < zoomX then ADest.scale(zoomY) else ADest.scale(zoomX); 1693 end else 1694 ADest.scale(zoomX); 1695 end else 1696 if bounds.Bottom-bounds.Top > 0 then 1697 begin 1698 zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top); 1699 ADest.scale(zoomY); 1700 end; 1701 ADest.translate(-(bounds.Left+bounds.Right)*0.5, -(bounds.Bottom+bounds.Top)*0.5); 1702 copyTo(ADest); 1703 ADest.resetTransform; 1704 end; 710 1705 711 1706 function TBGRAPath.GetSvgString: string; 712 1707 const RadToDeg = 180/Pi; 713 var savedPos: integer; 714 a: TArcDef; 715 formats: TFormatSettings; 716 lastPos,p1: TPointF; 717 implicitCommand: char; 1708 var 1709 formats: TFormatSettings; 1710 lastPosF: TPointF; 1711 implicitCommand: char; 718 1712 719 1713 function FloatToString(value: single): string; … … 724 1718 function CoordToString(const pt: TPointF): string; 725 1719 begin 726 lastPos := pt;1720 lastPosF := pt; 727 1721 result := FloatToString(pt.x)+FloatToString(pt.y); 728 1722 end; … … 745 1739 end; 746 1740 747 var param: string; 748 1741 var elemType: TBGRAPathElementType; 1742 elem: pointer; 1743 a: PArcElement; 1744 Pos: PtrInt; 1745 p1: TPointF; 1746 pts: array of TPointF; 1747 i: integer; 749 1748 begin 750 1749 formats := DefaultFormatSettings; … … 752 1751 753 1752 result := ''; 754 savedPos:= FDataPos; 755 FDataPos := 0; 756 lastPos := EmptyPointF; 1753 Pos := 0; 1754 lastPosF := EmptyPointF; 757 1755 implicitCommand := #0; 758 while FDataPos < savedPos do 759 begin 760 case ReadElementType of 761 peMoveTo: addCommand('M',CoordToString(ReadCoord)); 762 peLineTo: addCommand('L',CoordToString(ReadCoord)); 763 peCloseSubPath: addCommand('z',''); 764 peQuadraticBezierTo: 765 begin 766 param := CoordToString(ReadCoord); 767 param += CoordToString(ReadCoord); 768 addCommand('Q',param); 769 end; 770 peCubicBezierTo: 771 begin 772 param := CoordToString(ReadCoord); 773 param += CoordToString(ReadCoord); 774 param += CoordToString(ReadCoord); 775 addCommand('C',param); 776 end; 777 peArc: 778 begin 779 a := ReadArcDef; 780 p1 := ArcStartPoint(a); 781 if isEmptyPointF(lastPos) or (p1 <> lastPos) then 782 addCommand('L',CoordToString(p1)); 783 param := CoordToString(a.radius); 784 param += FloatToString(a.xAngleRadCW*RadToDeg); 785 param += BoolToString(IsLargeArc(a)); 786 param += BoolToString(not a.anticlockwise); 787 param += CoordToString(ArcEndPoint(a)); 788 addCommand('A',param); 789 end; 790 end; 791 end; 792 FDataPos := savedPos; 1756 repeat 1757 GetElementAt(Pos, elemType, elem); 1758 if elem = nil then break; 1759 case elemType of 1760 peMoveTo: addCommand('M',CoordToString(PPointF(elem)^)); 1761 peLineTo: addCommand('L',CoordToString(PPointF(elem)^)); 1762 peCloseSubPath: addCommand('z',''); 1763 peQuadraticBezierTo: 1764 with PQuadraticBezierToElement(elem)^ do 1765 addCommand('Q',CoordToString(ControlPoint)+CoordToString(Destination)); 1766 peCubicBezierTo: 1767 with PCubicBezierToElement(elem)^ do 1768 addCommand('C',CoordToString(ControlPoint1)+ 1769 CoordToString(ControlPoint2)+CoordToString(Destination)); 1770 peArc: 1771 begin 1772 a := PArcElement(elem); 1773 p1 := ArcStartPoint(a^); 1774 if isEmptyPointF(lastPosF) or (p1 <> lastPosF) then 1775 addCommand('L',CoordToString(p1)); 1776 addCommand('A',CoordToString(a^.radius)+ 1777 FloatToString(a^.xAngleRadCW*RadToDeg)+ 1778 BoolToString(IsLargeArc(a^))+ 1779 BoolToString(not a^.anticlockwise)+ 1780 CoordToString(ArcEndPoint(a^))); 1781 end; 1782 peOpenedSpline, peClosedSpline: 1783 begin 1784 pts := GetPolygonalApprox(Pos, 0.1,True); 1785 for i := 0 to high(pts) do 1786 begin 1787 if isEmptyPointF(lastPosF) then 1788 addCommand('M',CoordToString(pts[i])) 1789 else 1790 addCommand('L',CoordToString(pts[i])); 1791 end; 1792 end; 1793 end; 1794 until not GoToNextElement(Pos); 793 1795 end; 794 1796 … … 800 1802 end; 801 1803 1804 procedure TBGRAPath.RegisterCursor(ACursor: TBGRAPathCursor); 1805 begin 1806 setlength(FCursors, length(FCursors)+1); 1807 FCursors[high(FCursors)] := ACursor; 1808 end; 1809 1810 procedure TBGRAPath.UnregisterCursor(ACursor: TBGRAPathCursor); 1811 var 1812 i,j: Integer; 1813 begin 1814 for i := high(FCursors) downto 0 do 1815 if FCursors[i] = ACursor then 1816 begin 1817 for j := i to high(FCursors)-1 do 1818 FCursors[j] := FCursors[j+1]; 1819 setlength(FCursors, length(FCursors)-1); 1820 exit; 1821 end; 1822 end; 1823 1824 function TBGRAPath.SetLastCoord(ACoord: TPointF): TPointF; 1825 begin 1826 FLastCoord := ACoord; 1827 FLastTransformedCoord := FMatrix*ACoord; 1828 result := FLastTransformedCoord; 1829 end; 1830 1831 procedure TBGRAPath.ClearLastCoord; 1832 begin 1833 FLastCoord := EmptyPointF; 1834 FLastTransformedCoord := EmptyPointF; 1835 end; 1836 1837 procedure TBGRAPath.BezierCurveFromTransformed(tcp1, cp2, pt: TPointF); 1838 begin 1839 with PCubicBezierToElement(AllocateElement(peCubicBezierTo))^ do 1840 begin 1841 ControlPoint1 := tcp1; 1842 ControlPoint2 := FMatrix*cp2; 1843 Destination := SetLastCoord(pt); 1844 FExpectedTransformedControlPoint := Destination + (Destination-ControlPoint2); 1845 end; 1846 end; 1847 1848 procedure TBGRAPath.QuadraticCurveFromTransformed(tcp, pt: TPointF); 1849 begin 1850 with PQuadraticBezierToElement(AllocateElement(peQuadraticBezierTo))^ do 1851 begin 1852 ControlPoint := tcp; 1853 Destination := SetLastCoord(pt); 1854 FExpectedTransformedControlPoint := Destination+(Destination-ControlPoint); 1855 end; 1856 end; 1857 1858 function TBGRAPath.LastCoordDefined: boolean; 1859 begin 1860 result := not isEmptyPointF(FLastTransformedCoord); 1861 end; 1862 1863 function TBGRAPath.GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF; 1864 var pts: ArrayOfTPointF; 1865 elemType: TBGRAPathElementType; 1866 elem: pointer; 1867 pt : TPointF; 1868 i: NativeInt; 1869 begin 1870 GetElementAt(APos, elemType, elem); 1871 case elemType of 1872 peQuadraticBezierTo: 1873 with PQuadraticBezierToElement(elem)^ do 1874 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint); 1875 peCubicBezierTo: 1876 with PCubicBezierToElement(elem)^ do 1877 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint); 1878 peArc: 1879 begin 1880 result := ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation); 1881 pt := GetElementStartCoord(APos); 1882 if pt <> result[0] then 1883 begin 1884 setlength(result, length(result)+1); 1885 for i := high(result) downto 1 do 1886 result[i] := result[i-1]; 1887 result[0] := pt; 1888 end; 1889 end; 1890 peOpenedSpline, peClosedSpline: 1891 with PSplineElement(elem)^ do 1892 begin 1893 setlength(pts, NbControlPoints); 1894 move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF)); 1895 if elemType = peOpenedSpline then 1896 result := ComputeOpenedSpline(pts, SplineStyle, 0.25, AAcceptedDeviation) 1897 else 1898 result := ComputeClosedSpline(pts, SplineStyle, AAcceptedDeviation); 1899 end; 1900 end; 1901 end; 1902 1903 function TBGRAPath.getPoints: ArrayOfTPointF; 1904 begin 1905 result := ToPoints; 1906 end; 1907 1908 function TBGRAPath.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; 1909 begin 1910 result := ToPoints(AMatrix); 1911 end; 1912 1913 function TBGRAPath.getCursor: TBGRACustomPathCursor; 1914 begin 1915 result := CreateCursor; 1916 end; 1917 1918 procedure TBGRAPath.InternalDraw(ADrawProc: TBGRAPathDrawProc; 1919 const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer); 1920 var 1921 nbSub: NativeInt; 1922 1923 procedure OutputSub(subPathStartPos, subPathEndPos: IntPtr); 1924 var 1925 sub: array of ArrayOfTPointF; 1926 temp: ArrayOfTPointF; 1927 startPos,pos,nbPts,curPt,curSub: NativeInt; 1928 elemType: TBGRAPathElementType; 1929 elem: pointer; 1930 begin 1931 pos := subPathStartPos; 1932 setlength(sub, nbSub); 1933 curSub := 0; 1934 while (pos <= subPathEndPos) and (curSub < nbSub) do 1935 begin 1936 GetElementAt(pos, elemType, elem); 1937 if elem = nil then break; 1938 case elemType of 1939 peMoveTo,peLineTo,peCloseSubPath: begin 1940 startPos := pos; 1941 if (elemType = peMoveTo) and (curSub > 0) then 1942 nbPts := 2 1943 else 1944 nbPts := 1; 1945 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1946 begin 1947 GoToNextElement(pos); 1948 inc(nbPts); 1949 end; 1950 setlength(temp, nbPts); 1951 pos := startPos; 1952 if (elemType = peMoveTo) and (curSub > 0) then 1953 begin 1954 temp[0] := EmptyPointF; 1955 temp[1] := PPointF(elem)^; 1956 curPt := 2; 1957 end else 1958 begin 1959 temp[0] := PPointF(elem)^; 1960 curPt := 1; 1961 end; 1962 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 1963 begin 1964 GoToNextElement(pos); 1965 GetElementAt(pos, elemType, elem); 1966 temp[curPt] := PPointF(elem)^; 1967 inc(curPt); 1968 end; 1969 sub[curSub] := temp; 1970 inc(curSub); 1971 temp := nil; 1972 end; 1973 peQuadraticBezierTo,peCubicBezierTo,peArc, 1974 peOpenedSpline, peClosedSpline: 1975 begin 1976 sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False); 1977 inc(curSub); 1978 end; 1979 end; 1980 GoToNextElement(pos); 1981 end; 1982 temp := ConcatPointsF(sub); 1983 if not IsAffineMatrixIdentity(AMatrix) then 1984 temp := AMatrix*temp; 1985 if (elemType = peCloseSubPath) or ((curSub = 2) and (elemType = peClosedSpline)) then 1986 ADrawProc(temp, True, AData) 1987 else 1988 ADrawProc(temp, False, AData); 1989 end; 1990 1991 var 1992 subPathStartPos: IntPtr; 1993 prevPos,pos: PtrInt; 1994 elemType: TBGRAPathElementType; 1995 elem: pointer; 1996 begin 1997 AAcceptedDeviation := CorrectAcceptedDeviation(AAcceptedDeviation, AMatrix); 1998 pos := 0; 1999 nbSub := 0; 2000 subPathStartPos := pos; 2001 repeat 2002 prevPos := pos; 2003 GetElementAt(pos, elemType, elem); 2004 if elem = nil then 2005 begin 2006 pos := prevPos; 2007 break; 2008 end; 2009 if (elemType = peMoveTo) and (nbSub > 0) then 2010 begin 2011 OutputSub(subPathStartPos,prevPos); 2012 nbSub := 0; 2013 subPathStartPos := pos; 2014 end; 2015 case elemType of 2016 peMoveTo,peLineTo,peCloseSubPath: begin 2017 inc(nbSub); 2018 while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do 2019 GoToNextElement(pos); 2020 end; 2021 peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub); 2022 end; 2023 until not GoToNextElement(pos); 2024 if nbSub > 0 then OutputSub(subPathStartPos,pos); 2025 end; 2026 802 2027 procedure TBGRAPath.addPath(const AValue: string); 803 2028 var p: integer; 804 2029 numberError: boolean; 2030 startCoord,lastCoord: TPointF; 805 2031 806 2032 function parseFloat: single; … … 812 2038 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 813 2039 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 814 if (p <= length(AValue)) and (AValue[p] in['e','E']) then inc(p); 815 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 816 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 2040 if (p <= length(AValue)) and (AValue[p] in['e','E']) then 2041 begin 2042 inc(p); 2043 if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p); 2044 while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p); 2045 end; 817 2046 val(copy(AValue,numberStart,p-numberStart),result,errPos); 818 2047 if errPos <> 0 then numberError := true; … … 821 2050 function parseCoord(relative: boolean): TPointF; 822 2051 begin 823 result := PointF(parseFloat,parseFloat); 824 if relative and not isEmptyPointF(FLastCoord) then result += FLastCoord; 2052 result.x := parseFloat; 2053 result.y := parseFloat; 2054 if relative and not isEmptyPointF(lastCoord) then result += lastCoord; 2055 if isEmptyPointF(lastCoord) then startCoord := result; 825 2056 end; 826 2057 … … 832 2063 largeArc: boolean; 833 2064 begin 834 FLastCoord := EmptyPointF; 835 FStartCoord := EmptyPointF; 2065 BeginSubPath; 2066 lastCoord := EmptyPointF; 2067 startCoord := EmptyPointF; 836 2068 p := 1; 837 2069 implicitCommand:= #0; … … 853 2085 closePath; 854 2086 implicitCommand:= #0; 2087 lastCoord := startCoord; 855 2088 end; 856 2089 'M': begin 857 2090 p1 := parseCoord(relative); 858 if not numberError then moveTo(p1); 2091 if not numberError then 2092 begin 2093 moveTo(p1); 2094 lastCoord := p1; 2095 end; 859 2096 if relative then implicitCommand:= 'l' else 860 2097 implicitCommand:= 'L'; … … 862 2099 'L': begin 863 2100 p1 := parseCoord(relative); 864 if not numberError then lineTo(p1); 2101 if not numberError then 2102 begin 2103 lineTo(p1); 2104 lastCoord := p1; 2105 end; 865 2106 end; 866 2107 'H': begin 867 if not isEmptyPointF(FLastCoord) then p1 := FLastCoord 868 else p1 := PointF(0,0); 869 if relative then p1.x += parseFloat 870 else p1.x := parseFloat; 871 if not numberError then lineTo(p1); 2108 if not isEmptyPointF(lastCoord) then 2109 begin 2110 p1 := lastCoord; 2111 if relative then p1.x += parseFloat 2112 else p1.x := parseFloat; 2113 end else 2114 begin 2115 p1 := PointF(parseFloat,0); 2116 lastCoord := p1; 2117 startCoord := p1; 2118 end; 2119 if not numberError then 2120 begin 2121 lineTo(p1); 2122 lastCoord := p1; 2123 end; 872 2124 end; 873 2125 'V': begin 874 if not isEmptyPointF(FLastCoord) then p1 := FLastCoord 875 else p1 := PointF(0,0); 876 if relative then p1.y += parseFloat 877 else p1.y := parseFloat; 878 if not numberError then lineTo(p1); 2126 if not isEmptyPointF(lastCoord) then 2127 begin 2128 p1 := lastCoord; 2129 if relative then p1.y += parseFloat 2130 else p1.y := parseFloat; 2131 end else 2132 begin 2133 p1 := PointF(0,parseFloat); 2134 lastCoord := p1; 2135 startCoord := p1; 2136 end; 2137 if not numberError then 2138 begin 2139 lineTo(p1); 2140 lastCoord := p1; 2141 end; 879 2142 end; 880 2143 'C': begin … … 882 2145 c2 := parseCoord(relative); 883 2146 p1 := parseCoord(relative); 884 if not numberError then bezierCurveTo(c1,c2,p1); 2147 if not numberError then 2148 begin 2149 bezierCurveTo(c1,c2,p1); 2150 lastCoord := p1; 2151 end; 885 2152 end; 886 2153 'S': begin 887 2154 c2 := parseCoord(relative); 888 2155 p1 := parseCoord(relative); 889 if not numberError then smoothBezierCurveTo(c2,p1); 2156 if not numberError then 2157 begin 2158 smoothBezierCurveTo(c2,p1); 2159 lastCoord := p1; 2160 end; 890 2161 end; 891 2162 'Q': begin 892 2163 c1 := parseCoord(relative); 893 2164 p1 := parseCoord(relative); 894 if not numberError then quadraticCurveTo(c1,p1); 2165 if not numberError then 2166 begin 2167 quadraticCurveTo(c1,p1); 2168 lastCoord := p1; 2169 end; 895 2170 end; 896 2171 'T': begin 897 2172 p1 := parseCoord(relative); 898 if not numberError then smoothQuadraticCurveTo(p1); 899 end; 900 'A': begin 901 a.radius := parseCoord(false); 2173 if not numberError then 2174 begin 2175 smoothQuadraticCurveTo(p1); 2176 lastCoord := p1; 2177 end; 2178 end; 2179 'A': 2180 begin 2181 a.radius.x := parseFloat; 2182 a.radius.y := parseFloat; 902 2183 a.xAngleRadCW := parseFloat*Pi/180; 903 2184 largeArc := parseFloat<>0; 904 2185 a.anticlockwise:= parseFloat=0; 905 2186 p1 := parseCoord(relative); 906 arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y); 2187 if not numberError then 2188 begin 2189 arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y); 2190 lastCoord := p1; 2191 end; 907 2192 end; 908 2193 end; … … 915 2200 end; 916 2201 2202 procedure TBGRAPath.openedSpline(const pts: array of TPointF; 2203 style: TSplineStyle); 2204 var elem: PSplineElement; 2205 i: NativeInt; 2206 p: PPointF; 2207 begin 2208 if length(pts) <= 2 then 2209 begin 2210 polyline(pts); 2211 exit; 2212 end; 2213 if not LastCoordDefined then moveTo(pts[0]); 2214 elem := AllocateElement(peOpenedSpline, length(pts)*sizeof(TPointF)); 2215 elem^.NbControlPoints := length(pts); 2216 elem^.SplineStyle := style; 2217 p := PPointF(elem+1); 2218 for i := 0 to high(pts)-1 do 2219 begin 2220 p^ := FMatrix*pts[i]; 2221 inc(p); 2222 end; 2223 p^ := SetLastCoord(pts[high(pts)]); 2224 inc(p); 2225 PInteger(p)^ := length(pts); 2226 end; 2227 2228 procedure TBGRAPath.closedSpline(const pts: array of TPointF; 2229 style: TSplineStyle); 2230 var elem: PSplineElement; 2231 i: NativeInt; 2232 p: PPointF; 2233 begin 2234 if length(pts) = 0 then exit; 2235 if not LastCoordDefined then moveTo(ClosedSplineStartPoint(pts, style)); 2236 if length(pts) <= 2 then exit; 2237 elem := AllocateElement(peClosedSpline, length(pts)*sizeof(TPointF)); 2238 elem^.NbControlPoints := length(pts); 2239 elem^.SplineStyle := style; 2240 p := PPointF(elem+1); 2241 for i := 0 to high(pts) do 2242 begin 2243 p^ := FMatrix*pts[i]; 2244 inc(p); 2245 end; 2246 PInteger(p)^ := length(pts); 2247 end; 2248 2249 procedure TBGRAPath.BitmapDrawSubPathProc(const APoints: array of TPointF; 2250 AClosed: boolean; AData: pointer); 2251 begin 2252 with TStrokeData(AData^) do 2253 if AClosed then 2254 begin 2255 if Texture <> nil then 2256 Bitmap.DrawPolygonAntialias(APoints, Texture, Width) 2257 else 2258 Bitmap.DrawPolygonAntialias(APoints, Color, Width); 2259 end else 2260 begin 2261 if Texture <> nil then 2262 Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Texture, Width) 2263 else 2264 Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Color, Width); 2265 end; 2266 end; 2267 2268 function TBGRAPath.CorrectAcceptedDeviation(AAcceptedDeviation: single; 2269 const AMatrix: TAffineMatrix): single; 2270 var maxZoom: single; 2271 begin 2272 //determine the zoom of the matrix 2273 maxZoom := Max(VectLen(PointF(AMatrix[1,1],AMatrix[2,1])), 2274 VectLen(PointF(AMatrix[1,2],AMatrix[2,2]))); 2275 //make the accepted deviation smaller if the matrix zooms to avoid that 2276 // curves would look angular 2277 if maxZoom = 0 then 2278 result:= 1e10 2279 else 2280 result := AAcceptedDeviation / maxZoom; 2281 end; 2282 2283 procedure TBGRAPath.OnModify; 2284 begin 2285 if length(FCursors)> 0 then 2286 raise Exception.Create('You cannot modify the path when there are cursors'); 2287 end; 2288 2289 procedure TBGRAPath.OnMatrixChange; 2290 begin 2291 //transformed coord are not changed, 2292 //but original coords are lost in the process. 2293 //this has a consequence when using 2294 //arc functions that rely on the previous 2295 //coordinate 2296 FLastCoord := EmptyPointF; 2297 FSubPathStartCoord := EmptyPointF; 2298 end; 2299 917 2300 procedure TBGRAPath.NeedSpace(count: integer); 918 2301 begin 919 if FDataPos + count > FDataSize then 920 begin 921 FDataSize := FDataSize*2+8; 922 ReAllocMem(FData, FDataSize); 923 end; 924 end; 925 926 procedure TBGRAPath.StoreCoord(const pt: TPointF); 927 begin 928 NeedSpace(sizeof(single)*2); 929 with FMatrix*pt do 930 begin 931 PSingle(FData+FDataPos)^ := x; 932 PSingle(FData+FDataPos+sizeof(single))^ := y; 933 end; 934 Inc(FDataPos, sizeof(single)*2); 935 FLastCoord := pt; 936 end; 937 938 function TBGRAPath.ReadCoord: TPointF; 939 begin 940 result := PPointF(FData+FDataPos)^; 941 inc(FDataPos,sizeof(TPointF)); 942 end; 943 944 procedure TBGRAPath.StoreElementType(value: TBGRAPathElementType); 945 begin 946 NeedSpace(sizeof(TBGRAPathElementType)); 947 PBGRAPathElementType(FData+FDataPos)^ := value; 948 Inc(FDataPos, sizeof(TBGRAPathElementType)); 949 FLastElementType:= value; 950 end; 951 952 function TBGRAPath.ReadElementType: TBGRAPathElementType; 953 begin 954 result := PBGRAPathElementType(FData+FDataPos)^; 955 inc(FDataPos,sizeof(TBGRAPathElementType)); 956 end; 957 958 function TBGRAPath.ReadArcDef: TArcDef; 959 begin 960 result := PArcDef(FData+FDataPos)^; 961 inc(FDataPos,sizeof(TArcDef)); 962 end; 963 964 procedure TBGRAPath.RewindFloat; 965 begin 966 if FDataPos >= sizeof(single) then dec(FDataPos, sizeof(Single)); 2302 OnModify; 2303 if FDataPos + count > FDataCapacity then 2304 begin 2305 FDataCapacity := (FDataCapacity shl 1)+8; 2306 if FDataPos + count + 8 > FDataCapacity then 2307 FDataCapacity := FDataPos + count + 8; 2308 ReAllocMem(FData, FDataCapacity); 2309 end; 2310 end; 2311 2312 function TBGRAPath.AllocateElement(AElementType: TBGRAPathElementType; 2313 AExtraBytes: PtrInt): Pointer; 2314 var t: PtrInt; 2315 begin 2316 if not (AElementType in [succ(peNone)..high(TBGRAPathElementType)]) then 2317 raise exception.Create('Invalid element type'); 2318 OnModify; 2319 t := PathElementSize[AElementType]+AExtraBytes; 2320 NeedSpace(SizeOf(TPathElementHeader)+t); 2321 with PPathElementHeader(FData+FDataPos)^ do 2322 begin 2323 ElementType:= AElementType; 2324 PreviousElementType := FLastStoredElementType; 2325 end; 2326 result := FData+(FDataPos+SizeOf(TPathElementHeader)); 2327 FLastSubPathElementType:= AElementType; 2328 FLastStoredElementType:= AElementType; 2329 Inc(FDataPos, sizeof(TPathElementHeader)+t); 967 2330 end; 968 2331 … … 970 2333 begin 971 2334 FData := nil; 972 FDataSize := 0; 973 FDataPos := 0; 974 FLastElementType := peNone; 975 FLastCoord := EmptyPointF; 976 FStartCoord := EmptyPointF; 977 FExpectedControlPoint := EmptyPointF; 2335 FDataCapacity := 0; 2336 FLastMoveToDataPos := -1; 2337 beginPath; 978 2338 resetTransform; 2339 end; 2340 2341 function TBGRAPath.GoToNextElement(var APos: PtrInt): boolean; 2342 var newPos: PtrInt; 2343 p: PSplineElement; 2344 elemType: TBGRAPathElementType; 2345 begin 2346 if APos >= FDataPos then 2347 result := false 2348 else 2349 begin 2350 elemType := PPathElementHeader(FData+APos)^.ElementType; 2351 newPos := APos + sizeof(TPathElementHeader) + PathElementSize[elemType]; 2352 if elemType in[peOpenedSpline,peClosedSpline] then 2353 begin 2354 p := PSplineElement(FData+(APos+sizeof(TPathElementHeader))); 2355 newPos += p^.NbControlPoints * sizeof(TPointF); //extra 2356 end; 2357 if newPos < FDataPos then 2358 begin 2359 result := true; 2360 APos := newPos; 2361 if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or 2362 not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then 2363 raise exception.Create('Internal structure error'); 2364 end 2365 else 2366 result := false; 2367 end; 2368 end; 2369 2370 function TBGRAPath.GoToPreviousElement(var APos: PtrInt): boolean; 2371 var lastElemType: TBGRAPathElementType; 2372 begin 2373 if APos <= 0 then 2374 result := false 2375 else 2376 begin 2377 result := true; 2378 if (APos = FDataPos) then 2379 lastElemType := FLastStoredElementType 2380 else 2381 lastElemType := PPathElementHeader(FData+APos)^.PreviousElementType; 2382 2383 if lastElemType in [peOpenedSpline,peClosedSpline] then 2384 dec(APos, (PInteger(FData+APos)-1)^ *sizeof(TPointF)); //extra 2385 dec(APos, sizeof(TPathElementHeader) + PathElementSize[lastElemType]); 2386 2387 if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or 2388 not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then 2389 raise exception.Create('Internal structure error'); 2390 end; 2391 end; 2392 2393 function TBGRAPath.PeekNextElement(APos: PtrInt): TBGRAPathElementType; 2394 begin 2395 if not GoToNextElement(APos) then 2396 result := peNone 2397 else 2398 result := PPathElementHeader(FData+APos)^.ElementType; 2399 end; 2400 2401 function TBGRAPath.GetElementStartCoord(APos: PtrInt): TPointF; 2402 var 2403 elemType: TBGRAPathElementType; 2404 elem: pointer; 2405 begin 2406 GetElementAt(APos, elemType, elem); 2407 case elemType of 2408 peNone: raise exception.Create('No element'); 2409 peMoveTo: result := PPointF(elem)^; 2410 else 2411 begin 2412 if not GoToPreviousElement(APos) then 2413 raise exception.Create('No previous element') 2414 else 2415 begin 2416 result := GetElementEndCoord(APos); 2417 end; 2418 end; 2419 end; 2420 end; 2421 2422 function TBGRAPath.GetElementEndCoord(APos: PtrInt): TPointF; 2423 var elemType: TBGRAPathElementType; 2424 elem: pointer; 2425 begin 2426 GetElementAt(APos, elemType, elem); 2427 case elemType of 2428 peMoveTo,peLineTo,peCloseSubPath: result := PPointF(elem)^; 2429 peQuadraticBezierTo: result := PQuadraticBezierToElement(elem)^.Destination; 2430 peCubicBezierTo: result := PCubicBezierToElement(elem)^.Destination; 2431 peArc: result := ArcEndPoint(PArcElement(elem)^); 2432 peClosedSpline: result := PPointF(PSplineElement(elem)+1)^; 2433 peOpenedSpline: result := (PPointF(PSplineElement(elem)+1)+(PSplineElement(elem)^.NbControlPoints-1))^; 2434 else 2435 result := EmptyPointF; 2436 end; 2437 end; 2438 2439 function TBGRAPath.GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single; 2440 var elemType: TBGRAPathElementType; 2441 elem: pointer; 2442 pts: array of TPointF; 2443 begin 2444 GetElementAt(APos, elemType, elem); 2445 case elemType of 2446 peMoveTo: result := 0; 2447 peLineTo,peCloseSubPath: result := VectLen(PPointF(elem)^ - GetElementStartCoord(APos))*FScale; 2448 peQuadraticBezierTo: with PQuadraticBezierToElement(elem)^ do 2449 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ComputeLength; 2450 peCubicBezierTo: with PCubicBezierToElement(elem)^ do 2451 result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ComputeLength(AAcceptedDeviation); 2452 peArc: begin 2453 result := VectLen(ArcStartPoint(PArcElement(elem)^) - GetElementStartCoord(APos)); 2454 result += PolylineLen(ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation)); 2455 end; 2456 peClosedSpline,peOpenedSpline: 2457 begin 2458 pts := GetPolygonalApprox(APos, AAcceptedDeviation, true); 2459 result := PolylineLen(pts) + VectLen(pts[0]-GetElementStartCoord(APos)); 2460 end 2461 else 2462 result := 0; 2463 end; 2464 end; 2465 2466 procedure TBGRAPath.GetElementAt(APos: PtrInt; out 2467 AElementType: TBGRAPathElementType; out AElement: pointer); 2468 begin 2469 if APos >= FDataPos then 2470 begin 2471 AElementType := peNone; 2472 AElement := nil; 2473 end else 2474 begin 2475 AElementType:= PPathElementHeader(FData+APos)^.ElementType; 2476 AElement := FData+(APos+sizeof(TPathElementHeader)); 2477 end; 979 2478 end; 980 2479 … … 990 2489 end; 991 2490 2491 constructor TBGRAPath.Create(const APoints: ArrayOfTPointF); 2492 begin 2493 Init; 2494 SetPoints(APoints); 2495 end; 2496 2497 constructor TBGRAPath.Create(APath: IBGRAPath); 2498 begin 2499 Init; 2500 APath.copyTo(self); 2501 end; 2502 992 2503 destructor TBGRAPath.Destroy; 993 begin 2504 var i: integer; 2505 begin 2506 for I := 0 to high(FCursors) do 2507 FCursors[i].OnPathFree; 994 2508 if Assigned(FData) then 995 2509 begin … … 1002 2516 procedure TBGRAPath.beginPath; 1003 2517 begin 2518 DoClear; 2519 end; 2520 2521 procedure TBGRAPath.beginSubPath; 2522 begin 2523 OnModify; 2524 FLastSubPathElementType := peNone; 2525 ClearLastCoord; 2526 FSubPathStartCoord := EmptyPointF; 2527 FExpectedTransformedControlPoint := EmptyPointF; 2528 end; 2529 2530 procedure TBGRAPath.DoClear; 2531 begin 2532 OnModify; 1004 2533 FDataPos := 0; 2534 BeginSubPath; 2535 end; 2536 2537 function TBGRAPath.CheckElementType(AElementType: TBGRAPathElementType): boolean; 2538 begin 2539 result := AElementType <= high(TBGRAPathElementType); 1005 2540 end; 1006 2541 1007 2542 procedure TBGRAPath.closePath; 1008 begin 1009 if (FLastElementType <> peNone) and (FLastElementType <> peCloseSubPath) then 1010 begin 1011 StoreElementType(peCloseSubPath); 1012 FLastCoord := FStartCoord; 2543 var 2544 moveToType: TBGRAPathElementType; 2545 moveToElem: pointer; 2546 begin 2547 if (FLastSubPathElementType <> peNone) and (FLastSubPathElementType <> peCloseSubPath) then 2548 begin 2549 with PClosePathElement(AllocateElement(peCloseSubPath))^ do 2550 begin 2551 StartCoordinate := FSubPathTransformedStartCoord; 2552 LoopDataPos := FLastMoveToDataPos; 2553 end; 2554 if FLastMoveToDataPos <> -1 then 2555 begin 2556 GetElementAt(FLastMoveToDataPos,moveToType,moveToElem); 2557 PMoveToElement(moveToElem)^.LoopDataPos := FDataPos; 2558 FLastMoveToDataPos:= -1; 2559 end; 2560 FLastCoord := FSubPathStartCoord; 2561 FLastTransformedCoord := FSubPathTransformedStartCoord; 1013 2562 end; 1014 2563 end; … … 1016 2565 procedure TBGRAPath.translate(x, y: single); 1017 2566 begin 2567 OnMatrixChange; 1018 2568 FMatrix *= AffineMatrixTranslation(x,y); 1019 2569 end; … … 1021 2571 procedure TBGRAPath.resetTransform; 1022 2572 begin 2573 OnMatrixChange; 1023 2574 FMatrix := AffineMatrixIdentity; 1024 2575 FAngleRadCW := 0; … … 1028 2579 procedure TBGRAPath.rotate(angleRadCW: single); 1029 2580 begin 2581 OnMatrixChange; 1030 2582 FMatrix *= AffineMatrixRotationRad(-angleRadCW); 1031 2583 FAngleRadCW += angleRadCW; … … 1054 2606 procedure TBGRAPath.scale(factor: single); 1055 2607 begin 2608 OnMatrixChange; 1056 2609 FMatrix *= AffineMatrixScale(factor,factor); 1057 2610 FScale *= factor; … … 1070 2623 procedure TBGRAPath.moveTo(const pt: TPointF); 1071 2624 begin 1072 if FLastElementType <> peMoveTo then 1073 begin 1074 StoreElementType(peMoveTo); 1075 StoreCoord(pt); 2625 if FLastSubPathElementType <> peMoveTo then 2626 begin 2627 FLastMoveToDataPos:= FDataPos; 2628 with PMoveToElement(AllocateElement(peMoveTo))^ do 2629 begin 2630 StartCoordinate := SetLastCoord(pt); 2631 LoopDataPos := -1; 2632 end 1076 2633 end else 1077 begin 1078 RewindFloat; 1079 RewindFloat; 1080 StoreCoord(pt); 1081 end; 1082 FLastCoord := pt; 1083 FStartCoord := FLastCoord; 2634 PMoveToElement(FData+(FDataPos-Sizeof(TMoveToElement)))^.StartCoordinate := SetLastCoord(pt); 2635 FSubPathStartCoord := FLastCoord; 2636 FSubPathTransformedStartCoord := FLastTransformedCoord; 1084 2637 end; 1085 2638 1086 2639 procedure TBGRAPath.lineTo(const pt: TPointF); 1087 begin 1088 if not isEmptyPointF(FLastCoord) then 1089 begin 1090 StoreElementType(peLineTo); 1091 StoreCoord(pt); 1092 FLastCoord := pt; 2640 var lastTransfCoord, newTransfCoord: TPointF; 2641 begin 2642 if LastCoordDefined then 2643 begin 2644 lastTransfCoord := FLastTransformedCoord; 2645 newTransfCoord := SetLastCoord(pt); 2646 if newTransfCoord <> lastTransfCoord then 2647 PPointF(AllocateElement(peLineTo))^ := newTransfCoord; 1093 2648 end else 1094 2649 moveTo(pt); 1095 2650 end; 1096 2651 2652 procedure TBGRAPath.polyline(const pts: array of TPointF); 2653 var i: integer; 2654 begin 2655 if length(pts) = 0 then exit; 2656 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts)); 2657 moveTo(pts[0]); 2658 for i := 1 to high(pts) do lineTo(pts[i]); 2659 end; 2660 1097 2661 procedure TBGRAPath.polylineTo(const pts: array of TPointF); 1098 2662 var i: integer; 1099 2663 begin 1100 NeedSpace((sizeof(TBGRAPathElementType)+2*sizeof(single))*length(pts)); 1101 for i := 0 to high(pts) do with pts[i] do lineTo(x,y); 2664 NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts)); 2665 for i := 0 to high(pts) do lineTo(pts[i]); 2666 end; 2667 2668 procedure TBGRAPath.polygon(const pts: array of TPointF); 2669 var lastPt: integer; 2670 begin 2671 if length(pts) = 0 then exit; 2672 lastPt := high(pts); 2673 while (lastPt > 1) and (pts[lastPt] = pts[0]) do dec(lastPt); 2674 if lastPt <> high(pts) then 2675 polyline(slice(pts,lastPt+1)) 2676 else 2677 polyline(pts); 2678 closePath; 1102 2679 end; 1103 2680 … … 1109 2686 procedure TBGRAPath.quadraticCurveTo(const cp, pt: TPointF); 1110 2687 begin 1111 if not isEmptyPointF(FLastCoord) then 1112 begin 1113 StoreElementType(peQuadraticBezierTo); 1114 StoreCoord(cp); 1115 StoreCoord(pt); 1116 FLastCoord := pt; 1117 end else 2688 if LastCoordDefined then 2689 QuadraticCurveFromTransformed(FMatrix*cp, pt) else 2690 begin 1118 2691 lineTo(pt); 1119 FExpectedControlPoint := pt+(pt-cp); 2692 FExpectedTransformedControlPoint := FMatrix*(pt+(pt-cp)); 2693 end; 1120 2694 end; 1121 2695 … … 1127 2701 procedure TBGRAPath.bezierCurveTo(const cp1, cp2, pt: TPointF); 1128 2702 begin 1129 if isEmptyPointF(FLastCoord) then moveTo(cp1); 1130 StoreElementType(peCubicBezierTo); 1131 StoreCoord(cp1); 1132 StoreCoord(cp2); 1133 StoreCoord(pt); 1134 FLastCoord := pt; 1135 FExpectedControlPoint := pt + (pt-cp2); 2703 if not LastCoordDefined then moveTo(cp1); 2704 BezierCurveFromTransformed(FMatrix*cp1, cp2, pt); 1136 2705 end; 1137 2706 … … 1142 2711 end; 1143 2712 2713 procedure TBGRAPath.bezierCurve(p1, cp1, cp2, p2: TPointF); 2714 begin 2715 moveTo(p1); 2716 bezierCurveTo(cp1,cp2,p2); 2717 end; 2718 1144 2719 procedure TBGRAPath.smoothBezierCurveTo(cp2x, cp2y, x, y: single); 1145 2720 begin … … 1149 2724 procedure TBGRAPath.smoothBezierCurveTo(const cp2, pt: TPointF); 1150 2725 begin 1151 if (FLast ElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedControlPoint) then1152 bezierCurveTo(FExpectedControlPoint,cp2,pt)1153 else if not isEmptyPointF(FLastCoord)then1154 bezierCurveTo(FLastCoord,cp2,pt)2726 if (FLastSubPathElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then 2727 BezierCurveFromTransformed(FExpectedTransformedControlPoint,cp2,pt) 2728 else if LastCoordDefined then 2729 BezierCurveFromTransformed(FLastTransformedCoord,cp2,pt) 1155 2730 else 1156 2731 bezierCurveTo(cp2,cp2,pt); … … 1163 2738 end; 1164 2739 2740 procedure TBGRAPath.quadraticCurve(p1, cp, p2: TPointF); 2741 begin 2742 moveTo(p1); 2743 quadraticCurveTo(cp,p2); 2744 end; 2745 1165 2746 procedure TBGRAPath.smoothQuadraticCurveTo(x, y: single); 1166 2747 begin … … 1170 2751 procedure TBGRAPath.smoothQuadraticCurveTo(const pt: TPointF); 1171 2752 begin 1172 if (FLast ElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedControlPoint) then1173 quadraticCurveTo(FExpectedControlPoint,pt)1174 else if not isEmptyPointF(FLastCoord)then1175 quadraticCurveTo(FLastCoord,pt)2753 if (FLastSubPathElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then 2754 QuadraticCurveFromTransformed(FExpectedTransformedControlPoint,pt) 2755 else if LastCoordDefined then 2756 QuadraticCurveFromTransformed(FLastTransformedCoord,pt) 1176 2757 else 1177 2758 quadraticCurveTo(pt,pt); … … 1237 2818 var p0 : TPointF; 1238 2819 begin 1239 if isEmptyPointF(FLastCoord) then2820 if IsEmptyPointF(FLastCoord) then 1240 2821 p0 := p1 else p0 := FLastCoord; 1241 2822 arc(Html5ArcTo(p0,p1,p2,radius)); … … 1243 2824 1244 2825 procedure TBGRAPath.arc(const arcDef: TArcDef); 1245 var transformedArc: TArc Def;2826 var transformedArc: TArcElement; 1246 2827 begin 1247 2828 if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then … … 1249 2830 else 1250 2831 begin 1251 if isEmptyPointF(FLastCoord)then2832 if not LastCoordDefined then 1252 2833 moveTo(ArcStartPoint(arcDef)); 1253 StoreElementType(peArc);1254 NeedSpace(sizeof(TArcDef));1255 2834 transformedArc.anticlockwise := arcDef.anticlockwise; 1256 2835 transformedArc.startAngleRadCW := arcDef.startAngleRadCW; … … 1259 2838 transformedArc.radius := arcDef.radius*FScale; 1260 2839 transformedArc.xAngleRadCW := arcDef.xAngleRadCW+FAngleRadCW; 1261 PArcDef(FData+FDataPos)^ := transformedArc; 1262 inc(FDataPos, sizeof(TArcDef)); 1263 FLastCoord := ArcEndPoint(arcDef); 1264 end; 1265 end; 1266 1267 procedure TBGRAPath.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, 2840 PArcElement(AllocateElement(peArc))^ := transformedArc; 2841 {$PUSH}{$OPTIMIZATION OFF} 2842 SetLastCoord(ArcEndPoint(arcDef)); 2843 {$POP} 2844 end; 2845 end; 2846 2847 procedure TBGRAPath.arc(cx, cy, rx, ry: single; xAngleRadCW, startAngleRadCW, 1268 2848 endAngleRadCW: single); 1269 2849 begin … … 1280 2860 anticlockwise: boolean; x, y: single); 1281 2861 begin 1282 if isEmptyPointF(FLastCoord) then2862 if IsEmptyPointF(FLastCoord) then 1283 2863 moveTo(x,y) 1284 2864 else … … 1287 2867 1288 2868 procedure TBGRAPath.copyTo(dest: IBGRAPath); 1289 var savedPos: integer; 1290 cp1,cp2,p1: TPointF; 1291 begin 1292 savedPos:= FDataPos; 1293 FDataPos := 0; 1294 while FDataPos < savedPos do 1295 begin 1296 case ReadElementType of 1297 peMoveTo: dest.moveTo(ReadCoord); 1298 peLineTo: dest.lineTo(ReadCoord); 1299 peCloseSubPath: dest.closePath; 1300 peQuadraticBezierTo: 1301 begin 1302 cp1 := ReadCoord; 1303 p1 := ReadCoord; 1304 dest.quadraticCurveTo(cp1,p1); 1305 end; 1306 peCubicBezierTo: 1307 begin 1308 cp1 := ReadCoord; 1309 cp2 := ReadCoord; 1310 p1 := ReadCoord; 1311 dest.bezierCurveTo(cp1,cp2,p1); 1312 end; 1313 peArc: dest.arc(ReadArcDef); 1314 end; 1315 end; 1316 FDataPos := savedPos; 2869 var pos: IntPtr; 2870 elemType: TBGRAPathElementType; 2871 elem: Pointer; 2872 pts: array of TPointF; 2873 begin 2874 pos := 0; 2875 repeat 2876 GetElementAt(pos, elemType, elem); 2877 if elem = nil then break; 2878 case elemType of 2879 peMoveTo: dest.moveTo(PPointF(elem)^); 2880 peLineTo: dest.lineTo(PPointF(elem)^); 2881 peCloseSubPath: dest.closePath; 2882 peQuadraticBezierTo: 2883 with PQuadraticBezierToElement(elem)^ do 2884 dest.quadraticCurveTo(ControlPoint,Destination); 2885 peCubicBezierTo: 2886 with PCubicBezierToElement(elem)^ do 2887 dest.bezierCurveTo(ControlPoint1,ControlPoint2,Destination); 2888 peArc: dest.arc(PArcElement(elem)^); 2889 peOpenedSpline, peClosedSpline: 2890 begin 2891 with PSplineElement(elem)^ do 2892 begin 2893 setlength(pts, NbControlPoints); 2894 move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF)); 2895 if elemType = peOpenedSpline then 2896 dest.openedSpline(pts, SplineStyle) 2897 else 2898 dest.closedSpline(pts, SplineStyle); 2899 pts := nil; 2900 end; 2901 end; 2902 end; 2903 until not GoToNextElement(pos); 1317 2904 end; 1318 2905 -
GraphicTest/Packages/bgrabitmap/bgrapen.pas
r472 r494 12 12 13 13 uses 14 SysUtils, Graphics, BGRABitmapTypes;14 SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform; 15 15 16 16 var //predefined pen styles … … 18 18 19 19 type 20 21 { TBGRAPenStroker } 22 23 TBGRAPenStroker = class(TBGRACustomPenStroker) 24 protected 25 { Pen style can be defined by PenStyle property of by CustomPenStyle property. 26 When PenStyle property is assigned, CustomPenStyle property is assigned the actual 27 pen pattern. } 28 FCustomPenStyle: TBGRAPenStyle; 29 FPenStyle: TPenStyle; 30 FArrow: TBGRACustomArrow; 31 FArrowOwned: boolean; 32 FOriginalStrokeMatrix,FStrokeMatrix,FStrokeMatrixInverse: TAffineMatrix; 33 FStrokeZoom: single; 34 FStrokeMatrixIdentity: boolean; 35 FLineCap: TPenEndCap; 36 FJoinStyle: TPenJoinStyle; 37 FMiterLimit: single; 38 39 function GetArrow: TBGRACustomArrow; override; 40 function GetArrowOwned: boolean; override; 41 function GetCustomPenStyle: TBGRAPenStyle; override; 42 function GetJoinStyle: TPenJoinStyle; override; 43 function GetLineCap: TPenEndCap; override; 44 function GetMiterLimit: single; override; 45 function GetPenStyle: TPenStyle; override; 46 function GetStrokeMatrix: TAffineMatrix; override; 47 procedure SetArrow(AValue: TBGRACustomArrow); override; 48 procedure SetArrowOwned(AValue: boolean); override; 49 procedure SetCustomPenStyle(AValue: TBGRAPenStyle); override; 50 procedure SetJoinStyle(AValue: TPenJoinStyle); override; 51 procedure SetLineCap(AValue: TPenEndCap); override; 52 procedure SetMiterLimit(AValue: single); override; 53 procedure SetPenStyle(AValue: TPenStyle); override; 54 procedure SetStrokeMatrix(const AValue: TAffineMatrix); override; 55 public 56 constructor Create; 57 destructor Destroy; override; 58 function ComputePolyline(const APoints: array of TPointF; AWidth: single; AClosedCap: boolean = true): ArrayOfTPointF; override; 59 function ComputePolyline(const APoints: array of TPointF; AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean = true): ArrayOfTPointF; override; 60 function ComputePolylineAutocycle(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override; 61 function ComputePolygon(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override; 62 63 end; 64 20 65 TBGRAPolyLineOption = (plRoundCapOpen, //specifies that the line ending is opened 21 66 plCycle, //specifies that it is a polygon … … 26 71 TComputeArrowHeadProc = function(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF of object; 27 72 28 { Draw a polyline with specified parameters. If a scanner is specified, it is used as a texture.29 Else the pencolor parameter is used as a solid color. }30 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF;31 width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;32 options: TBGRAPolyLineOptions; scan: IBGRAScanner = nil; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0);33 34 73 { Compute the path for a polyline } 35 74 function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single; 36 75 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 37 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow Start: TComputeArrowHeadProc = nil; wantedStartArrowPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; WantedEndArrowPos: single = 0): ArrayOfTPointF;76 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF; 38 77 39 78 { Compute the path for a poly-polyline } 40 79 function ComputeWidePolyPolylinePoints(const linepts: array of TPointF; width: single; 41 80 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 42 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow Start: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0): ArrayOfTPointF;81 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF; 43 82 44 83 {--------------------- Pixel line procedures --------------------------} … … 106 145 end; 107 146 dest.VertLine(X1,Y1,Y2,c, ADrawMode); 147 Exit; 108 148 end; 109 149 … … 690 730 styleLength := 0; 691 731 styleIndex := -1; 732 remainingDash := 0; 733 betweenDash := false; 692 734 for i := 0 to high(penstyle) do 693 735 if penstyle[i] <= 0 then … … 745 787 end; 746 788 747 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF; width: single;748 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;749 options: TBGRAPolyLineOptions; scan: IBGRAScanner; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single);750 var751 widePolylinePoints: ArrayOfTPointF;752 begin753 widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);754 if scan <> nil then755 bmp.FillPolyAntialias(widePolylinePoints,scan)756 else757 bmp.FillPolyAntialias(widePolylinePoints,pencolor);758 end;759 760 789 function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single; 761 790 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 762 options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; wantedStartArrowPos: single; arrowEnd: TComputeArrowHeadProc; wantedEndArrowPos: single): ArrayOfTPointF; 791 options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF; 792 const oneOver512 = 1/512; 763 793 var 764 794 startArrowPos, startArrowDir, endArrowPos, endArrowDir: TPointF; … … 986 1016 hasStart,hasEnd: boolean; 987 1017 begin 988 if assigned(arrow Start) and not isEmptyPointF(startArrowPos) then989 arrowStartData := arrow Start(startArrowPos, startArrowDir, width, startArrowLinePos)1018 if assigned(arrow) and not isEmptyPointF(startArrowPos) then 1019 arrowStartData := arrow.ComputeStartAt(startArrowPos, startArrowDir, width, startArrowLinePos) 990 1020 else 991 1021 arrowStartData := nil; 992 if assigned(arrow End) and not isEmptyPointF(endArrowPos) then993 arrowEndData := arrow End(endArrowPos, endArrowDir, width, endArrowLinePos)1022 if assigned(arrow) and not isEmptyPointF(endArrowPos) then 1023 arrowEndData := arrow.ComputeEndAt(endArrowPos, endArrowDir, width, endArrowLinePos) 994 1024 else 995 1025 arrowEndData := nil; … … 1033 1063 linePos: single; 1034 1064 startArrowDone,endArrowDone: boolean; 1065 wantedStartArrowPos,wantedEndArrowPos: single; 1035 1066 1036 1067 begin … … 1042 1073 if isEmptyPointF(linepts[i]) then 1043 1074 begin 1044 result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit );1075 result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow); 1045 1076 exit; 1046 1077 end; … … 1055 1086 pjsMiter: if miterLimit < 1.001 then maxMiter := hw*1.001 else 1056 1087 maxMiter := hw*miterLimit; 1088 else 1089 raise Exception.Create('Unknown join style'); 1057 1090 end; 1058 1091 … … 1062 1095 setlength(pts, length(linepts)+2); 1063 1096 for i := 0 to high(linepts) do 1064 if (nbPts = 0) or ( linepts[i] <> pts[nbPts-1]) then1097 if (nbPts = 0) or (abs(linepts[i].x-pts[nbPts-1].x)>oneOver512) or (abs(linepts[i].y-pts[nbPts-1].y)>oneOver512) then 1065 1098 begin 1066 1099 pts[nbPts]:= linePts[i]; 1067 1100 inc(nbPts); 1068 1101 end; 1069 if (nbPts > 1) and 1070 (pts[nbPts-1] = pts[0]) then dec(nbPts); 1102 if (nbPts > 1) and (plCycle in options) and 1103 (abs(pts[0].x-pts[nbPts-1].x)<=oneOver512) and 1104 (abs(pts[0].y-pts[nbPts-1].y)<=oneOver512) then dec(nbPts); 1071 1105 if (plCycle in options) and (nbPts > 2) then 1072 1106 begin … … 1078 1112 pts[nbPts] := pts[1]; 1079 1113 inc(nbPts); 1080 linecap := pecRound;1081 1114 end else 1082 1115 options -= [plCycle]; … … 1095 1128 endArrowDir := EmptyPointF; 1096 1129 endArrowPos := EmptyPointF; 1097 startArrowDone := @arrowStart = nil; 1098 endArrowDone := @arrowEnd = nil; 1130 if Assigned(arrow) then 1131 begin 1132 wantedStartArrowPos:= arrow.StartOffsetX; 1133 wantedEndArrowPos:= arrow.EndOffsetX; 1134 startArrowDone := not arrow.IsStartDefined; 1135 endArrowDone := not arrow.IsEndDefined; 1136 end 1137 else 1138 begin 1139 wantedStartArrowPos:= 0; 1140 wantedEndArrowPos:= 0; 1141 startArrowDone := true; 1142 endArrowDone := true; 1143 end; 1099 1144 1100 1145 //init computed points arrays … … 1411 1456 width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; 1412 1457 joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 1413 options: TBGRAPolyLineOptions; miterLimit: single; arrow Start: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single): ArrayOfTPointF;1458 options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF; 1414 1459 1415 1460 var … … 1428 1473 for j := startIndex to endIndexP1-1 do 1429 1474 subPts[j-startIndex] := linepts[j]; 1430 tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow Start,arrowStartPos,arrowEnd,arrowEndPos);1475 tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow); 1431 1476 if length(results) = nbresults then 1432 1477 setlength(results,(nbresults+1)*2); … … 1470 1515 end; 1471 1516 1517 { TBGRAPenStroker } 1518 1519 function TBGRAPenStroker.GetArrow: TBGRACustomArrow; 1520 begin 1521 result := FArrow; 1522 end; 1523 1524 function TBGRAPenStroker.GetArrowOwned: boolean; 1525 begin 1526 result := FArrowOwned; 1527 end; 1528 1529 function TBGRAPenStroker.GetCustomPenStyle: TBGRAPenStyle; 1530 begin 1531 result := FCustomPenStyle; 1532 end; 1533 1534 function TBGRAPenStroker.GetJoinStyle: TPenJoinStyle; 1535 begin 1536 result := FJoinStyle; 1537 end; 1538 1539 function TBGRAPenStroker.GetLineCap: TPenEndCap; 1540 begin 1541 result := FLineCap; 1542 end; 1543 1544 function TBGRAPenStroker.GetMiterLimit: single; 1545 begin 1546 result := FMiterLimit; 1547 end; 1548 1549 function TBGRAPenStroker.GetPenStyle: TPenStyle; 1550 begin 1551 result := FPenStyle; 1552 end; 1553 1554 function TBGRAPenStroker.GetStrokeMatrix: TAffineMatrix; 1555 begin 1556 result := FOriginalStrokeMatrix; 1557 end; 1558 1559 procedure TBGRAPenStroker.SetArrow(AValue: TBGRACustomArrow); 1560 begin 1561 FArrow := AValue; 1562 end; 1563 1564 procedure TBGRAPenStroker.SetArrowOwned(AValue: boolean); 1565 begin 1566 FArrowOwned := AValue; 1567 end; 1568 1569 procedure TBGRAPenStroker.SetCustomPenStyle(AValue: TBGRAPenStyle); 1570 begin 1571 if FCustomPenStyle=AValue then Exit; 1572 FCustomPenStyle:=AValue; 1573 if AValue = SolidPenStyle then FPenStyle := psSolid 1574 else if AValue = ClearPenStyle then FPenStyle:= psClear 1575 else if AValue = DashPenStyle then FPenStyle:= psDash 1576 else if AValue = DotPenStyle then FPenStyle := psDot 1577 else if AValue = DashDotPenStyle then FPenStyle:= psDashDot 1578 else if AValue = DashDotDotPenStyle then FPenStyle:= psDashDotDot 1579 else 1580 begin 1581 FPenStyle := psPattern; 1582 FCustomPenStyle:= DuplicatePenStyle(AValue); 1583 end; 1584 end; 1585 1586 procedure TBGRAPenStroker.SetJoinStyle(AValue: TPenJoinStyle); 1587 begin 1588 FJoinStyle:= AValue; 1589 end; 1590 1591 procedure TBGRAPenStroker.SetLineCap(AValue: TPenEndCap); 1592 begin 1593 FLineCap:= AValue; 1594 end; 1595 1596 procedure TBGRAPenStroker.SetMiterLimit(AValue: single); 1597 begin 1598 FMiterLimit := AValue; 1599 end; 1600 1601 procedure TBGRAPenStroker.SetStrokeMatrix(const AValue: TAffineMatrix); 1602 begin 1603 if FOriginalStrokeMatrix=AValue then Exit; 1604 FOriginalStrokeMatrix:=AValue; 1605 FStrokeMatrix := AValue; 1606 FStrokeMatrix[1,3] := 0; 1607 FStrokeMatrix[2,3] := 0; 1608 FStrokeZoom := max(VectLen(PointF(FStrokeMatrix[1,1],FStrokeMatrix[2,1])), 1609 VectLen(PointF(FStrokeMatrix[1,2],FStrokeMatrix[2,2]))); 1610 if FStrokeZoom > 0 then 1611 FStrokeMatrix *= AffineMatrixScale(1/FStrokeZoom,1/FStrokeZoom); 1612 FStrokeMatrixIdentity := IsAffineMatrixIdentity(FStrokeMatrix); 1613 FStrokeMatrixInverse := AffineMatrixInverse(FStrokeMatrix); 1614 end; 1615 1616 procedure TBGRAPenStroker.SetPenStyle(AValue: TPenStyle); 1617 begin 1618 if FPenStyle=AValue then Exit; 1619 Case AValue of 1620 psSolid: FCustomPenStyle := SolidPenStyle; 1621 psDash: FCustomPenStyle := DashPenStyle; 1622 psDot: FCustomPenStyle := DotPenStyle; 1623 psDashDot: FCustomPenStyle := DashDotPenStyle; 1624 psDashDotDot: FCustomPenStyle := DashDotDotPenStyle; 1625 else FCustomPenStyle := ClearPenStyle; 1626 end; 1627 FPenStyle := AValue; 1628 end; 1629 1630 constructor TBGRAPenStroker.Create; 1631 begin 1632 Style := psSolid; 1633 LineCap := pecRound; 1634 JoinStyle := pjsBevel; 1635 MiterLimit := 2; 1636 fillchar(FOriginalStrokeMatrix,sizeof(FOriginalStrokeMatrix),0); 1637 StrokeMatrix := AffineMatrixIdentity; 1638 end; 1639 1640 destructor TBGRAPenStroker.Destroy; 1641 begin 1642 if ArrowOwned then FreeAndNil(FArrow); 1643 inherited Destroy; 1644 end; 1645 1646 function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF; 1647 AWidth: single; AClosedCap: boolean): ArrayOfTPointF; 1648 var 1649 c: TBGRAPixel; 1650 begin 1651 if not AClosedCap then 1652 c := BGRAWhite //needed for alpha junction 1653 else 1654 c := BGRAPixelTransparent; 1655 1656 if FStrokeMatrixIdentity then 1657 result := ComputePolyline(APoints,AWidth*FStrokeZoom,c,AClosedCap) 1658 else 1659 result := FStrokeMatrix*ComputePolyline(FStrokeMatrixInverse*APoints,AWidth*FStrokeZoom,c,AClosedCap); 1660 end; 1661 1662 function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF; 1663 AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean): ArrayOfTPointF; 1664 var options: TBGRAPolyLineOptions; 1665 begin 1666 options := []; 1667 if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap]; 1668 if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap]; 1669 if not AClosedCap then options += [plRoundCapOpen]; 1670 if FStrokeMatrixIdentity then 1671 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1672 else 1673 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow); 1674 end; 1675 1676 function TBGRAPenStroker.ComputePolylineAutocycle( 1677 const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; 1678 var options: TBGRAPolyLineOptions; 1679 begin 1680 options := [plAutoCycle]; 1681 if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap]; 1682 if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap]; 1683 if FStrokeMatrixIdentity then 1684 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1685 else 1686 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1687 end; 1688 1689 function TBGRAPenStroker.ComputePolygon(const APoints: array of TPointF; 1690 AWidth: single): ArrayOfTPointF; 1691 begin 1692 if FStrokeMatrixIdentity then 1693 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit) 1694 else 1695 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit); 1696 end; 1697 1472 1698 initialization 1473 1699 -
GraphicTest/Packages/bgrabitmap/bgrapolygon.pas
r472 r494 33 33 34 34 uses 35 Classes, SysUtils, Graphics, BGRABitmapTypes, BGRAFillInfo;35 Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRAFillInfo, BGRAPath; 36 36 37 37 procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo; … … 59 59 procedure AddShape(AInfo: TBGRACustomFillInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel); 60 60 function CheckRectangleBorderBounds(var x1, y1, x2, y2: single; w: single): boolean; 61 procedure InternalAddStroke(const APoints: array of TPointF; AClosed: boolean; AData: Pointer); 61 62 public 62 63 FillMode : TFillMode; … … 70 71 procedure AddPolygon(const points: array of TPointF; AColor: TBGRAPixel); 71 72 procedure AddPolygon(const points: array of TPointF; ATexture: IBGRAScanner); 73 procedure AddPathStroke(APath: TBGRAPath; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker); 74 procedure AddPathStroke(APath: TBGRAPath; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker); 75 procedure AddPathStroke(APath: TBGRAPath; AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker); 76 procedure AddPathStroke(APath: TBGRAPath; AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker); 77 procedure AddPathFill(APath: TBGRAPath; AColor: TBGRAPixel); 78 procedure AddPathFill(APath: TBGRAPath; ATexture: IBGRAScanner); 79 procedure AddPathFill(APath: TBGRAPath; AMatrix: TAffineMatrix; AColor: TBGRAPixel); 80 procedure AddPathFill(APath: TBGRAPath; AMatrix: TAffineMatrix; ATexture: IBGRAScanner); 81 procedure AddPolylineStroke(const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker); 82 procedure AddPolylineStroke(const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker); 83 procedure AddPolygonStroke(const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker); 84 procedure AddPolygonStroke(const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker); 72 85 procedure AddTriangleLinearColor(pt1, pt2, pt3: TPointF; c1, c2, c3: TBGRAPixel); 73 86 procedure AddTriangleLinearMapping(pt1, pt2, pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); 74 87 procedure AddQuadLinearColor(pt1, pt2, pt3, pt4: TPointF; c1, c2, c3, c4: TBGRAPixel); 75 procedure AddQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); 88 procedure AddQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, {%H-}tex3, tex4: TPointF; 89 ACulling: TFaceCulling = fcNone); 76 90 procedure AddQuadPerspectiveMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); 77 91 procedure AddEllipse(x, y, rx, ry: single; AColor: TBGRAPixel); … … 126 140 uses Math, BGRABlend, BGRAGradientScanner, BGRATransform; 127 141 142 type 143 TPathStrokeData = record 144 Stroker: TBGRACustomPenStroker; 145 Texture: IBGRAScanner; 146 Color: TBGRAPixel; 147 Width: Single; 148 end; 149 128 150 procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo; 129 151 c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean); 152 const oneOver512 = 1/512; 130 153 var 131 154 inter: array of TIntersectionInfo; … … 135 158 inter: array of TIntersectionInfo; 136 159 nbInter: integer; 160 sliceIndex: integer; 137 161 end; 138 162 … … 230 254 begin 231 255 if (scan=nil) and (c.alpha=0) then exit; 232 If not shapeInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;256 If not BGRAShapeComputeMinMax(shapeInfo,minx,miny,maxx,maxy,bmp) then exit; 233 257 234 258 inter := shapeInfo.CreateIntersectionArray; … … 265 289 begin 266 290 with firstScan do 291 begin 267 292 shapeInfo.ComputeAndSort(yb+1/256,inter,nbInter,NonZeroWinding); 293 sliceIndex:= shapeInfo.GetSliceIndex; 294 end; 268 295 with lastScan do 296 begin 269 297 shapeInfo.ComputeAndSort(yb+255/256,inter,nbInter,NonZeroWinding); 270 if (firstScan.nbInter = lastScan.nbInter) and (firstScan.nbInter >= 2) then 298 sliceIndex:= shapeInfo.GetSliceIndex; 299 end; 300 if (firstScan.sliceIndex = lastScan.sliceIndex) and (firstScan.nbInter = lastScan.nbInter) then 271 301 begin 272 302 optimised := true; … … 286 316 x1 := firstScan.inter[i+i].interX; 287 317 x1b := lastScan.inter[i+i].interX; 288 if (x1 > x1b) then289 begin290 temp := x1;291 x1 := x1b;292 x1b := temp;293 end;294 318 x2 := firstScan.inter[i+i+1].interX; 295 319 x2b := lastScan.inter[i+i+1].interX; 296 if (x2 < x2b) then 320 if (abs(x1-x1b)<oneOver512) and (abs(x2-x2b)<oneOver512) and 321 ((i+i+2 >= firstScan.nbInter) or 322 ((firstScan.inter[i+i+2].interX >= x2+1) and 323 (lastScan.inter[i+i+2].interX >= x2b+1))) then 297 324 begin 298 temp := x2; 299 x2 := x2b; 300 x2b := temp; 325 x1 := (x1+x1b)*0.5; 326 x2 := (x2+x2b)*0.5; 327 ix1 := floor(x1); 328 ix2 := floor(x2); 329 if ix1 < minx then ix1 := minx; 330 if ix2 > maxx then ix2 := maxx; 331 if ix1>ix2 then continue; 332 if ix1=ix2 then 333 begin 334 tempDensity:= round((x2-x1)*256); 335 if scan <> nil then //with texture scan 336 begin 337 scan.ScanMoveTo(ix1,yb); 338 c := scan.ScanNextPixel; 339 c.alpha := c.alpha*tempDensity shr 8; 340 if linearBlend then 341 bmp.DrawPixel(ix1, yb, c, dmLinearBlend) 342 else 343 bmp.DrawPixel(ix1, yb, c, dmDrawWithTransparency); 344 end else 345 if EraseMode then //erase with alpha 346 bmp.ErasePixel(ix1,yb,c.alpha*tempDensity shr 8) 347 else 348 begin //solid color 349 c2.alpha := c.alpha*tempDensity shr 8; 350 if linearBlend then 351 bmp.DrawPixel(ix1, yb, c2, dmLinearBlend) 352 else 353 bmp.DrawPixel(ix1, yb, c2, dmDrawWithTransparency); 354 end; 355 end else 356 begin 357 tempDensity:= round((ix1+1-x1)*256); 358 if scan <> nil then scan.ScanMoveTo(ix1,yb); 359 if tempDensity < 256 then 360 begin 361 if scan <> nil then //with texture scan 362 begin 363 c := scan.ScanNextPixel; 364 c.alpha := c.alpha*tempDensity shr 8; 365 if linearBlend then 366 bmp.DrawPixel(ix1, yb, c, dmLinearBlend) 367 else 368 bmp.DrawPixel(ix1, yb, c, dmDrawWithTransparency); 369 end else 370 if EraseMode then //erase with alpha 371 bmp.ErasePixel(ix1,yb, c.alpha*tempDensity shr 8) 372 else 373 begin //solid color 374 c2.alpha := c.alpha*tempDensity shr 8; 375 if linearBlend then 376 bmp.DrawPixel(ix1, yb, c2, dmLinearBlend) 377 else 378 bmp.DrawPixel(ix1, yb, c2, dmDrawWithTransparency); 379 end; 380 inc(ix1); 381 end; 382 tempDensity:= round((x2-ix2)*256); 383 if tempDensity < 256 then dec(ix2); 384 if ix2 >= ix1 then 385 begin 386 if scan <> nil then //with texture scan 387 begin 388 if linearBlend then 389 ScannerPutPixels(scan, bmp.ScanLine[yb] + ix1, ix2-ix1+1, dmLinearBlend) 390 else 391 ScannerPutPixels(scan, bmp.ScanLine[yb] + ix1, ix2-ix1+1, dmDrawWithTransparency); 392 end else 393 if EraseMode then //erase with alpha 394 bmp.EraseLine(ix1,yb,ix2,yb,c.alpha,True) 395 else 396 begin //solid color 397 if LinearBlend then 398 bmp.HorizLine(ix1,yb,ix2,c,dmLinearBlend) 399 else 400 bmp.HorizLine(ix1,yb,ix2,c,dmDrawWithTransparency); 401 end; 402 end; 403 if tempDensity < 256 then 404 begin 405 inc(ix2); 406 if scan <> nil then //with texture scan 407 begin 408 c := scan.ScanNextPixel; 409 c.alpha := c.alpha*tempDensity shr 8; 410 if linearBlend then 411 bmp.DrawPixel(ix2, yb, c, dmLinearBlend) 412 else 413 bmp.DrawPixel(ix2, yb, c, dmDrawWithTransparency); 414 end else 415 if EraseMode then //erase with alpha 416 bmp.ErasePixel(ix2,yb,c.alpha*tempDensity shr 8) 417 else 418 begin //solid color 419 c2.alpha := c.alpha*tempDensity shr 8; 420 if linearBlend then 421 bmp.DrawPixel(ix2, yb, c2, dmLinearBlend) 422 else 423 bmp.DrawPixel(ix2, yb, c2, dmDrawWithTransparency); 424 end; 425 end; 426 end; 427 continue; 428 end else 429 begin 430 if (x1 > x1b) then 431 begin 432 temp := x1; 433 x1 := x1b; 434 x1b := temp; 435 end; 436 if (x2 < x2b) then 437 begin 438 temp := x2; 439 x2 := x2b; 440 x2b := temp; 441 end; 442 443 {$DEFINE INCLUDE_FILLDENSITY} 444 {$DEFINE PARAM_SINGLESEGMENT} 445 {$i density256.inc} 446 SubTriangleDensity(x1,256,x1b,0); 447 SubTriangleDensity(x2b,0,x2,256); 301 448 end; 302 {$i filldensitysegment256.inc}303 SubTriangleDensity(x1,256,x1b,0);304 SubTriangleDensity(x2b,0,x2,256);305 449 end; 306 450 end else … … 311 455 shapeInfo.ComputeAndSort(GetYScan(yc),inter,nbInter,NonZeroWinding); 312 456 313 {$i filldensity256.inc} 457 {$DEFINE INCLUDE_FILLDENSITY} 458 {$i density256.inc} 314 459 end; 315 460 end; … … 323 468 shapeInfo.ComputeAndSort(GetYScan(yc),inter,nbInter,NonZeroWinding); 324 469 325 {$i filldensity256.inc} 470 {$DEFINE INCLUDE_FILLDENSITY} 471 {$i density256.inc} 326 472 end; 327 473 end; … … 330 476 begin 331 477 if optimised then 478 {$DEFINE INCLUDE_RENDERDENSITY} 332 479 {$define PARAM_LINEARANTIALIASING} 333 {$i renderdensity256.inc}480 {$i density256.inc} 334 481 else 482 {$DEFINE INCLUDE_RENDERDENSITY} 335 483 {$define PARAM_LINEARANTIALIASING} 336 484 {$define PARAM_ANTIALIASINGFACTOR} 337 {$i renderdensity256.inc}485 {$i density256.inc} 338 486 end else 339 487 begin 340 488 if optimised then 341 {$i renderdensity256.inc} 489 {$DEFINE INCLUDE_RENDERDENSITY} 490 {$i density256.inc} 342 491 else 492 {$DEFINE INCLUDE_RENDERDENSITY} 343 493 {$define PARAM_ANTIALIASINGFACTOR} 344 {$i renderdensity256.inc}494 {$i density256.inc} 345 495 end; 346 496 end; … … 383 533 begin 384 534 if (scan=nil) and (c.alpha=0) then exit; 385 If not shapeInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;535 If not BGRAShapeComputeMinMax(shapeInfo,minx,miny,maxx,maxy,bmp) then exit; 386 536 inter := shapeInfo.CreateIntersectionArray; 387 537 … … 532 682 info: TFillBorderEllipseInfo; 533 683 begin 534 if ( rx = 0) or (ry = 0) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then684 if ((rx = 0) and (ry = 0)) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then 535 685 exit; 536 686 info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w); … … 544 694 info: TFillBorderEllipseInfo; 545 695 begin 546 if ( rx = 0) or (ry = 0) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then696 if ((rx = 0) and (ry = 0)) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then 547 697 exit; 548 698 info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w); … … 587 737 end; 588 738 739 procedure TBGRAMultishapeFiller.InternalAddStroke( 740 const APoints: array of TPointF; AClosed: boolean; AData: Pointer); 741 var pts: ArrayOfTPointF; 742 begin 743 with TPathStrokeData(AData^) do 744 begin 745 if AClosed then 746 pts := Stroker.ComputePolygon(APoints, Width) 747 else 748 pts := Stroker.ComputePolylineAutoCycle(APoints, Width); 749 if Texture <> nil then 750 AddPolygon(pts, Texture) 751 else 752 AddPolygon(pts, Color); 753 end; 754 end; 755 589 756 constructor TBGRAMultishapeFiller.Create; 590 757 begin … … 635 802 end; 636 803 804 procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath; 805 AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker); 806 begin 807 AddPathStroke(APath,AffineMatrixIdentity,AColor,AWidth,AStroker); 808 end; 809 810 procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath; 811 ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker); 812 begin 813 AddPathStroke(APath,AffineMatrixIdentity,ATexture,AWidth,AStroker); 814 end; 815 816 procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath; 817 AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; 818 AStroker: TBGRACustomPenStroker); 819 var data: TPathStrokeData; 820 begin 821 data.Stroker := AStroker; 822 data.Color := AColor; 823 data.Texture := nil; 824 data.Width := AWidth; 825 APath.stroke(@InternalAddStroke, AMatrix, 0.1, @data); 826 end; 827 828 procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath; 829 AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; 830 AStroker: TBGRACustomPenStroker); 831 var data: TPathStrokeData; 832 begin 833 data.Stroker := AStroker; 834 data.Color := BGRAPixelTransparent; 835 data.Texture := ATexture; 836 data.Width := AWidth; 837 APath.stroke(@InternalAddStroke, AMatrix, 0.1, @data); 838 end; 839 840 procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath; AColor: TBGRAPixel); 841 begin 842 AddPolygon(APath.ToPoints, AColor); 843 end; 844 845 procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath; 846 ATexture: IBGRAScanner); 847 begin 848 AddPolygon(APath.ToPoints, ATexture); 849 end; 850 851 procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath; 852 AMatrix: TAffineMatrix; AColor: TBGRAPixel); 853 begin 854 AddPolygon(APath.ToPoints(AMatrix), AColor); 855 end; 856 857 procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath; 858 AMatrix: TAffineMatrix; ATexture: IBGRAScanner); 859 begin 860 AddPolygon(APath.ToPoints(AMatrix), ATexture); 861 end; 862 863 procedure TBGRAMultishapeFiller.AddPolylineStroke( 864 const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; 865 AStroker: TBGRACustomPenStroker); 866 begin 867 AddPolygon(AStroker.ComputePolyline(points,AWidth,AColor), AColor); 868 end; 869 870 procedure TBGRAMultishapeFiller.AddPolylineStroke( 871 const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; 872 AStroker: TBGRACustomPenStroker); 873 begin 874 AddPolygon(AStroker.ComputePolyline(points,AWidth), ATexture); 875 end; 876 877 procedure TBGRAMultishapeFiller.AddPolygonStroke( 878 const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; 879 AStroker: TBGRACustomPenStroker); 880 begin 881 AddPolygon(AStroker.ComputePolygon(points,AWidth), AColor); 882 end; 883 884 procedure TBGRAMultishapeFiller.AddPolygonStroke( 885 const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; 886 AStroker: TBGRACustomPenStroker); 887 begin 888 AddPolygon(AStroker.ComputePolygon(points,AWidth), ATexture); 889 end; 890 637 891 procedure TBGRAMultishapeFiller.AddTriangleLinearColor(pt1, pt2, pt3: TPointF; c1, c2, 638 892 c3: TBGRAPixel); 639 var 640 grad: TBGRAGradientTriangleScanner; 641 begin 642 grad := TBGRAGradientTriangleScanner.Create(pt1,pt2,pt3, c1,c2,c3); 643 AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent); 893 var grad: TBGRAGradientTriangleScanner; 894 begin 895 if (c1 = c2) and (c2 = c3) then 896 AddPolygon([pt1,pt2,pt3],c1) 897 else 898 begin 899 grad := TBGRAGradientTriangleScanner.Create(pt1,pt2,pt3, c1,c2,c3); 900 AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent); 901 end; 644 902 end; 645 903 … … 659 917 centerColor: TBGRAPixel; 660 918 begin 661 center := (pt1+pt2+pt3+pt4)*(1/4); 662 centerColor := GammaCompression( MergeBGRA(MergeBGRA(GammaExpansion(c1),GammaExpansion(c2)), 663 MergeBGRA(GammaExpansion(c3),GammaExpansion(c4))) ); 664 AddTriangleLinearColor(pt1,pt2,center, c1,c2,centerColor); 665 AddTriangleLinearColor(pt2,pt3,center, c2,c3,centerColor); 666 AddTriangleLinearColor(pt3,pt4,center, c3,c4,centerColor); 667 AddTriangleLinearColor(pt4,pt1,center, c4,c1,centerColor); 919 if (c1 = c2) and (c2 = c3) and (c3 = c4) then 920 AddPolygon([pt1,pt2,pt3,pt4],c1) 921 else 922 begin 923 center := (pt1+pt2+pt3+pt4)*(1/4); 924 centerColor := GammaCompression( MergeBGRA(MergeBGRA(GammaExpansion(c1),GammaExpansion(c2)), 925 MergeBGRA(GammaExpansion(c3),GammaExpansion(c4))) ); 926 AddTriangleLinearColor(pt1,pt2,center, c1,c2,centerColor); 927 AddTriangleLinearColor(pt2,pt3,center, c2,c3,centerColor); 928 AddTriangleLinearColor(pt3,pt4,center, c3,c4,centerColor); 929 AddTriangleLinearColor(pt4,pt1,center, c4,c1,centerColor); 930 end; 668 931 end; 669 932 670 933 procedure TBGRAMultishapeFiller.AddQuadLinearMapping(pt1, pt2, pt3, 671 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); 672 var 673 center: TPointF; 674 centerTex: TPointF; 675 begin 676 center := (pt1+pt2+pt3+pt4)*(1/4); 677 centerTex := (tex1+tex2+tex3+tex4)*(1/4); 678 AddTriangleLinearMapping(pt1,pt2,center, texture,tex1,tex2,centerTex); 679 AddTriangleLinearMapping(pt2,pt3,center, texture,tex2,tex3,centerTex); 680 AddTriangleLinearMapping(pt3,pt4,center, texture,tex3,tex4,centerTex); 681 AddTriangleLinearMapping(pt4,pt1,center, texture,tex4,tex1,centerTex); 934 pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; 935 ACulling: TFaceCulling); 936 var 937 mapping: TBGRAQuadLinearScanner; 938 begin 939 mapping := TBGRAQuadLinearScanner.Create(texture, 940 [tex1,tex2,tex3,tex4], 941 [pt1,pt2,pt3,pt4]); 942 mapping.Culling := ACulling; 943 AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3,pt4]),True,mapping,mapping,BGRAPixelTransparent); 682 944 end; 683 945 … … 891 1153 end; 892 1154 end else 893 {$I filldensity256.inc} 1155 {$DEFINE INCLUDE_FILLDENSITY} 1156 {$i density256.inc} 894 1157 end; 895 1158 … … 938 1201 for k := 0 to nbShapes-1 do 939 1202 begin 940 If shapes[k].info.ComputeMinMax(minx,miny,maxx,maxy,dest) then1203 If BGRAShapeComputeMinMax(shapes[k].info,minx,miny,maxx,maxy,dest) then 941 1204 begin 942 1205 shapes[k].bounds := rect(minx,miny,maxx+1,maxy+1); … … 1218 1481 EraseMode: boolean; LinearBlend: boolean); 1219 1482 var 1220 info: TFillBorderRoundRectInfo; 1221 begin 1222 if (rx = 0) or (ry = 0) or (w=0) then exit; 1483 info: TFillShapeInfo; 1484 oldLinear: boolean; 1485 begin 1486 if w=0 then exit; 1487 if ((rx=0) or (ry=0)) and not EraseMode then 1488 begin 1489 oldLinear := bmp.LinearAntialiasing; 1490 bmp.LinearAntialiasing := LinearBlend; 1491 bmp.RectangleAntialias(x1,y1,x2,y2,c,w); 1492 bmp.LinearAntialiasing := oldLinear; 1493 exit; 1494 end; 1223 1495 info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options); 1224 1496 FillShapeAntialias(bmp, info, c, EraseMode, nil, False, LinearBlend); … … 1231 1503 var 1232 1504 info: TFillBorderRoundRectInfo; 1233 begin 1234 if (rx = 0) or (ry = 0) or (w=0) then exit; 1505 oldLinear: Boolean; 1506 begin 1507 if w=0 then exit; 1508 if (rx=0) or (ry=0) then 1509 begin 1510 oldLinear := bmp.LinearAntialiasing; 1511 bmp.LinearAntialiasing := LinearBlend; 1512 bmp.RectangleAntialias(x1,y1,x2,y2,scan,w); 1513 bmp.LinearAntialiasing := oldLinear; 1514 exit; 1515 end; 1235 1516 info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options); 1236 1517 FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend); -
GraphicTest/Packages/bgrabitmap/bgrapolygonaliased.pas
r472 r494 431 431 432 432 begin 433 If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;433 If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit; 434 434 inter := polyInfo.CreateIntersectionArray; 435 435 … … 668 668 669 669 begin 670 If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;670 If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit; 671 671 inter := polyInfo.CreateIntersectionArray; 672 672 … … 735 735 736 736 begin 737 If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;737 If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit; 738 738 739 739 scanAtFunc := @texture.ScanAt; -
GraphicTest/Packages/bgrabitmap/bgraqtbitmap.pas
r452 r494 28 28 29 29 uses 30 Classes, SysUtils, BGRA DefaultBitmap, Graphics,31 GraphType ;30 Classes, SysUtils, BGRALCLBitmap, Graphics, 31 GraphType, BGRABitmapTypes; 32 32 33 33 type 34 34 { TBGRAQtBitmap } 35 35 36 TBGRAQtBitmap = class(TBGRA DefaultBitmap)36 TBGRAQtBitmap = class(TBGRALCLBitmap) 37 37 private 38 procedure SlowDrawTransparent(ABitmap: TBGRA DefaultBitmap;38 procedure SlowDrawTransparent(ABitmap: TBGRACustomBitmap; 39 39 ACanvas: TCanvas; ARect: TRect); 40 40 public … … 44 44 procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); override; 45 45 procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override; 46 procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;47 ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);48 override;49 46 procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); override; 50 47 end; … … 52 49 implementation 53 50 54 uses BGRABitmapTypes,LCLType,51 uses LCLType, 55 52 LCLIntf, IntfGraphics, 56 53 qtobjects, qt4, 57 54 FPImage; 58 55 59 procedure TBGRAQtBitmap.SlowDrawTransparent(ABitmap: TBGRA DefaultBitmap;56 procedure TBGRAQtBitmap.SlowDrawTransparent(ABitmap: TBGRACustomBitmap; 60 57 ACanvas: TCanvas; ARect: TRect); 61 58 begin … … 66 63 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); 67 64 var 68 Temp: TBGRA PtrBitmap;65 Temp: TBGRALCLPtrBitmap; 69 66 begin 70 Temp := TBGRA PtrBitmap.Create(AWidth, AHeight, AData);67 Temp := TBGRALCLPtrBitmap.Create(AWidth, AHeight, AData); 71 68 Temp.LineOrder := ALineOrder; 72 69 SlowDrawTransparent(Temp, ACanvas, Rect); … … 95 92 end; 96 93 97 procedure TBGRAQtBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect;98 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);99 var100 Temp: TBitmap;101 RawImage: TRawImage;102 BitmapHandle, MaskHandle: HBitmap;103 CreateSuccess: boolean;104 begin105 if (AHeight = 0) or (AWidth = 0) then106 exit;107 108 RawImage.Init;109 RawImage.Description.Init_BPP32_B8G8R8_BIO_TTB(AWidth, AHeight);110 RawImage.Description.LineOrder := ALineOrder;111 RawImage.Description.LineEnd := rileDWordBoundary;112 RawImage.Data := PByte(AData);113 RawImage.DataSize := AWidth * AHeight * Sizeof(TBGRAPixel);114 CreateSuccess := RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False);115 116 if not CreateSuccess then117 raise FPImageException.Create('Failed to create bitmap handle');118 Temp := TBitmap.Create;119 Temp.Handle := BitmapHandle;120 Temp.MaskHandle := MaskHandle;121 ACanvas.StretchDraw(Rect, Temp);122 Temp.Free;123 end;124 125 94 procedure TBGRAQtBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); 126 95 var … … 144 113 SrcY := y + Ofs.Y; 145 114 146 {$warning QT: recheck this}147 115 if (dcSource.vImage <> nil) and (dcSource.vImage.Handle <> nil) then 148 116 begin … … 157 125 end; 158 126 159 (*160 gdk_window_copy_area(dcDest.Drawable, dcDest.GC, 0, 0, dcSource.Drawable,161 SrcX, SrcY, Width, Height);162 *)163 127 LoadFromRawImage(bmp.RawImage, 255, True); 164 128 bmp.Free; -
GraphicTest/Packages/bgrabitmap/bgrareadbmp.pas
r472 r494 36 36 37 37 type 38 TBMPTransparencyOption = (toAuto, toTransparent, toOpaque); 38 39 39 40 { TBGRAReaderBMP } … … 56 57 FOutputHeight: integer; 57 58 FOriginalHeight: Integer; 59 FTransparencyOption: TBMPTransparencyOption; 58 60 FBuffer: packed array of byte; 59 61 FBufferPos, FBufferSize: integer; 60 62 FBufferStream: TStream; 63 FHasAlphaValues: boolean; 61 64 // SetupRead will allocate the needed buffers, and read the colormap if needed. 62 65 procedure SetupRead(nPalette, nRowBits: Integer; Stream : TStream); virtual; … … 77 80 procedure CloseReadBuffer; 78 81 function GetNextBufferByte: byte; 82 procedure MakeOpaque(Img: TFPCustomImage); 79 83 public 80 84 MinifyHeight,WantedHeight: integer; … … 83 87 property OriginalHeight: integer read FOriginalHeight; 84 88 property OutputHeight: integer read FOutputHeight; 89 property TransparencyOption: TBMPTransparencyOption read FTransparencyOption write FTransparencyOption; 85 90 end; 86 91 87 92 implementation 88 89 uses dialogs;90 93 91 94 type … … 117 120 end; 118 121 119 Constructor TBGRAReaderBMP.create;122 constructor TBGRAReaderBMP.Create; 120 123 121 124 begin 122 125 inherited create; 123 end; 124 125 Destructor TBGRAReaderBMP.Destroy; 126 FTransparencyOption := toTransparent; 127 end; 128 129 destructor TBGRAReaderBMP.Destroy; 126 130 127 131 begin … … 130 134 end; 131 135 132 Procedure TBGRAReaderBMP.FreeBufs;136 procedure TBGRAReaderBMP.FreeBufs; 133 137 134 138 begin … … 405 409 PrevSourceRow := SourceRow-SourceRowDelta; 406 410 if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then InitReadBuffer(Stream,2048); 411 FHasAlphaValues:= false; 407 412 while SourceRow <> SourceLastRow+SourceRowDelta do 408 413 begin … … 434 439 if percent<>prevPercent then Progress(psRunning,percent,false,Rect,'',continue); 435 440 end; 441 if not FHasAlphaValues and (TransparencyOption = toAuto) and (BFI.BitCount = 32) then 442 MakeOpaque(Img); 436 443 Progress(psEnding,100,false,Rect,'',continue); 437 except438 on ex:exception do439 ShowMessage(ex.Message);444 finally 445 if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then CloseReadBuffer; 446 FreeBufs; 440 447 end; 441 if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then CloseReadBuffer;442 FreeBufs;443 448 end; 444 449 … … 602 607 Var 603 608 Column : Integer; 604 609 c: TFPColor; 605 610 begin 606 611 Case BFI.BitCount of … … 628 633 img.colors[Column,Row]:=ExpandColor(PLongWord(LineBuf)[Column]) 629 634 else 630 img.colors[Column,Row]:=RGBAToFPColor(PColorRGBA(LineBuf)[Column]); 635 begin 636 if FTransparencyOption = toOpaque then 637 img.colors[Column,Row]:=RGBToFPColor(PColorRGB(PColorRGBA(LineBuf)+Column)^) 638 else 639 begin 640 c := RGBAToFPColor(PColorRGBA(LineBuf)[Column]); 641 if c.alpha <> 0 then FHasAlphaValues:= true; 642 img.colors[Column,Row]:= c; 643 end; 644 end; 631 645 end; 632 646 end; … … 672 686 for Column:=0 to img.Width-1 do 673 687 begin 674 {$IFDEF ENDIAN_BIG} 675 PDWord(PDest)^ := (PWord(PSrc)^ shl 16) or ((Psrc+2)^ shl 8) or $000000ff; 676 {$ELSE} 677 PDWord(PDest)^ := PWord(PSrc)^ or ((Psrc+2)^ shl 16) or $ff000000; 678 {$ENDIF} 688 PDest^ := BGRA((Psrc+2)^,(Psrc+1)^,(Psrc)^); 679 689 inc(PDest); 680 690 inc(PSrc,3); … … 689 699 inc(PDest); 690 700 end; 691 end else Move(LineBuf^, PDest^, img.Width*SizeOf(TBGRAPixel)); 701 end else 702 if FTransparencyOption = toOpaque then 703 begin 704 if TBGRAPixel_RGBAOrder then 705 begin 706 PSrc := LineBuf; 707 for Column:=0 to img.Width-1 do 708 begin 709 PDest^:= BGRA((PSrc)^,(PSrc+1)^,(PSrc+2)^); 710 inc(PDest); 711 Inc(PSrc,4); 712 end; 713 end 714 else 715 begin 716 PSrc := LineBuf; 717 for Column:=0 to img.Width-1 do 718 begin 719 PDest^:= BGRA((PSrc+2)^,(PSrc+1)^,(PSrc+1)^); 720 inc(PDest); 721 Inc(PSrc,4); 722 end; 723 end; 724 end else 725 begin 726 if TBGRAPixel_RGBAOrder then 727 begin 728 PSrc := LineBuf; 729 for Column:=0 to img.Width-1 do 730 begin 731 PDest^:= BGRA((PSrc)^,(PSrc+1)^,(PSrc+2)^,(PSrc+3)^); 732 if PDest^.alpha <> 0 then FHasAlphaValues:= true; 733 inc(PDest); 734 Inc(PSrc,4); 735 end; 736 end 737 else 738 begin 739 PSrc := LineBuf; 740 for Column:=0 to img.Width-1 do 741 begin 742 PDest^ := PBGRAPixel(PSrc)^; 743 if PDest^.alpha <> 0 then FHasAlphaValues:= true; 744 inc(PDest); 745 Inc(PSrc,4); 746 end; 747 end; 748 end; 692 749 end; 693 750 end; … … 741 798 end; 742 799 800 procedure TBGRAReaderBMP.MakeOpaque(Img: TFPCustomImage); 801 var c: TFPColor; 802 x,y: NativeInt; 803 begin 804 if Img is TBGRACustomBitmap then 805 TBGRACustomBitmap(Img).AlphaFill(255) 806 else 807 for y := 0 to Img.Height-1 do 808 for x := 0 to Img.Width-1 do 809 begin 810 c := Img.Colors[x,y]; 811 c.alpha := alphaOpaque; 812 Img.Colors[x,y] := c; 813 end; 814 end; 815 743 816 744 817 initialization -
GraphicTest/Packages/bgrabitmap/bgrareadbmpmiomap.pas
r472 r494 7 7 uses 8 8 Classes, SysUtils, FPimage, BGRABitmapTypes; 9 10 const 11 MioMapMagicValue = 'RL'; 12 MioMapTransparentColor = $F81F; 9 13 10 14 type … … 29 33 end; 30 34 35 function MioMapToBGRA(AColor: Word): TBGRAPixel; 36 function BGRAToMioMap(const AColor: TBGRAPixel): Word; 37 function MioMapToAlpha(AValue: Byte): Byte; 38 function AlphaToMioMap(AValue: Byte): Byte; 39 31 40 implementation 32 41 33 42 uses bufstream; 43 44 function MioMapToBGRA(AColor: Word): TBGRAPixel; 45 begin 46 if AColor = MioMapTransparentColor then 47 result := BGRAPixelTransparent 48 else 49 result := Color16BitToBGRA(AColor); 50 end; 51 52 function BGRAToMioMap(const AColor: TBGRAPixel): Word; 53 begin 54 if AColor.alpha < 7 then 55 result := MioMapTransparentColor 56 else 57 begin 58 result := BGRAToColor16Bit(AColor); 59 if result = MioMapTransparentColor then dec(result); 60 end; 61 end; 62 63 function MioMapToAlpha(AValue: Byte): Byte; 64 begin 65 result := AValue*255 div 32; 66 end; 67 68 function AlphaToMioMap(AValue: Byte): Byte; 69 begin 70 result := (AValue*32 + 64) div 255; 71 end; 34 72 35 73 { TBGRAReaderBmpMioMap } … … 41 79 fillchar({%H-}header,sizeof(header),0); 42 80 if stream.Read(header, sizeof(header))<> sizeof(header) then exit; 43 if header.magic <> 'RL'then exit;81 if header.magic <> MioMapMagicValue then exit; 44 82 header.format:= LEtoN(header.format); 45 83 header.width:= LEtoN(header.width); … … 64 102 begin 65 103 colorValue := LEtoN(mioPalette[i]); 66 if colorValue = $F81F then 67 result[i] := BGRAPixelTransparent 68 else 69 result[i] := BGRA( ((colorValue and $F800) shr 11)*255 div 31, 70 ((colorValue and $07e0) shr 5)*255 div 63, 71 (colorValue and $001f)*255 div 31); 104 result[i] := MioMapToBGRA(colorValue); 72 105 end; 73 106 for i := nbColorsRead to nbColors-1 do … … 78 111 Stream.Read(alphaPalette[0],nbColors); 79 112 for i := 0 to nbColors-1 do 80 if mioPalette[i] <> $F81Fthen81 result[i].alpha := alphaPalette[i]*255 div 32;113 if mioPalette[i] <> MioMapTransparentColor then 114 result[i].alpha := MioMapToAlpha(alphaPalette[i]); 82 115 end; 83 116 end; -
GraphicTest/Packages/bgrabitmap/bgrareadgif.pas
r472 r494 338 338 Every := 4; 339 339 end; 340 4 : begin 340 else{4} 341 begin 341 342 Row := 1; 342 343 Every := 2; -
GraphicTest/Packages/bgrabitmap/bgrareadico.pas
r472 r494 2 2 3 3 {$mode objfpc}{$H+} 4 {$i bgrabitmap.inc} 4 5 5 6 interface … … 14 15 TBGRAReaderIco = class(TFPCustomImageReader) 15 16 protected 16 procedure InternalRead( Str: TStream;Img: TFPCustomImage); override;17 procedure InternalRead({%H-}Str: TStream; {%H-}Img: TFPCustomImage); override; 17 18 function InternalCheck(Str: TStream): boolean; override; 18 19 public … … 22 23 implementation 23 24 24 uses BGRABitmapTypes , Graphics;25 uses BGRABitmapTypes{$IFDEF BGRABITMAP_USE_LCL}, Graphics{$ENDIF}; 25 26 26 27 { TBGRAReaderIco } 27 28 28 29 procedure TBGRAReaderIco.InternalRead(Str: TStream; Img: TFPCustomImage); 30 {$IFDEF BGRABITMAP_USE_LCL} 29 31 var ico: TIcon; i,bestIdx: integer; 30 32 height,width: word; format:TPixelFormat; … … 57 59 begin 58 60 ico.Current := bestIdx; 59 with Img as TBGRACustomBitmap do 60 begin 61 SetSize(bestWidth,bestHeight); 62 GetImageFromCanvas(ico.Canvas,0,0); 63 end; 61 (Img as TBGRACustomBitmap).Assign(ico); 64 62 end; 65 63 finally … … 67 65 end; 68 66 end; 67 {$ELSE} 68 begin 69 raise exception.create('Not implemented'); 70 end; 71 {$ENDIF} 69 72 70 73 function TBGRAReaderIco.InternalCheck(Str: TStream): boolean; -
GraphicTest/Packages/bgrabitmap/bgrareadjpeg.pas
r472 r494 1 unit bgrareadjpeg;1 unit BGRAReadJpeg; 2 2 3 3 {$mode objfpc}{$H+} … … 9 9 10 10 type 11 TJPEGScale = FPReadJPEG.TJPEGScale; 12 TJPEGReadPerformance = FPReadJPEG.TJPEGReadPerformance; 11 13 14 const 15 jsFullSize = FPReadJPEG.jsFullSize; 16 jsHalf = FPReadJPEG.jsHalf; 17 jsQuarter = FPReadJPEG.jsQuarter; 18 jsEighth = FPReadJPEG.jsEighth; 19 20 jpBestQuality = FPReadJPEG.jpBestQuality; 21 jpBestSpeed = FPReadJPEG.jpBestSpeed; 22 23 type 12 24 { TBGRAReaderJpeg } 13 25 -
GraphicTest/Packages/bgrabitmap/bgrareadlzp.pas
r472 r494 296 296 for x := w-1 downto 0 do 297 297 begin 298 {$IFDEF ENDIAN_LITTLE} 299 PDWord(PDest)^ := PCurBlue^ or (PCurGreen^ shl 8) or (PCurRed^ shl 16) or $ff000000; 300 {$ELSE} 301 PDWord(PDest)^ := (PCurBlue^ shl 24) or (PCurGreen^ shl 16) or (PCurRed^ shl 8) or $ff; 302 {$ENDIF} 298 PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^); 303 299 inc(PCurBlue); 304 300 inc(PCurGreen); … … 317 313 else 318 314 begin 319 {$IFDEF ENDIAN_LITTLE} 320 PDWord(PDest)^ := PCurBlue^ or (PCurGreen^ shl 8) or (PCurRed^ shl 16) or (PCurAlpha^ shl 24); 321 {$ELSE} 322 PDWord(PDest)^ := (PCurBlue^ shl 24) or (PCurGreen^ shl 16) or (PCurRed^ shl 8) or PCurAlpha^; 323 {$ENDIF} 315 PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^,PCurAlpha^); 324 316 inc(PCurBlue); 325 317 inc(PCurGreen); -
GraphicTest/Packages/bgrabitmap/bgrareadpng.pas
r472 r494 56 56 //CFmt : TColorFormat; // format of the colors to convert from 57 57 StartX,StartY, DeltaX,DeltaY, StartPass,EndPass : integer; // number and format of passes 58 FSwitchLine, FCurrentLine, FPreviousLine : pByteArray;59 58 FPalette : TFPPalette; 60 59 FSetPixel : TSetPixelProc; … … 100 99 DataIndex : longword; 101 100 DataBytes : TColorData; 102 function CurrentLine(x:longword) : byte;103 function PrevSample (x:longword): byte;104 function PreviousLine (x:longword) : byte;105 function PrevLinePrevSample (x:longword): byte;106 101 procedure HandleChunk; virtual; 107 102 procedure HandlePalette; virtual; … … 109 104 function CalcX (relX:integer) : integer; 110 105 function CalcY (relY:integer) : integer; 111 function CalcColor : TColorData;106 function CalcColor(const ScanLine : PByteArray): TColorData; 112 107 procedure HandleScanLine (const y : integer; const ScanLine : PByteArray); virtual; 113 108 procedure BGRAHandleScanLine(const y: integer; const ScanLine: PByteArray); 114 109 procedure BGRAHandleScanLineTr(const y: integer; const ScanLine: PByteArray); 115 110 procedure DoDecompress; virtual; 116 function DoFilter(LineFilter:byte;index:longword; b:byte) : byte; virtual;117 111 procedure SetPalettePixel (x,y:integer; const CD : TColordata); 118 112 procedure SetPalColPixel (x,y:integer; const CD : TColordata); … … 381 375 end; 382 376 383 function TBGRAReaderPNG.CurrentLine(x:longword):byte;384 begin385 result := FCurrentLine^[x];386 end;387 388 function TBGRAReaderPNG.PrevSample (x:longword): byte;389 begin390 if x < byteWidth then391 result := 0392 else393 result := FCurrentLine^[x - bytewidth];394 end;395 396 function TBGRAReaderPNG.PreviousLine (x:longword) : byte;397 begin398 result := FPreviousline^[x];399 end;400 401 function TBGRAReaderPNG.PrevLinePrevSample (x:longword): byte;402 begin403 if x < byteWidth then404 result := 0405 else406 result := FPreviousLine^[x - bytewidth];407 end;408 409 function TBGRAReaderPNG.DoFilter(LineFilter:byte;index:longword; b:byte) : byte;410 var diff : byte;411 procedure FilterSub;412 begin413 diff := PrevSample(index);414 end;415 procedure FilterUp;416 begin417 diff := PreviousLine(index);418 end;419 procedure FilterAverage;420 var l, p : word;421 begin422 l := PrevSample(index);423 p := PreviousLine(index);424 diff := (l + p) div 2;425 end;426 procedure FilterPaeth;427 var dl, dp, dlp : word; // index for previous and distances for:428 l, p, lp : byte; // r:predictor, Left, Previous, LeftPrevious429 r : integer;430 begin431 l := PrevSample(index);432 lp := PrevLinePrevSample(index);433 p := PreviousLine(index);434 r := integer(l) + integer(p) - integer(lp);435 dl := abs (r - l);436 dlp := abs (r - lp);437 dp := abs (r - p);438 if (dl <= dp) and (dl <= dlp) then439 diff := l440 else if dp <= dlp then441 diff := p442 else443 diff := lp;444 end;445 begin446 case LineFilter of447 0 : diff := 0;448 1 : FilterSub;449 2 : FilterUp;450 3 : FilterAverage;451 4 : FilterPaeth;452 end;453 result := (b + diff) mod $100;454 end;455 456 377 function TBGRAReaderPNG.DecideSetPixel : TSetPixelProc; 457 378 begin … … 488 409 end; 489 410 490 function TBGRAReaderPNG.CalcColor : TColorData;411 function TBGRAReaderPNG.CalcColor(const ScanLine : PByteArray): TColorData; 491 412 var cd : longword; 492 413 r : word; … … 499 420 begin 500 421 p := @Databytes; 501 p^ := 0; 502 for r:=0 to bytewidth-2 do 422 for r:=0 to bytewidth shr 1 - 1 do 503 423 begin 504 inc(p); 505 p^:=FCurrentLine^[Dataindex+r]; 424 p^ := ScanLine^[Dataindex+(r shl 1)+1]; 425 (p+1)^ := ScanLine^[Dataindex+(r shl 1)]; 426 inc(p,2); 506 427 end; 507 428 end 508 else move ( FCurrentLine^[DataIndex], Databytes, bytewidth);429 else move (ScanLine^[DataIndex], Databytes, bytewidth); 509 430 {$IFDEF ENDIAN_BIG} 510 431 Databytes:=swap(Databytes); … … 586 507 for rx := 0 to ScanlineLength[CurrentPass]-1 do 587 508 begin 588 c := CalcColor ;509 c := CalcColor(ScanLine); 589 510 FSetPixel (x,y,c); 590 511 Inc(X, deltaX); … … 666 587 for rx := 0 to ScanlineLength[CurrentPass]-1 do 667 588 begin 668 c := CalcColor ;589 c := CalcColor(ScanLine); 669 590 FSetPixel (x,y,c); 670 591 Inc(X, deltaX); … … 767 688 for rx := 0 to ScanlineLength[CurrentPass]-1 do 768 689 begin 769 c := CalcColor ;690 c := CalcColor(ScanLine); 770 691 FSetPixel (x,y,c); 771 692 Inc(X, deltaX); … … 934 855 c := c + (c shl 2); 935 856 c := c + (c shl 4); 936 with result do 937 begin 938 red := c; 939 green := c; 940 blue := c; 941 alpha := 255; 942 end; 857 result := BGRA(c,c,c); 943 858 end; 944 859 … … 948 863 c := CD and $F; 949 864 c := c + (c shl 4); 950 with result do 951 begin 952 red := c; 953 green := c; 954 blue := c; 955 alpha := 255; 956 end; 865 result := BGRA(c,c,c); 957 866 end; 958 867 … … 961 870 begin 962 871 c := CD and $FF; 963 with result do 964 begin 965 red := c; 966 green := c; 967 blue := c; 968 alpha := 255; 969 end; 872 result := BGRA(c,c,c); 970 873 end; 971 874 … … 974 877 begin 975 878 c := (CD shr 8) and $FF; 976 with result do 977 begin 978 red := c; 979 green := c; 980 blue := c; 981 alpha := 255; 982 end; 879 result := BGRA(c,c,c); 983 880 end; 984 881 … … 987 884 begin 988 885 c := CD and $00FF; 989 with result do 990 begin 991 red := c; 992 green := c; 993 blue := c; 994 alpha := (CD shr 8) and $FF; 995 end; 886 result := BGRA(c,c,c,(CD shr 8) and $FF); 996 887 end; 997 888 … … 1000 891 begin 1001 892 c := (CD shr 8) and $FF; 1002 with result do 1003 begin 1004 red := c; 1005 green := c; 1006 blue := c; 1007 alpha := (CD shr 24) and $FF; 1008 end; 893 result := BGRA(c,c,c,(CD shr 24) and $FF); 1009 894 end; 1010 895 … … 1013 898 begin 1014 899 temp := CD; 1015 temp := ((temp and $ff) shl 16) or 1016 (temp and $ff00) or ((temp shr 16) and $ff) or 1017 $ff000000; 1018 {$IFDEF ENDIAN_BIG} 1019 DWord(result) := swap(temp); 1020 {$ELSE} 1021 DWord(result) := temp; 1022 {$ENDIF} 900 result := BGRA(temp and $ff, (temp shr 8) and $ff, (temp shr 16) and $ff); 1023 901 end; 1024 902 1025 903 function TBGRAReaderPNG.BGRAColorColor16(const CD: TColorData): TBGRAPixel; 1026 904 begin 1027 with result do 1028 begin 1029 red := CD shr 8 and $FF; 1030 green := (CD shr 24) and $FF; 1031 blue := (CD shr 40) and $FF; 1032 alpha := 255; 1033 end; 905 result := BGRA(CD shr 8 and $FF,(CD shr 24) and $FF,(CD shr 40) and $FF); 1034 906 end; 1035 907 … … 1038 910 begin 1039 911 temp := CD; 1040 temp := ((temp and $ff) shl 16) or 1041 (temp and $ff00) or ((temp shr 16) and $ff) or 1042 (temp and $ff000000); 1043 {$IFDEF ENDIAN_BIG} 1044 DWord(result) := swap(temp); 1045 {$ELSE} 1046 DWord(result) := temp; 1047 {$ENDIF} 912 result := BGRA(temp and $ff, (temp shr 8) and $ff, (temp shr 16) and $ff, temp shr 24); 1048 913 end; 1049 914 1050 915 function TBGRAReaderPNG.BGRAColorColorAlpha16(const CD: TColorData): TBGRAPixel; 1051 916 begin 1052 with result do 1053 begin 1054 red := (CD shr 8) and $FF; 1055 green := (CD shr 24) and $FF; 1056 blue := (CD shr 40) and $FF; 1057 alpha := (CD shr 56) and $FF; 1058 end; 917 result := BGRA(CD shr 8 and $FF,(CD shr 24) and $FF,(CD shr 40) and $FF, CD shr 56); 1059 918 end; 1060 919 … … 1185 1044 end; 1186 1045 1046 procedure FilterSub(p: PByte; Count: NativeInt; bw: NativeInt); 1047 begin 1048 inc(p,bw); 1049 dec(Count,bw); 1050 while Count > 0 do 1051 begin 1052 {$push}{$r-} 1053 p^ += (p-bw)^; 1054 {$pop} 1055 inc(p); 1056 dec(Count); 1057 end; 1058 end; 1059 1060 procedure FilterUp(p,pPrev: PByte; Count: NativeUInt); 1061 var Count4: NativeInt; 1062 begin 1063 Count4 := Count shr 2; 1064 dec(Count, Count4 shl 2); 1065 while Count4 > 0 do 1066 begin 1067 {$push}{$r-} 1068 PDWord(p)^ := (((PDWord(pPrev)^ and $00FF00FF) + (PDWord(p)^ and $00FF00FF)) and $00FF00FF) 1069 or (((PDWord(pPrev)^ and $FF00FF00) + (PDWord(p)^ and $FF00FF00)) and $FF00FF00); 1070 {$pop} 1071 inc(p,4); 1072 inc(pPrev,4); 1073 dec(Count4); 1074 end; 1075 while Count > 0 do 1076 begin 1077 {$push}{$r-} 1078 p^ += pPrev^; 1079 {$pop} 1080 1081 inc(p); 1082 inc(pPrev); 1083 dec(Count); 1084 end; 1085 end; 1086 1087 procedure FilterAverage(p,pPrev: PByte; Count: NativeUInt; bw: NativeInt); 1088 var CountBW: NativeInt; 1089 begin 1090 CountBW := bw; 1091 dec(Count,CountBW); 1092 while CountBW > 0 do 1093 begin 1094 {$push}{$r-} 1095 p^ += pPrev^ shr 1; 1096 {$pop} 1097 inc(p); 1098 inc(pPrev); 1099 dec(CountBW); 1100 end; 1101 1102 while Count > 0 do 1103 begin 1104 {$push}{$r-} 1105 p^ += (pPrev^+(p-bw)^) shr 1; 1106 {$pop} 1107 inc(p); 1108 inc(pPrev); 1109 dec(Count); 1110 end; 1111 end; 1112 1113 procedure FilterPaeth(p,pPrev: PByte; Count: NativeUInt; bw: NativeInt); 1114 var 1115 rx, dl, dp, dlp : NativeInt; 1116 diag,left: NativeUInt; 1117 begin 1118 for rx := 0 to bw-1 do 1119 begin 1120 {$push}{$r-} 1121 p^ += pPrev^; 1122 {$pop} 1123 inc(p); 1124 inc(pPrev); 1125 end; 1126 dec(Count,bw); 1127 while Count > 0 do 1128 begin 1129 diag := (pPrev-bw)^; 1130 left := (p - bw)^; 1131 dl := pPrev^ - NativeInt(diag); 1132 dp := NativeInt(left) - NativeInt(diag); 1133 dlp := abs(dl+dp); 1134 if dl < 0 then dl := -dl; 1135 if dp < 0 then dp := -dp; 1136 {$push}{$r-} 1137 if dp <= dlp then 1138 begin 1139 if dl <= dp then 1140 p^ += left 1141 else 1142 p^ += pPrev^ 1143 end 1144 else 1145 if dl <= dlp then 1146 p^ += left 1147 else 1148 p^ += diag; 1149 {$pop} 1150 inc(p); 1151 inc(pPrev); 1152 dec(Count); 1153 end; 1154 end; 1155 1187 1156 procedure Decode; 1188 var y, rp, ry, rx, l : integer;1157 var y, rp, ry, l : NativeInt; 1189 1158 lf : byte; 1159 switchLine, currentLine, previousLine : pByteArray; 1190 1160 begin 1191 1161 FSetPixel := DecideSetPixel; … … 1215 1185 if (l>0) then 1216 1186 begin 1217 GetMem ( FPreviousLine, l);1218 GetMem ( FCurrentLine, l);1219 fillchar ( FCurrentLine^,l,0);1187 GetMem (previousLine, l); 1188 GetMem (currentLine, l); 1189 fillchar (currentLine^,l,0); 1220 1190 try 1221 1191 for ry := 0 to CountScanlines[rp]-1 do 1222 1192 begin 1223 FSwitchLine := FCurrentLine;1224 FCurrentLine := FPreviousLine;1225 FPreviousLine := FSwitchLine;1226 Y := CalcY(ry);1193 switchLine := currentLine; 1194 currentLine := previousLine; 1195 previousLine := switchLine; 1196 Y := StartY + (ry * deltaY); 1227 1197 lf := 0; 1228 1198 Decompress.Read (lf, sizeof(lf)); 1229 Decompress.Read (FCurrentLine^, l); 1230 if lf <> 0 then // Do nothing when there is no filter used 1231 for rx := 0 to l-1 do 1232 FCurrentLine^[rx] := DoFilter (lf, rx, FCurrentLine^[rx]); 1199 Decompress.Read (currentLine^, l); 1200 1201 case lf of 1202 1: FilterSub(PByte(currentLine), l, ByteWidth); 1203 2: FilterUp(PByte(currentLine), PByte(previousLine), l); 1204 3: FilterAverage(PByte(currentLine), PByte(previousLine), l, ByteWidth); 1205 4: FilterPaeth(PByte(currentLine), PByte(previousLine), l, ByteWidth); 1206 end; 1207 1233 1208 if FVerticalShrinkShr <> 0 then 1234 1209 begin 1235 1210 if (y and FVerticalShrinkMask) = 0 then 1236 FHandleScanLine (y shr FVerticalShrinkShr, FCurrentLine);1211 FHandleScanLine (y shr FVerticalShrinkShr, currentLine); 1237 1212 end else 1238 FHandleScanLine (y, FCurrentLine);1213 FHandleScanLine (y, currentLine); 1239 1214 end; 1240 1215 finally 1241 freemem ( FPreviousLine);1242 freemem ( FCurrentLine);1216 freemem (previousLine); 1217 freemem (currentLine); 1243 1218 end; 1244 1219 end; -
GraphicTest/Packages/bgrabitmap/bgrareadtga.pas
r472 r494 116 116 for Col:=Img.Width-1 downto 0 do 117 117 begin 118 PWord(PDest)^ := PWord(P)^; 119 (PByte(PDest)+2)^ := (PByte(P)+2)^; 120 (PByte(PDest)+3)^ := 255; 118 PDest^ := BGRA((P+2)^,(P+1)^,P^); 121 119 inc(Pdest); 122 120 Inc(p,3); … … 129 127 inc(P); 130 128 Value:=value or (P[0] shl 8); 131 With PDest^ do 132 begin 133 Red:=((value)shr 10) shl 3; 134 Green:=((value)shr 5) shl 3; 135 Blue:=((value)) shl 3; 136 end; 129 PDest^ := BGRA(((value)shr 10) shl 3,((value)shr 5) shl 3,((value)) shl 3); 137 130 Inc(PDest); 138 131 Inc(P); -
GraphicTest/Packages/bgrabitmap/bgrareadxpm.pas
r472 r494 22 22 implementation 23 23 24 uses BGRABitmapTypes , Dialogs;24 uses BGRABitmapTypes; 25 25 26 26 { TBGRAReaderXPM } -
GraphicTest/Packages/bgrabitmap/bgraresample.pas
r472 r494 97 97 implementation 98 98 99 uses GraphType,Math, BGRABlend;99 uses Math, BGRABlend; 100 100 101 101 function SimpleStretch(bmp: TBGRACustomBitmap; -
GraphicTest/Packages/bgrabitmap/bgrascene3d.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, BGRABitmapTypes, BGRAColorInt, BGRASSE, BGRAMatrix3D; 8 Classes, SysUtils, BGRABitmapTypes, BGRAColorInt, 9 BGRASSE, BGRAMatrix3D, 10 BGRASceneTypes, BGRARenderer3D; 9 11 10 12 type 11 13 TProjection3D = BGRAMatrix3D.TProjection3D; 12 TBox3D = record 13 min,max: TPoint3D; 14 end; 15 16 TLightingNormal3D = (lnNone, lnFace, lnVertex, lnFaceVertexMix); 17 TLightingInterpolation3D = (liLowQuality, liSpecularHighQuality, liAlwaysHighQuality); 18 TAntialiasingMode3D = (am3dNone, am3dMultishape, am3dResample); 19 TPerspectiveMode3D = (pmLinearMapping, pmPerspectiveMapping, pmZBuffer); 20 21 TRenderingOptions = record 22 LightingInterpolation: TLightingInterpolation3D; 23 AntialiasingMode: TAntialiasingMode3D; 24 AntialiasingResampleLevel: integer; 25 PerspectiveMode: TPerspectiveMode3D; 26 TextureInterpolation: boolean; 27 MinZ: single; 28 end; 29 30 PSceneLightingContext = ^TSceneLightingContext; 31 TSceneLightingContext = packed record 32 basic: TBasicLightingContext; 33 {128} diffuseColor, {144} specularColor: TColorInt65536; 34 {160} vL, {176} dummy: TPoint3D_128; 35 {192} vH: TPoint3D_128; 36 {208} lightness: integer; 37 {212} material : TObject; 38 LightThroughFactor: single; 39 LightThrough: LongBool; 40 SaturationLow: integer; 41 SaturationLowF: single; 42 SaturationHigh: integer; 43 SaturationHighF: single; 44 end; 45 46 TBGRAScene3D = class; 47 48 {$i bgrascene3Dinterface.inc} 14 TLightingNormal3D = BGRASceneTypes.TLightingNormal3D; 15 TLightingInterpolation3D = BGRASceneTypes.TLightingInterpolation3D; 16 TAntialiasingMode3D = BGRASceneTypes.TAntialiasingMode3D; 17 TPerspectiveMode3D = BGRASceneTypes.TPerspectiveMode3D; 18 TRenderingOptions = BGRASceneTypes.TRenderingOptions; 19 20 IBGRAVertex3D = BGRASceneTypes.IBGRAVertex3D; 21 IBGRANormal3D = BGRASceneTypes.IBGRANormal3D; 22 IBGRALight3D = BGRASceneTypes.IBGRALight3D; 23 IBGRADirectionalLight3D = BGRASceneTypes.IBGRADirectionalLight3D; 24 IBGRAPointLight3D = BGRASceneTypes.IBGRAPointLight3D; 25 IBGRAMaterial3D = BGRASceneTypes.IBGRAMaterial3D; 26 IBGRAFace3D = BGRASceneTypes.IBGRAFace3D; 27 IBGRAPart3D = BGRASceneTypes.IBGRAPart3D; 28 IBGRAObject3D = BGRASceneTypes.IBGRAObject3D; 29 30 arrayOfIBGRAVertex3D = BGRASceneTypes.arrayOfIBGRAVertex3D; 31 32 const 33 lnNone = BGRASceneTypes.lnNone; 34 lnFace = BGRASceneTypes.lnFace; 35 lnVertex = BGRASceneTypes.lnVertex; 36 lnFaceVertexMix = BGRASceneTypes.lnFaceVertexMix; 37 38 liLowQuality = BGRASceneTypes.liLowQuality; 39 liSpecularHighQuality = BGRASceneTypes.liSpecularHighQuality; 40 liAlwaysHighQuality = BGRASceneTypes.liAlwaysHighQuality; 41 42 am3dNone = BGRASceneTypes.am3dNone; 43 am3dMultishape = BGRASceneTypes.am3dMultishape; 44 am3dResample = BGRASceneTypes.am3dResample; 45 46 pmLinearMapping = BGRASceneTypes.pmLinearMapping; 47 pmPerspectiveMapping = BGRASceneTypes.pmPerspectiveMapping; 48 pmZBuffer = BGRASceneTypes.pmZBuffer; 49 49 50 50 type 51 52 { TCamera3D } 53 54 TCamera3D = class 55 private 56 procedure ComputeMatrix; 57 function GetLookWhere: TPoint3D; 58 function GetMatrix: TMatrix3D; 59 function GetViewPoint: TPoint3D; 60 procedure SetMatrix(AValue: TMatrix3D); 61 procedure SetViewPoint(AValue: TPoint3D); 62 protected 63 FMatrix: TMatrix3D; 64 FMatrixComputed: boolean; 65 FViewPoint: TPoint3D_128; 66 FLookWhere, FTopDir: TPoint3D_128; 67 public 68 procedure LookAt(AWhere: TPoint3D; ATopDir: TPoint3D); 69 procedure LookDown(angleDeg: single); 70 procedure LookLeft(angleDeg: single); 71 procedure LookRight(angleDeg: single); 72 procedure LookUp(angleDeg: single); 73 property ViewPoint: TPoint3D read GetViewPoint write SetViewPoint; 74 property LookWhere: TPoint3D read GetLookWhere; 75 property Matrix: TMatrix3D read GetMatrix write SetMatrix; 76 end; 77 51 78 { TBGRAScene3D } 52 79 53 80 TBGRAScene3D = class 54 81 private 55 FSurface: TBGRACustomBitmap; 56 FViewCenter: TPointF; 57 FAutoViewCenter: boolean; 82 FSurface: TBGRACustomBitmap; //destination of software renderer 83 FViewCenter: TPointF; //where origin is drawn 84 FAutoViewCenter: boolean; //use middle of the screen 85 FZoom: TPointF; //how much the drawing is zoomed 86 FAutoZoom: Boolean; //display 1 as 80% of surface size 87 FProjection: TProjection3D; //current projection 88 FRenderedFaceCount: integer; //current counter of rendered faces 89 90 FCamera: TCamera3D; 91 58 92 FObjects: array of IBGRAObject3D; 59 93 FObjectCount: integer; 60 94 FMaterials: array of IBGRAMaterial3D; 61 95 FMaterialCount: integer; 62 FMatrix: TMatrix3D; 63 FViewPoint: TPoint3D_128; 64 FLookWhere, FTopDir: TPoint3D_128; 65 FZoom: TPointF; 66 FAutoZoom: Boolean; 67 FLights: TList; 68 FAmbiantLightness: integer; 69 FAmbiantLightColor: TColorInt65536; 70 FRenderedFaceCount: integer; 71 FProjection: TProjection3D; 96 FDefaultMaterial : IBGRAMaterial3D; 97 98 FAmbiantLightColorF: TColorF; //lightness without light sources 99 FLights: TList; //individual light sources 100 72 101 function GetAmbiantLightColorF: TColorF; 73 102 function GetAmbiantLightness: single; … … 91 120 procedure SetViewPoint(const AValue: TPoint3D); 92 121 procedure ComputeView(ScaleX,ScaleY: single); 93 function ComputeCoordinate(ASceneCoord: TPoint3D_128; APart: IBGRAPart3D): TPointF; overload; 94 function ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF; overload; 95 procedure ComputeLight; 96 procedure ComputeMatrix; 122 function ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF; 97 123 procedure AddObject(AObj: IBGRAObject3D); 98 124 procedure AddLight(ALight: TObject); 99 125 procedure AddMaterial(AMaterial: IBGRAMaterial3D); 100 126 procedure Init; 101 procedure InternalRender(ASurface: TBGRACustomBitmap; AAntialiasingMode: TAntialiasingMode3D; GlobalScale: single); virtual;102 127 103 128 protected 104 function ApplyLightingWithLightness(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual; 105 function ApplyLightingWithDiffuseColor(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual; 106 function ApplyLightingWithDiffuseAndSpecularColor(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual; 107 function ApplyLightingWithAmbiantLightnessOnly(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual; 108 function ApplyNoLighting(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual; 129 FRenderer: TCustomRenderer3D; 130 FMaterialLibrariesFetched: array of string; 131 FTexturesFetched: array of record 132 Name: string; 133 Bitmap: TBGRACustomBitmap; 134 end; 109 135 procedure UseMaterial(AMaterialName: string; AFace: IBGRAFace3D); virtual; 110 function FetchTexture({%H-}AName: string; out texSize: TPointF): IBGRAScanner; virtual; 136 function LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap; virtual; 137 function FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; virtual; 138 procedure HandleFetchException(AException: Exception); virtual; 139 procedure DoRender; virtual; 140 procedure DoClear; virtual; 141 function GetRenderWidth: integer; 142 function GetRenderHeight: integer; 143 procedure OnMaterialTextureChanged({%H-}ASender: TObject); virtual; 144 procedure SetDefaultMaterial(AValue: IBGRAMaterial3D); 145 procedure InvalidateMaterial; 111 146 112 147 public 113 148 DefaultLightingNormal: TLightingNormal3D; 114 DefaultMaterial : IBGRAMaterial3D;115 149 RenderingOptions: TRenderingOptions; 116 150 UnknownColor: TBGRAPixel; 151 FetchDirectory: string; 152 FetchThrowsException: boolean; 117 153 118 154 constructor Create; … … 120 156 destructor Destroy; override; 121 157 procedure Clear; virtual; 158 function FetchObject(AName: string; SwapFacesOrientation: boolean = true): IBGRAObject3D; 159 procedure FetchMaterials(ALibraryName: string); virtual; 122 160 function LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D; 123 161 function LoadObjectFromFileUTF8(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D; … … 132 170 procedure LookDown(angleDeg: single); 133 171 procedure Render; virtual; 172 procedure Render(ARenderer: TCustomRenderer3D); 134 173 function CreateObject: IBGRAObject3D; overload; 135 174 function CreateObject(ATexture: IBGRAScanner): IBGRAObject3D; overload; … … 154 193 procedure ForEachVertex(ACallback: TVertex3DCallback); 155 194 procedure ForEachFace(ACallback: TFace3DCallback); 195 function MakeLightList: TList; 196 156 197 property ViewCenter: TPointF read GetViewCenter write SetViewCenter; 157 198 property AutoViewCenter: boolean read FAutoViewCenter write SetAutoViewCenter; … … 173 214 property Material[AIndex: integer] : IBGRAMaterial3D read GetMaterial; 174 215 property MaterialCount: integer read FMaterialCount; 216 property Camera: TCamera3D read FCamera; 217 property DefaultMaterial: IBGRAMaterial3D read FDefaultMaterial write SetDefaultMaterial; 175 218 end; 176 219 177 220 implementation 178 221 179 uses BGRAPolygon, BGRAPolygonAliased, BGRACoordPool3D, BGRAResample, 180 lazutf8classes; 222 uses BGRACoordPool3D, BGRAUTF8; 181 223 182 224 {$i lightingclasses3d.inc} 183 225 {$i vertex3d.inc} 184 226 {$i face3d.inc} 185 186 type187 { TBGRAObject3D }188 189 TBGRAObject3D = class(TInterfacedObject,IBGRAObject3D)190 private191 FColor: TBGRAPixel;192 FLight: Single;193 FTexture: IBGRAScanner;194 FMainPart: IBGRAPart3D;195 FFaces: array of IBGRAFace3D;196 FFaceCount: integer;197 FLightingNormal : TLightingNormal3D;198 FParentLighting: boolean;199 FMaterial: IBGRAMaterial3D;200 FScene: TBGRAScene3D;201 procedure AddFace(AFace: IBGRAFace3D);202 public203 constructor Create(AScene: TBGRAScene3D);204 destructor Destroy; override;205 procedure Clear;206 207 function AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;208 function AddFace(const AVertices: array of IBGRAVertex3D; ABiface: boolean): IBGRAFace3D;209 function AddFace(const AVertices: array of IBGRAVertex3D; ATexture: IBGRAScanner): IBGRAFace3D;210 function AddFace(const AVertices: array of IBGRAVertex3D; AColor: TBGRAPixel): IBGRAFace3D;211 function AddFace(const AVertices: array of IBGRAVertex3D; AColors: array of TBGRAPixel): IBGRAFace3D;212 function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;213 procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D);214 function GetColor: TBGRAPixel;215 function GetLight: Single;216 function GetTexture: IBGRAScanner;217 function GetMainPart: IBGRAPart3D;218 function GetLightingNormal: TLightingNormal3D;219 function GetParentLighting: boolean;220 function GetFace(AIndex: integer): IBGRAFace3D;221 function GetFaceCount: integer;222 function GetTotalVertexCount: integer;223 function GetTotalNormalCount: integer;224 function GetMaterial: IBGRAMaterial3D;225 procedure SetLightingNormal(const AValue: TLightingNormal3D);226 procedure SetParentLighting(const AValue: boolean);227 procedure SetColor(const AValue: TBGRAPixel);228 procedure SetLight(const AValue: Single);229 procedure SetTexture(const AValue: IBGRAScanner);230 procedure SetMaterial(const AValue: IBGRAMaterial3D);231 procedure RemoveUnusedVertices;232 procedure SeparatePart(APart: IBGRAPart3D);233 function GetScene: TBGRAScene3D;234 function GetRefCount: integer;235 procedure SetBiface(AValue : boolean);236 procedure ForEachVertex(ACallback: TVertex3DCallback);237 procedure ForEachFace(ACallback: TFace3DCallback);238 end;239 240 227 {$i part3d.inc} 241 228 {$i object3d.inc} 242 229 {$i shapes3d.inc} 243 230 231 { TCamera3D } 232 233 function TCamera3D.GetLookWhere: TPoint3D; 234 begin 235 result := Point3D(FLookWhere); 236 end; 237 238 function TCamera3D.GetMatrix: TMatrix3D; 239 begin 240 if not FMatrixComputed then 241 begin 242 ComputeMatrix; 243 FMatrixComputed := true; 244 end; 245 result := FMatrix; 246 end; 247 248 function TCamera3D.GetViewPoint: TPoint3D; 249 begin 250 result := Point3D(FViewPoint); 251 end; 252 253 procedure TCamera3D.SetMatrix(AValue: TMatrix3D); 254 begin 255 FMatrix := AValue; 256 FMatrixComputed:= true; 257 FViewPoint := Point3D_128(FMatrix[1,4],FMatrix[2,4],FMatrix[3,4]); 258 end; 259 260 procedure TCamera3D.SetViewPoint(AValue: TPoint3D); 261 begin 262 FViewPoint := Point3D_128(AValue); 263 FMatrix[1,4] := FViewPoint.x; 264 FMatrix[2,4] := FViewPoint.y; 265 FMatrix[3,4] := FViewPoint.z; 266 FMatrixComputed := false; 267 end; 268 269 procedure TCamera3D.ComputeMatrix; 270 var ZDir, XDir, YDir: TPoint3D_128; 271 begin 272 if IsPoint3D_128_Zero(FTopDir) then exit; 273 YDir := -FTopDir; 274 Normalize3D_128(YDir); 275 276 ZDir := FLookWhere-FViewPoint; 277 if IsPoint3D_128_Zero(ZDir) then exit; 278 Normalize3D_128(ZDir); 279 280 VectProduct3D_128(YDir,ZDir,XDir); 281 VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir 282 Normalize3D_128(XDir); 283 Normalize3D_128(YDir); 284 285 FMatrix := Matrix3D(XDir,YDir,ZDir,FViewPoint); 286 FMatrix := MatrixInverse3D(FMatrix); 287 end; 288 289 procedure TCamera3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D); 290 begin 291 FLookWhere := Point3D_128(AWhere); 292 FTopDir := Point3D_128(ATopDir); 293 FMatrixComputed := false; 294 end; 295 296 procedure TCamera3D.LookLeft(angleDeg: single); 297 var m,inv: TMatrix3D; 298 begin 299 inv := MatrixInverse3D(Matrix); 300 m := MatrixRotateY(angleDeg*Pi/180); 301 FLookWhere := inv*m*Matrix*FLookWhere; 302 FMatrixComputed := false; 303 end; 304 305 procedure TCamera3D.LookRight(angleDeg: single); 306 begin 307 LookLeft(-angleDeg); 308 end; 309 310 procedure TCamera3D.LookUp(angleDeg: single); 311 var m,inv: TMatrix3D; 312 begin 313 inv := MatrixInverse3D(Matrix); 314 m := MatrixRotateX(-angleDeg*Pi/180); 315 FLookWhere := inv*m*Matrix*FLookWhere; 316 FMatrixComputed := false; 317 end; 318 319 procedure TCamera3D.LookDown(angleDeg: single); 320 begin 321 LookUp(-angleDeg); 322 end; 323 324 244 325 { TBGRAScene3D } 245 326 … … 248 329 if FAutoViewCenter then 249 330 begin 250 if Surface = nil then 251 result := PointF(0,0) 252 else 253 result := PointF((Surface.Width-1)/2,(Surface.Height-1)/2) 331 result := PointF((GetRenderWidth-1)/2,(GetRenderHeight-1)/2) 254 332 end 255 333 else … … 259 337 function TBGRAScene3D.GetViewPoint: TPoint3D; 260 338 begin 261 result := Point3D(FViewPoint);339 result := Camera.ViewPoint; 262 340 end; 263 341 … … 267 345 if FAutoZoom then 268 346 begin 269 if FSurface = nil then 347 Size := sqrt(GetRenderWidth*GetRenderHeight)*0.8; 348 if Size = 0 then 270 349 result := PointF(1,1) 271 350 else 272 begin273 Size := sqrt(FSurface.Width*FSurface.Height)*0.8;274 351 result := PointF(size,size); 275 end;276 352 end else 277 353 result := FZoom; … … 280 356 procedure TBGRAScene3D.SetAmbiantLightColorF(const AValue: TColorF); 281 357 begin 282 FAmbiantLightColor := ColorFToColorInt65536(AValue); 283 FAmbiantLightness := (FAmbiantLightColor.r + FAmbiantLightColor.g + FAmbiantLightColor.b) div 6; 358 FAmbiantLightColorF := AValue; 284 359 end; 285 360 286 361 procedure TBGRAScene3D.SetAmbiantLightness(const AValue: single); 287 362 begin 288 FAmbiantLightness:= round(AValue*32768); 289 FAmbiantLightColor := ColorInt65536(FAmbiantLightness*2, FAmbiantLightness*2, FAmbiantLightness*2); 363 FAmbiantLightColorF := ColorF(AValue, AValue, AValue, 1); 290 364 end; 291 365 292 366 procedure TBGRAScene3D.SetAmbiantLightColor(const AValue: TBGRAPixel); 293 367 begin 294 FAmbiantLightColor := BGRAToColorInt(AValue); 295 FAmbiantLightness := (FAmbiantLightColor.r + FAmbiantLightColor.g + FAmbiantLightColor.b) div 6; 368 FAmbiantLightColorF := ColorInt65536ToColorF(BGRAToColorInt(AValue,True)); 296 369 end; 297 370 … … 313 386 function TBGRAScene3D.GetAmbiantLightColor: TBGRAPixel; 314 387 begin 315 result := ColorIntToBGRA( FAmbiantLightColor);388 result := ColorIntToBGRA(ColorFToColorInt65536(FAmbiantLightColorF),True); 316 389 end; 317 390 … … 354 427 function TBGRAScene3D.GetAmbiantLightness: single; 355 428 begin 356 result := FAmbiantLightness/32768;429 result := (FAmbiantLightColorF[1]+FAmbiantLightColorF[2]+FAmbiantLightColorF[3])/3; 357 430 end; 358 431 359 432 function TBGRAScene3D.GetAmbiantLightColorF: TColorF; 360 433 begin 361 result := ColorInt65536ToColorF(FAmbiantLightColor);434 result := FAmbiantLightColorF; 362 435 end; 363 436 … … 378 451 end; 379 452 453 procedure TBGRAScene3D.SetDefaultMaterial(AValue: IBGRAMaterial3D); 454 begin 455 if FDefaultMaterial=AValue then Exit; 456 FDefaultMaterial:=AValue; 457 InvalidateMaterial; 458 end; 459 380 460 procedure TBGRAScene3D.SetViewCenter(const AValue: TPointF); 381 461 begin … … 384 464 end; 385 465 386 procedure TBGRAScene3D.ComputeMatrix; 387 var ZDir, XDir, YDir: TPoint3D_128; 388 begin 389 if IsPoint3D_128_Zero(FTopDir) then exit; 390 YDir := -FTopDir; 391 Normalize3D_128(YDir); 392 393 ZDir := FLookWhere-FViewPoint; 394 if IsPoint3D_128_Zero(ZDir) then exit; 395 Normalize3D_128(ZDir); 396 397 VectProduct3D_128(YDir,ZDir,XDir); 398 VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir 399 Normalize3D_128(XDir); 400 Normalize3D_128(YDir); 401 402 FMatrix := Matrix3D(XDir,YDir,ZDir,FViewPoint); 403 FMatrix := MatrixInverse3D(FMatrix); 466 procedure TBGRAScene3D.SetViewPoint(const AValue: TPoint3D); 467 begin 468 Camera.ViewPoint := AValue; 404 469 end; 405 470 … … 431 496 FAutoZoom := True; 432 497 FAutoViewCenter := True; 433 ViewPoint := Point3D(0,0,-100); 434 LookAt(Point3D(0,0,0), Point3D(0,-1,0)); 498 499 FCamera := TCamera3D.Create; 500 Camera.ViewPoint := Point3D(0,0,-100); 501 Camera.LookAt(Point3D(0,0,0), Point3D(0,-1,0)); 435 502 with RenderingOptions do 436 503 begin … … 464 531 465 532 destructor TBGRAScene3D.Destroy; 466 begin 467 Clear; 468 FLights.Free; 533 var 534 i: Integer; 535 begin 536 DoClear; 537 FreeAndNil(FLights); 538 FreeAndNil(FCamera); 539 for i := 0 to high(FTexturesFetched) do 540 FTexturesFetched[i].Bitmap.Free; 469 541 inherited Destroy; 470 542 end; 471 543 472 544 procedure TBGRAScene3D.Clear; 473 var i: integer; 474 begin 475 for i := 0 to FLights.Count-1 do 476 TBGRALight3D(FLights[i])._Release; 477 FLights.Clear; 478 479 for i := 0 to FObjectCount-1 do 480 FObjects[i].Clear; 481 FObjects := nil; 482 FObjectCount := 0; 483 484 FMaterials := nil; 485 FMaterialCount := 0; 545 begin 546 DoClear; 486 547 DefaultMaterial := CreateMaterial; 548 end; 549 550 function TBGRAScene3D.FetchObject(AName: string; SwapFacesOrientation: boolean 551 ): IBGRAObject3D; 552 begin 553 if FetchDirectory = '' then raise exception.Create('Please define first the FetchDirectory'); 554 try 555 result := LoadObjectFromFileUTF8(ConcatPaths([FetchDirectory,AName]), SwapFacesOrientation); 556 except 557 on ex:Exception do 558 HandleFetchException(ex); 559 end; 487 560 end; 488 561 … … 529 602 end; 530 603 604 function TBGRAScene3D.LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap; 605 begin 606 result := BGRABitmapFactory.Create(AfileNameUTF8,True); 607 end; 608 531 609 function TBGRAScene3D.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; 532 begin 533 result := nil; 534 texSize := PointF(1,1); 610 var 611 i: Integer; 612 bmp: TBGRACustomBitmap; 613 begin 614 bmp := nil; 615 for i := 0 to high(FTexturesFetched) do 616 if FTexturesFetched[i].Name = AName then 617 begin 618 bmp := FTexturesFetched[i].Bitmap; 619 result := bmp; 620 texSize := PointF(bmp.Width,bmp.Height); 621 exit; 622 end; 623 if FetchDirectory <> '' then 624 begin 625 try 626 bmp := LoadBitmapFromFileUTF8(ConcatPaths([FetchDirectory,AName])); 627 except 628 on ex:Exception do 629 HandleFetchException(ex); 630 end; 631 end; 632 if bmp = nil then 633 begin 634 result := nil; 635 texSize := PointF(1,1); 636 end else 637 begin 638 setlength(FTexturesFetched, length(FTexturesFetched)+1); 639 FTexturesFetched[high(FTexturesFetched)].Name := AName; 640 FTexturesFetched[high(FTexturesFetched)].Bitmap := bmp; 641 result := bmp; 642 texSize := PointF(bmp.Width,bmp.Height); 643 end; 644 end; 645 646 procedure TBGRAScene3D.FetchMaterials(ALibraryName: string); 647 var 648 i: Integer; 649 begin 650 if FetchDirectory <> '' then 651 begin 652 for i := 0 to high(FMaterialLibrariesFetched) do 653 if FMaterialLibrariesFetched[i]=ALibraryName then exit; 654 setlength(FMaterialLibrariesFetched,length(FMaterialLibrariesFetched)+1); 655 FMaterialLibrariesFetched[high(FMaterialLibrariesFetched)] := ALibraryName; 656 try 657 LoadMaterialsFromFile(ConcatPaths([FetchDirectory,ALibraryName])); 658 except 659 on ex:Exception do 660 HandleFetchException(ex); 661 end; 662 end; 663 end; 664 665 procedure TBGRAScene3D.HandleFetchException(AException: Exception); 666 begin 667 if FetchThrowsException then 668 raise AException; 669 end; 670 671 procedure TBGRAScene3D.DoClear; 672 var i: integer; 673 begin 674 for i := 0 to FLights.Count-1 do 675 TBGRALight3D(FLights[i]).ReleaseInterface; 676 FLights.Clear; 677 678 for i := 0 to FObjectCount-1 do 679 begin 680 FObjects[i].Clear; 681 FObjects[i] := nil; 682 end; 683 FObjects := nil; 684 FObjectCount := 0; 685 686 FMaterials := nil; 687 FMaterialCount := 0; 688 DefaultMaterial := nil; 689 end; 690 691 function TBGRAScene3D.GetRenderWidth: integer; 692 begin 693 if Assigned(FRenderer) then 694 result := FRenderer.SurfaceWidth 695 else 696 if Assigned(FSurface) then 697 result := FSurface.Width 698 else 699 result := 0; 700 end; 701 702 function TBGRAScene3D.GetRenderHeight: integer; 703 begin 704 if Assigned(FRenderer) then 705 result := FRenderer.SurfaceHeight 706 else 707 if Assigned(FSurface) then 708 result := FSurface.Height 709 else 710 result := 0; 711 end; 712 713 procedure TBGRAScene3D.OnMaterialTextureChanged(ASender: TObject); 714 begin 715 InvalidateMaterial; 716 end; 717 718 procedure TBGRAScene3D.InvalidateMaterial; 719 var 720 i: Integer; 721 begin 722 for i := 0 to FObjectCount-1 do 723 FObjects[i].InvalidateMaterial; 535 724 end; 536 725 537 726 function TBGRAScene3D.LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean): IBGRAObject3D; 538 var source: TFileStream; 539 begin 540 source := TFileStream.Create(AFilename,fmOpenRead,fmShareDenyWrite); 541 try 542 result := LoadObjectFromStream(source,SwapFacesOrientation); 543 finally 544 source.free; 545 end; 727 begin 728 result := LoadObjectFromFileUTF8(SysToUTF8(AFilename), SwapFacesOrientation); 546 729 end; 547 730 … … 659 842 result.LightingNormal := lnVertex; 660 843 end else 844 if lineType = 'mtllib' then 845 FetchMaterials(trim(s)) 846 else 661 847 if lineType = 'usemtl' then 662 848 materialname := trim(s) … … 837 1023 procedure TBGRAScene3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D); 838 1024 begin 839 FLookWhere := Point3D_128(AWhere); 840 FTopDir := Point3D_128(ATopDir); 1025 Camera.LookAt(AWhere,ATopDir); 841 1026 end; 842 1027 843 1028 procedure TBGRAScene3D.LookLeft(angleDeg: single); 844 var m,inv: TMatrix3D; 845 begin 846 inv := MatrixInverse3D(FMatrix); 847 m := MatrixRotateY(angleDeg*Pi/180); 848 FLookWhere := inv*m*FMatrix*FLookWhere; 1029 begin 1030 Camera.LookLeft(angleDeg); 849 1031 end; 850 1032 851 1033 procedure TBGRAScene3D.LookRight(angleDeg: single); 852 1034 begin 853 LookLeft(-angleDeg);1035 Camera.LookRight(angleDeg); 854 1036 end; 855 1037 856 1038 procedure TBGRAScene3D.LookUp(angleDeg: single); 857 var m,inv: TMatrix3D; 858 begin 859 inv := MatrixInverse3D(FMatrix); 860 m := MatrixRotateX(-angleDeg*Pi/180); 861 FLookWhere := inv*m*FMatrix*FLookWhere; 1039 begin 1040 Camera.LookUp(angleDeg); 862 1041 end; 863 1042 864 1043 procedure TBGRAScene3D.LookDown(angleDeg: single); 865 1044 begin 866 LookUp(-angleDeg);1045 Camera.LookDown(angleDeg); 867 1046 end; 868 1047 869 1048 procedure TBGRAScene3D.Render; 870 1049 begin 871 InternalRender(FSurface, RenderingOptions.AntialiasingMode, 1); 1050 FRenderer := TBGRARenderer3D.Create(FSurface, RenderingOptions, 1051 FAmbiantLightColorF, 1052 FLights); 1053 DoRender; 1054 FRenderer.Free; 1055 end; 1056 1057 procedure TBGRAScene3D.Render(ARenderer: TCustomRenderer3D); 1058 begin 1059 FRenderer := ARenderer; 1060 DoRender; 1061 FRenderer := nil; 872 1062 end; 873 1063 … … 876 1066 i: Integer; 877 1067 begin 878 ComputeMatrix;879 880 1068 FProjection.Zoom := Zoom; 881 1069 FProjection.Zoom.X *= ScaleX; … … 885 1073 FProjection.Center.Y *= ScaleY; 886 1074 for i := 0 to FObjectCount-1 do 887 FObjects[i].ComputeWithMatrix(FMatrix, FProjection); 888 end; 889 890 function TBGRAScene3D.ComputeCoordinate(ASceneCoord: TPoint3D_128; APart: IBGRAPart3D): TPointF; 891 begin 892 result := APart.ComputeCoordinate(ASceneCoord, FProjection); 1075 FObjects[i].ComputeWithMatrix(Camera.Matrix, FProjection); 893 1076 end; 894 1077 … … 903 1086 end else 904 1087 result := PointF(0,0); 905 end;906 907 procedure TBGRAScene3D.ComputeLight;908 begin909 910 1088 end; 911 1089 … … 998 1176 end; 999 1177 1000 procedure TBGRAScene3D. InternalRender(ASurface: TBGRACustomBitmap; AAntialiasingMode: TAntialiasingMode3D; GlobalScale: single);1178 procedure TBGRAScene3D.DoRender; 1001 1179 var 1002 1180 LFaces: array of TBGRAFace3D; … … 1014 1192 obj := FObjects[i]; 1015 1193 inc(LFaceCount, obj.GetFaceCount); 1016 if obj.GetParentLighting then 1017 begin 1018 obj.SetLightingNormal(Self.DefaultLightingNormal); 1019 obj.SetParentLighting(True); 1020 end; 1194 obj.Update; 1021 1195 end; 1022 1196 setlength(LFaces, LFaceCount); … … 1034 1208 1035 1209 var 1036 multi: TBGRAMultishapeFiller; 1037 ColorGradientTempBmp: TBGRACustomBitmap; 1038 zbuffer: psingle; 1039 1210 faceDesc: TFaceRenderingDescription; 1040 1211 LVertices: array of TBGRAVertex3D; 1041 LColors: array of TBGRAPixel;1042 LTexCoord: array of TPointF;1043 LZ: array of single;1044 LProj: array of TPointF;1045 LPos3D, LNormal3D: array of TPoint3D_128;1046 LLighting: array of word;1047 shaderContext: TMemoryBlockAlign128;1048 lightingProc: TShaderFunction3D;1049 UseAmbiantColor: boolean;1050 1212 1051 1213 procedure DrawFace(numFace: integer); 1052 1053 procedure DrawAliasedColoredFace(shader: TShaderFunction3D; VCount: integer; context: PBasicLightingContext);1054 var j,k: integer;1055 SameColor: boolean;1056 center: record1057 proj: TPointF;1058 pos3D,normal3D: TPoint3D_128;1059 color: TBGRAPixel;1060 end;1061 1062 begin1063 SameColor := True;1064 for j := 1 to VCount-1 do1065 if (LColors[j]<>LColors[j-1]) then SameColor := False;1066 1067 if shader <> nil then1068 begin1069 if SameColor then1070 begin1071 BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,1072 slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),nil,1073 slice(LTexCoord,VCount),False,shader,True,LColors[0],zbuffer,context);1074 end else1075 if VCount = 3 then1076 begin1077 ColorGradientTempBmp.SetPixel(0,0,LColors[0]);1078 ColorGradientTempBmp.SetPixel(1,0,LColors[1]);1079 ColorGradientTempBmp.SetPixel(0,1,LColors[2]);1080 ColorGradientTempBmp.SetPixel(1,1,MergeBGRA(LColors[1],LColors[2]));1081 BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,1082 slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),ColorGradientTempBmp,1083 [PointF(0,0),PointF(1,0),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);1084 end else1085 if VCount = 4 then1086 begin1087 ColorGradientTempBmp.SetPixel(0,0,LColors[0]);1088 ColorGradientTempBmp.SetPixel(1,0,LColors[1]);1089 ColorGradientTempBmp.SetPixel(1,1,LColors[2]);1090 ColorGradientTempBmp.SetPixel(0,1,LColors[3]);1091 BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,1092 slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),ColorGradientTempBmp,1093 [PointF(0,0),PointF(1,0),PointF(1,1),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);1094 end else1095 if VCount >= 3 then1096 begin //split into triangles1097 with center do1098 begin1099 ClearPoint3D_128(pos3D);1100 ClearPoint3D_128(normal3D);1101 color := MergeBGRA(slice(LColors,VCount));1102 end;1103 for j := 0 to VCount-1 do1104 begin1105 center.pos3D += LPos3D[j];1106 center.normal3D += LNormal3D[j];1107 end;1108 with center do1109 begin1110 pos3D *= (1/VCount);1111 Normalize3D_128(normal3D);1112 end;1113 center.proj := ComputeCoordinate(center.pos3D);1114 k := VCount-1;1115 for j := 0 to VCount-1 do1116 begin1117 ColorGradientTempBmp.SetPixel(0,0,LColors[k]);1118 ColorGradientTempBmp.SetPixel(1,0,LColors[j]);1119 ColorGradientTempBmp.SetPixel(0,1,center.color);1120 ColorGradientTempBmp.SetPixel(1,1,MergeBGRA(LColors[j],center.color));1121 BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,1122 [LProj[k],LProj[j],center.proj], [LPos3D[k],LPos3D[j],center.pos3D],1123 [LNormal3D[k],LNormal3D[j],center.normal3D], ColorGradientTempBmp,1124 [PointF(0,0),PointF(1,0),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);1125 k := j;1126 end;1127 end;1128 end else1129 begin1130 if SameColor then1131 begin1132 if RenderingOptions.PerspectiveMode = pmZBuffer then1133 BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, slice(LProj,VCount),1134 slice(LZ,VCount), slice(LColors,VCount),True,zbuffer)1135 else1136 ASurface.FillPoly(slice(LProj,VCount),LColors[0],dmDrawWithTransparency);1137 end1138 else1139 begin1140 if VCount > 4 then1141 begin //split into triangles1142 with center do1143 begin1144 ClearPoint3D_128(pos3D);1145 color := MergeBGRA(slice(LColors,VCount));1146 end;1147 for j := 0 to VCount-1 do1148 center.pos3D += LPos3D[j];1149 with center do1150 pos3D *= (1/VCount);1151 center.proj := ComputeCoordinate(center.pos3D);1152 k := VCount-1;1153 if RenderingOptions.PerspectiveMode = pmLinearMapping then1154 begin1155 for j := 0 to VCount-1 do1156 begin1157 ASurface.FillPolyLinearColor([LProj[k],LProj[j],center.proj],[LColors[k],LColors[j],center.color]);1158 k := j;1159 end;1160 end else1161 begin1162 for j := 0 to VCount-1 do1163 begin1164 BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, [LProj[k],LProj[j],center.proj],1165 [LZ[k],LZ[j],center.pos3D.z], [LColors[k],LColors[j],center.color],True,zbuffer);1166 k := j;1167 end;1168 end;1169 end else1170 begin1171 if RenderingOptions.PerspectiveMode = pmLinearMapping then1172 ASurface.FillPolyLinearColor(slice(LProj,VCount),slice(LColors,VCount))1173 else1174 BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, slice(LProj,VCount),1175 slice(LZ,VCount), slice(LColors,VCount),True,zbuffer);1176 end;1177 end;1178 end;1179 end;1180 1181 1214 var 1182 1215 j,k: Integer; 1183 LTexture: IBGRAScanner;1184 LMaterial: TBGRAMaterial3D;1185 SameColor: boolean;1186 LLightNormal : TLightingNormal3D;1187 LNoLighting: boolean;1188 PtCenter: TPointF;1189 PtCenter3D: TPoint3D_128;1190 ColorCenter: TBGRAPixel;1191 1216 VCount,NewVCount: integer; 1192 ctx: PSceneLightingContext; 1193 NegNormals, UseDiffuseColor, 1194 UseDiffuseLightness{, OnlyDirectionalLight}: boolean; 1217 NegNormals: boolean; 1195 1218 LastVisibleVertex: integer; 1196 1219 … … 1204 1227 LVertices[NewVCount] := nil; //computed 1205 1228 1206 LColors[NewVCount] := MergeBGRA(LColors[n1],round((1-t)*65536),LColors[n2],round(t*65536)); 1207 LTexCoord[NewVCount] := LTexCoord[n1]*(1-t) + LTexCoord[n2]*t; 1208 LPos3D[NewVCount] := LPos3D[n1]*(1-t) + LPos3D[n2]*t; 1209 LNormal3D[NewVCount] := LNormal3D[n1]*(1-t) + LNormal3D[n2]*t; 1210 LZ[NewVCount] := LZ[n1]*(1-t) + LZ[n2]*t; 1211 LProj[NewVCount] := ComputeCoordinate(LPos3D[NewVCount]); 1229 faceDesc.Colors[NewVCount] := MergeBGRA(faceDesc.Colors[n1],round((1-t)*65536),faceDesc.Colors[n2],round(t*65536)); 1230 faceDesc.TexCoords[NewVCount] := faceDesc.TexCoords[n1]*(1-t) + faceDesc.TexCoords[n2]*t; 1231 faceDesc.Positions3D[NewVCount] := faceDesc.Positions3D[n1]*(1-t) + faceDesc.Positions3D[n2]*t; 1232 faceDesc.Normals3D[NewVCount] := faceDesc.Normals3D[n1]*(1-t) + faceDesc.Normals3D[n2]*t; 1233 faceDesc.Projections[NewVCount] := ComputeCoordinate(faceDesc.Positions3D[NewVCount]); 1212 1234 NewVCount += 1; 1213 1235 end; 1214 1236 1215 1237 procedure LoadVertex(idxL: integer; idxV: integer); 1216 var desc: PBGRAFaceVertexDescription;1238 var vertexDesc: PBGRAFaceVertexDescription; 1217 1239 tempV: TBGRAVertex3D; 1218 1240 begin 1219 1241 with LFaces[numFace] do 1220 1242 begin 1221 desc := VertexDescription[idxV];1222 with desc^ do1243 vertexDesc := VertexDescription[idxV]; 1244 with vertexDesc^ do 1223 1245 begin 1224 1246 tempV := TBGRAVertex3D(vertex.GetAsObject); 1225 1247 LVertices[idxL] := tempV; 1226 1248 1227 if LTexture <> nil then 1228 LColors[idxL] := BGRA(128,128,128) 1229 else 1230 begin 1231 if ColorOverride then 1232 LColors[idxL] := Color 1233 else 1234 begin 1235 if tempV.ParentColor then 1236 LColors[idxL] := Object3D.Color 1237 else 1238 LColors[idxL] := tempV.Color; 1239 end; 1240 end; 1241 1242 if TexCoordOverride then 1243 LTexCoord[idxL] := TexCoord 1244 else 1245 LTexCoord[idxL] := tempV.TexCoord; 1246 with LMaterial.GetTextureZoom do 1247 begin 1248 LTexCoord[idxL].x *= x; 1249 LTexCoord[idxL].y *= y; 1250 end; 1249 faceDesc.Colors[idxL] := ActualColor; 1250 faceDesc.TexCoords[idxL] := ActualTexCoord; 1251 1251 1252 1252 with tempV.CoordData^ do 1253 1253 begin 1254 LPos3D[idxL] := viewCoord; 1255 LNormal3D[idxL] := viewNormal; 1256 LProj[idxL] := projectedCoord; 1257 LZ[idxL] := viewCoord.Z; 1254 faceDesc.Positions3D[idxL] := viewCoord; 1255 facedesc.Normals3D[idxL] := viewNormal; 1256 faceDesc.Projections[idxL] := projectedCoord; 1258 1257 end; 1259 1258 if Normal <> nil then 1260 LNormal3D[idxL] := Normal.ViewNormal_128; 1259 facedesc.Normals3D[idxL] := Normal.ViewNormal_128; 1260 Normalize3D_128(facedesc.Normals3D[idxL]); 1261 1261 end; 1262 1262 end; … … 1269 1269 if VCount < 3 then exit; 1270 1270 1271 if Material <> nil then 1272 LMaterial := TBGRAMaterial3D(Material.GetAsObject) 1273 else if Object3D.Material <> nil then 1274 LMaterial := TBGRAMaterial3D(Object3D.Material.GetAsObject) 1275 else if self.DefaultMaterial <> nil then 1276 LMaterial := TBGRAMaterial3D(self.DefaultMaterial.GetAsObject) 1277 else 1278 exit; 1279 1280 if ParentTexture then 1281 begin 1282 if LMaterial.GetTexture <> nil then 1283 LTexture := LMaterial.GetTexture 1284 else 1285 LTexture := Object3D.Texture 1286 end 1287 else 1288 LTexture := Texture; 1289 1290 LLightNormal := Object3D.LightingNormal; 1271 faceDesc.NormalsMode := Object3D.LightingNormal; 1272 1273 faceDesc.Material := ActualMaterial; 1274 if faceDesc.Material = nil then exit; 1275 faceDesc.Texture := ActualTexture; 1291 1276 1292 1277 if length(LVertices) < VCount+3 then //keep margin for z-clip 1293 1278 begin 1294 1279 setlength(LVertices, (VCount+3)*2); 1295 setlength(LColors, length(LVertices)); 1296 setlength(LTexCoord, length(LVertices)); 1297 setlength(LZ, length(LVertices)); 1298 setlength(LProj, length(LVertices)); 1299 setlength(LPos3D, length(LVertices)); 1300 setlength(LNormal3D, length(LVertices)); 1301 setlength(LLighting, length(LVertices)); 1280 setlength(faceDesc.Colors, length(LVertices)); 1281 setlength(faceDesc.TexCoords, length(LVertices)); 1282 setlength(faceDesc.Projections, length(LVertices)); 1283 setlength(faceDesc.Positions3D, length(LVertices)); 1284 setlength(faceDesc.Normals3D, length(LVertices)); 1302 1285 end; 1303 1286 1304 NewVCount := 0; 1305 LastVisibleVertex := -1; 1306 for k := VCount-1 downto 0 do 1307 if Vertex[k].ViewCoordZ >= RenderingOptions.MinZ then 1308 begin 1309 LastVisibleVertex := k; 1310 break; 1311 end; 1312 if LastVisibleVertex = -1 then exit; 1313 1314 k := VCount-1; 1315 for j := 0 to VCount-1 do 1287 if FRenderer.HandlesNearClipping then 1316 1288 begin 1317 if Vertex[j].ViewCoordZ >= RenderingOptions.MinZ then 1318 begin 1319 if k <> LastVisibleVertex then //one or more vertices is out 1320 begin 1321 LoadVertex(NewVCount+1, LastVisibleVertex); 1322 LoadVertex(NewVCount+2, (LastVisibleVertex+1) mod VertexCount); 1323 AddZIntermediate(NewVCount+1,NewVCount+2); 1324 1325 LoadVertex(NewVCount+1, j); 1326 LoadVertex(NewVCount+2, k); 1327 1328 AddZIntermediate(NewVCount+1,NewVCount+2); 1329 inc(NewVCount); 1330 end else 1331 begin 1332 LoadVertex(NewVCount, j); 1333 NewVCount += 1; 1334 end; 1335 LastVisibleVertex := j; 1336 end; 1337 k := j; 1338 end; 1339 VCount := NewVCount; 1340 if VCount < 3 then exit; //after z-clipping 1341 1342 if not IsPolyVisible(slice(LProj,VCount)) then 1343 begin 1344 if not Biface then exit; 1345 NegNormals := True; 1289 for j := 0 to VCount-1 do 1290 LoadVertex(j,j); 1346 1291 end else 1347 1292 begin 1348 NegNormals := False; 1293 NewVCount := 0; 1294 LastVisibleVertex := -1; 1295 for k := VCount-1 downto 0 do 1296 if Vertex[k].ViewCoordZ >= RenderingOptions.MinZ then 1297 begin 1298 LastVisibleVertex := k; 1299 break; 1300 end; 1301 if LastVisibleVertex = -1 then exit; 1302 1303 k := VCount-1; 1304 for j := 0 to VCount-1 do 1305 begin 1306 if Vertex[j].ViewCoordZ >= RenderingOptions.MinZ then 1307 begin 1308 if k <> LastVisibleVertex then //one or more vertices is out 1309 begin 1310 LoadVertex(NewVCount+1, LastVisibleVertex); 1311 LoadVertex(NewVCount+2, (LastVisibleVertex+1) mod VertexCount); 1312 AddZIntermediate(NewVCount+1,NewVCount+2); 1313 1314 LoadVertex(NewVCount+1, j); 1315 LoadVertex(NewVCount+2, k); 1316 1317 AddZIntermediate(NewVCount+1,NewVCount+2); 1318 inc(NewVCount); 1319 end else 1320 begin 1321 LoadVertex(NewVCount, j); 1322 NewVCount += 1; 1323 end; 1324 LastVisibleVertex := j; 1325 end; 1326 k := j; 1327 end; 1328 VCount := NewVCount; 1329 if VCount < 3 then exit; //after z-clipping 1349 1330 end; 1350 1331 1351 //from here we assume the face will be drawn 1352 inc(FRenderedFaceCount); 1332 if not FRenderer.HandlesFaceCulling then 1333 begin 1334 if not IsPolyVisible(slice(faceDesc.Projections,VCount)) then 1335 begin 1336 if not Biface then exit; 1337 NegNormals := True; 1338 end else 1339 begin 1340 NegNormals := False; 1341 end; 1342 end else 1343 NegNormals := false; 1353 1344 1354 1345 //compute normals 1355 case LLightNormalof1346 case faceDesc.NormalsMode of 1356 1347 lnFace: for j := 0 to VCount-1 do 1357 LNormal3D[j] := ViewNormal_128;1348 faceDesc.Normals3D[j] := ViewNormal_128; 1358 1349 lnFaceVertexMix: 1359 1350 for j := 0 to VCount-1 do 1360 1351 begin 1361 LNormal3D[j] += ViewNormal_128;1362 Normalize3D_128( LNormal3D[j]);1352 faceDesc.Normals3D[j] += ViewNormal_128; 1353 Normalize3D_128(faceDesc.Normals3D[j]); 1363 1354 end; 1364 1355 end; 1365 1356 if NegNormals then 1366 1357 for j := 0 to VCount-1 do 1367 LNormal3D[j] := -LNormal3D[j]; 1368 1369 //prepare lighting 1370 {OnlyDirectionalLight := true; 1371 for j := 0 to LightCount-1 do 1372 if not Light[j].IsDirectional then OnlyDirectionalLight := false; } 1373 1374 if LMaterial.GetSpecularOn then 1375 lightingProc:= TShaderFunction3D(@ApplyLightingWithDiffuseAndSpecularColor) else 1376 begin 1377 UseDiffuseColor := UseAmbiantColor; 1378 if not UseDiffuseColor then 1379 begin 1380 with LMaterial.GetDiffuseColorInt do 1381 UseDiffuseColor := (r <> g) or (g <> b); 1382 if not UseDiffuseColor and LMaterial.GetAutoDiffuseColor then 1383 begin 1384 for j := 0 to LightCount-1 do 1385 if Light[j].ColoredLight then 1386 begin 1387 UseDiffuseColor := true; 1388 break; 1389 end; 1390 end; 1391 end; 1392 if UseDiffuseColor then 1393 lightingProc := TShaderFunction3D(@ApplyLightingWithDiffuseColor) else 1394 begin 1395 UseDiffuseLightness := FAmbiantLightness <> 32768; 1396 if not UseDiffuseLightness then 1397 begin 1398 if LightCount <> 0 then 1399 UseDiffuseLightness := true; 1400 end; 1401 1402 if UseDiffuseLightness then 1403 lightingProc := TShaderFunction3D(@ApplyLightingWithLightness) else 1404 if FAmbiantLightness <> 32768 then 1405 lightingProc := TShaderFunction3D(@ApplyLightingWithAmbiantLightnessOnly) else 1406 lightingProc := TShaderFunction3D(@ApplyNoLighting); 1407 end; 1408 end; 1409 1410 ctx := PSceneLightingContext( shaderContext.Data ); 1411 ctx^.material := LMaterial; 1358 faceDesc.Normals3D[j] := -faceDesc.Normals3D[j]; 1359 1412 1360 if LightThroughFactorOverride then 1413 ctx^.LightThroughFactor := LightThroughFactor1361 faceDesc.LightThroughFactor := LightThroughFactor 1414 1362 else 1415 ctx^.LightThroughFactor := LMaterial.GetLightThroughFactor; 1416 ctx^.LightThrough := ctx^.LightThroughFactor > 0; 1417 ctx^.SaturationHighF := LMaterial.GetSaturationHigh; 1418 ctx^.SaturationLowF := LMaterial.GetSaturationLow; 1419 ctx^.SaturationHigh := round(LMaterial.GetSaturationHigh*32768); 1420 ctx^.SaturationLow := round(LMaterial.GetSaturationLow*32768); 1421 1422 //high-quality lighting interpolation, necessary for Phong and high-quality Gouraud 1423 if ( 1424 (RenderingOptions.LightingInterpolation = liAlwaysHighQuality) or 1425 ((RenderingOptions.LightingInterpolation = liSpecularHighQuality) and LMaterial.GetSpecularOn) 1426 ) and (LLightNormal <> lnNone) {and (not (LLightNormal = lnFace) and OnlyDirectionalLight) }then 1427 begin 1428 if LTexture = nil then 1429 DrawAliasedColoredFace(lightingProc,VCount,PBasicLightingContext(ctx)) //use shader 1430 else 1431 BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface, 1432 slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),LTexture, 1433 slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation,lightingProc,True, BGRAPixelTransparent,zbuffer,PBasicLightingContext(ctx)); 1434 1435 exit; 1436 end; 1437 1438 //Vertex lighting interpolation (low-quality Gouraud, low-quality Phong) 1439 LNoLighting := True; 1440 for j := 0 to VCount-1 do 1441 begin 1442 with ctx^ do 1443 begin 1444 basic.Position := LPos3D[j]; 1445 basic.Normal := LNormal3D[j]; 1446 end; 1447 LColors[j] := lightingProc(PBasicLightingContext(ctx),LColors[j]); 1448 if LColors[j] <> BGRA(128,128,128) then 1449 LNoLighting := false; 1450 end; 1451 1452 if (AAntialiasingMode = am3dMultishape) and not (RenderingOptions.PerspectiveMode = pmZBuffer) then //high-quality antialiasing 1453 begin 1454 if LTexture <> nil then 1455 begin 1456 if (RenderingOptions.PerspectiveMode <> pmLinearMapping) and (VCount=4) then 1457 multi.AddQuadPerspectiveMapping(LProj[0],LProj[1],LProj[2],LProj[3],LTexture,LTexCoord[0],LTexCoord[1],LTexCoord[2],LTexCoord[3]) 1458 else 1459 if VCount>=3 then 1460 begin 1461 for j := 0 to VCount-3 do 1462 multi.AddTriangleLinearMapping(LProj[j],LProj[j+1],LProj[j+2],LTexture,LTexCoord[j],LTexCoord[j+1],LTexCoord[j+2]); 1463 end; 1464 end 1465 else 1466 begin 1467 SameColor := True; 1468 for j := 1 to VCount-1 do 1469 if (LColors[j]<>LColors[j-1]) then SameColor := False; 1470 1471 if SameColor then 1472 multi.AddPolygon(slice(LProj,VCount),LColors[0]) 1473 else 1474 if VCount=3 then 1475 multi.AddTriangleLinearColor(LProj[0],LProj[1],LProj[2],LColors[0],LColors[1],LColors[2]) 1476 else 1477 if VCount>=3 then 1478 begin //split into triangles 1479 PtCenter3D := Point3D_128_Zero; 1480 for j := 0 to VCount-1 do 1481 PtCenter3D += LPos3D[j]; 1482 PtCenter3D *= (1/VCount); 1483 PtCenter := ComputeCoordinate(PtCenter3D); 1484 ColorCenter := MergeBGRA(slice(LColors,VCount)); 1485 k := VCount-1; 1486 for j := 0 to VCount-1 do 1487 begin 1488 multi.AddTriangleLinearColor(LProj[k],LProj[j],PtCenter,LColors[k],LColors[j],ColorCenter); 1489 k := j; 1490 end; 1491 end; 1492 end; 1493 end else 1494 begin 1495 if LTexture <> nil then 1496 begin 1497 if LNoLighting then 1498 begin 1499 if RenderingOptions.PerspectiveMode <> pmLinearMapping then 1500 ASurface.FillPolyPerspectiveMapping(slice(LProj,VCount),slice(LZ,VCount),LTexture,slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation, zbuffer) 1501 else 1502 ASurface.FillPolyLinearMapping(slice(LProj,VCount),LTexture,slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation); 1503 end else 1504 begin 1505 for j := 0 to VCount-1 do 1506 LLighting[j] := LColors[j].green shl 8; 1507 if RenderingOptions.PerspectiveMode <> pmLinearMapping then 1508 ASurface.FillPolyPerspectiveMappingLightness(slice(LProj,VCount),slice(LZ,VCount),LTexture,slice(LTexCoord,VCount),slice(LLighting,VCount),RenderingOptions.TextureInterpolation, zbuffer) 1509 else 1510 ASurface.FillPolyLinearMappingLightness(slice(LProj,VCount),LTexture,slice(LTexCoord,VCount),slice(LLighting,VCount),RenderingOptions.TextureInterpolation); 1511 end; 1512 end 1513 else 1514 DrawAliasedColoredFace(nil,VCount,PBasicLightingContext(ctx)); //already low-quality shaded 1515 end; 1363 faceDesc.LightThroughFactor := faceDesc.Material.GetLightThroughFactor; 1364 1365 faceDesc.NbVertices:= VCount; 1366 faceDesc.Biface := Biface; 1367 1368 if FRenderer.RenderFace(faceDesc, @ComputeCoordinate) then 1369 inc(FRenderedFaceCount); 1516 1370 end; 1517 1371 end; 1518 1372 1519 procedure DrawWithResample;1520 var1521 tempSurface: TBGRACustomBitmap;1522 begin1523 tempSurface := ASurface.NewBitmap(ASurface.Width*RenderingOptions.AntialiasingResampleLevel,ASurface.Height*RenderingOptions.AntialiasingResampleLevel);1524 InternalRender(tempSurface, am3dNone, RenderingOptions.AntialiasingResampleLevel);1525 BGRAResample.DownSamplePutImage(tempSurface,RenderingOptions.AntialiasingResampleLevel,RenderingOptions.AntialiasingResampleLevel,1526 ASurface, 0,0, dmDrawWithTransparency);1527 tempSurface.Free;1528 end;1529 1530 1373 var i,j: integer; 1531 1374 … … 1533 1376 FRenderedFaceCount:= 0; 1534 1377 1535 if ASurface = nil then1536 raise exception.Create('No surface specified');1537 1538 if (AAntialiasingMode = am3dResample) and (RenderingOptions.AntialiasingResampleLevel > 1) then1539 begin1540 DrawWithResample;1541 exit;1542 end;1543 1544 1378 PrepareFaces; 1545 ComputeView(GlobalScale,GlobalScale); 1546 ComputeLight; 1547 UseAmbiantColor := (FAmbiantLightColor.r <> FAmbiantLightColor.g) or (FAmbiantLightColor.g <> FAmbiantLightColor.b); 1379 ComputeView(FRenderer.GlobalScale,FRenderer.GlobalScale); 1380 FRenderer.Projection := FProjection; 1548 1381 1549 1382 SortFaces(LFaces); 1550 1383 LVertices := nil; 1551 1384 1552 if AAntialiasingMode = am3dMultishape then 1553 begin 1554 multi := TBGRAMultishapeFiller.Create; 1555 multi.PolygonOrder := poLastOnTop; 1556 end 1557 else 1558 multi := nil; 1559 1560 ColorGradientTempBmp := ASurface.NewBitmap(2,2); 1561 ColorGradientTempBmp.ScanInterpolationFilter := rfLinear; 1562 1563 if RenderingOptions.PerspectiveMode = pmZBuffer then 1564 begin 1565 getmem(zbuffer, ASurface.NbPixels*sizeof(single)); 1566 FillDWord(zbuffer^, ASurface.NbPixels, dword(single(0))); 1567 end 1568 else 1569 zbuffer := nil; 1570 1571 shaderContext := TMemoryBlockAlign128.Create(sizeof(TSceneLightingContext)); 1572 1573 if zbuffer <> nil then 1385 //if there is a Z-Buffer, it is possible to avoid drawing things that 1386 //are hidden by opaque faces by drawing first all opaque faces 1387 if FRenderer.HasZBuffer then 1574 1388 begin 1575 1389 setlength(LFaceOpaque, length(LFaces)); … … 1602 1416 DrawFace(i); 1603 1417 end; 1604 1605 shaderContext.Free;1606 if zbuffer <> nil then freemem(zbuffer);1607 ColorGradientTempBmp.Free;1608 1609 if multi <> nil then1610 begin1611 multi.Draw(ASurface);1612 multi.Free;1613 end;1614 end;1615 1616 procedure TBGRAScene3D.SetViewPoint(const AValue: TPoint3D);1617 begin1618 FViewPoint := Point3D_128(AValue);1619 end;1620 1621 function TBGRAScene3D.ApplyLightingWithLightness(Context: PSceneLightingContext;1622 Color: TBGRAPixel): TBGRAPixel;1623 var i: Integer;1624 m: TBGRAMaterial3D;1625 begin1626 m := TBGRAMaterial3D(Context^.material);1627 if not m.GetAutoSimpleColor then Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetSimpleColorInt));1628 1629 Context^.lightness := FAmbiantLightness;1630 1631 i := FLights.Count-1;1632 while i >= 0 do1633 begin1634 TBGRALight3D(FLights[i]).ComputeDiffuseLightness(Context);1635 dec(i);1636 end;1637 1638 with Context^ do1639 if Lightness <= 0 then1640 result := BGRA(0,0,0,color.alpha)1641 else1642 begin1643 if Lightness <= SaturationLow then1644 result := ApplyIntensityFast(Color, Lightness)1645 else if Lightness >= SaturationHigh then1646 result := BGRA(255,255,255,color.alpha)1647 else1648 result := ApplyLightnessFast( ApplyIntensityFast(Color, SaturationLow),1649 (Lightness - SaturationLow)*32767 div (SaturationHigh-SaturationLow)+32768 );1650 end;1651 end;1652 1653 function TBGRAScene3D.ApplyLightingWithDiffuseColor(Context: PSceneLightingContext;1654 Color: TBGRAPixel): TBGRAPixel;1655 var i: Integer;1656 m: TBGRAMaterial3D;1657 begin1658 m := TBGRAMaterial3D(Context^.material);1659 1660 if m.GetAutoAmbiantColor then1661 Context^.diffuseColor := FAmbiantLightColor1662 else1663 Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;1664 1665 i := FLights.Count-1;1666 while i >= 0 do1667 begin1668 TBGRALight3D(FLights[i]).ComputeDiffuseColor(Context);1669 dec(i);1670 end;1671 1672 result := ColorIntToBGRA(BGRAToColorIntMultiply(Color,Context^.diffuseColor));1673 result.alpha := Color.alpha;1674 end;1675 1676 function TBGRAScene3D.ApplyLightingWithDiffuseAndSpecularColor(Context: PSceneLightingContext;1677 Color: TBGRAPixel): TBGRAPixel;1678 var i: Integer;1679 m: TBGRAMaterial3D;1680 begin1681 m := TBGRAMaterial3D(Context^.material);1682 1683 if m.GetAutoAmbiantColor then1684 Context^.diffuseColor := FAmbiantLightColor1685 else1686 Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;1687 Context^.specularColor := ColorInt65536(0,0,0,0);1688 1689 i := FLights.Count-1;1690 while i >= 0 do1691 begin1692 TBGRALight3D(FLights[i]).ComputeDiffuseAndSpecularColor(Context);1693 dec(i);1694 end;1695 1696 with Context^ do1697 begin1698 diffuseColor.a := 65536;1699 result := ColorIntToBGRA(BGRAToColorIntMultiply(Color,diffuseColor) + specularColor);1700 end;1701 end;1702 1703 function TBGRAScene3D.ApplyNoLighting(Context: PSceneLightingContext;1704 Color: TBGRAPixel): TBGRAPixel;1705 var1706 m: TBGRAMaterial3D;1707 begin1708 m := TBGRAMaterial3D(Context^.material);1709 1710 if not m.GetAutoAmbiantColor then1711 result := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt))1712 else1713 result := Color;1714 end;1715 1716 function TBGRAScene3D.ApplyLightingWithAmbiantLightnessOnly(1717 Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel;1718 var1719 m: TBGRAMaterial3D;1720 begin1721 m := TBGRAMaterial3D(Context^.material);1722 1723 if not m.GetAutoAmbiantColor then1724 Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt));1725 1726 if FAmbiantLightness <= 0 then1727 result := BGRA(0,0,0,color.alpha)1728 else1729 result := ApplyIntensityFast(Color, FAmbiantLightness);1730 1418 end; 1731 1419 … … 1860 1548 1861 1549 function TBGRAScene3D.CreateMaterial: IBGRAMaterial3D; 1862 begin 1863 result := TBGRAMaterial3D.Create; 1550 var m: TBGRAMaterial3D; 1551 begin 1552 m := TBGRAMaterial3D.Create; 1553 m.OnTextureChanged := @OnMaterialTextureChanged; 1554 result := m; 1864 1555 AddMaterial(result); 1865 1556 end; 1866 1557 1867 1558 function TBGRAScene3D.CreateMaterial(ASpecularIndex: integer): IBGRAMaterial3D; 1868 begin 1869 result := TBGRAMaterial3D.Create; 1870 result.SpecularIndex := ASpecularIndex; 1871 result.SpecularColor := BGRAWhite; 1559 var m: TBGRAMaterial3D; 1560 begin 1561 m := TBGRAMaterial3D.Create; 1562 m.SetSpecularIndex(ASpecularIndex); 1563 m.SetSpecularColor(BGRAWhite); 1564 m.OnTextureChanged := @OnMaterialTextureChanged; 1565 result := m; 1872 1566 AddMaterial(result); 1873 1567 end; … … 1933 1627 end; 1934 1628 1629 function TBGRAScene3D.MakeLightList: TList; 1630 var i: integer; 1631 begin 1632 result := TList.Create; 1633 for i := 0 to FLights.Count-1 do 1634 result.Add(FLights[i]); 1635 end; 1636 1935 1637 initialization 1936 1638 -
GraphicTest/Packages/bgrabitmap/bgraslicescaling.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, Graphics, BGRABitmap, BGRABitmapTypes, IniFiles, FileUtil;8 Classes, SysUtils, BGRAGraphics, BGRABitmap, BGRABitmapTypes, IniFiles; 9 9 10 10 type … … 151 151 implementation 152 152 153 uses types;153 uses BGRAUTF8, Types; 154 154 155 155 function Margins(ATop, ARight, ABottom, ALeft: integer): TMargins; … … 759 759 break; 760 760 end; 761 Inc(p);762 761 end; 763 762 if not isRepeating then -
GraphicTest/Packages/bgrabitmap/bgrasse.inc
r472 r494 1 {$IFDEF CPUI386} 2 {$DEFINE BGRASSE_AVAILABLE} 1 {$IFDEF SSE_LOADV} 2 {$UNDEF SSE_LOADV} 3 {$ifdef cpux86_64} 4 mov rax,v 5 movups xmm1,[rax] 6 {$else} 7 mov eax,v 8 movups xmm1,[eax] 9 {$endif} 10 {$ELSE} 11 {$IFDEF SSE_SAVEV} 12 {$UNDEF SSE_SAVEV} 13 {$ifdef cpux86_64} 14 mov rax,v 15 movups [rax],xmm1 16 {$else} 17 mov eax,v 18 movups [eax],xmm1 19 {$endif} 20 {$ELSE} 21 {$IFDEF CPUI386} 22 {$DEFINE BGRASSE_AVAILABLE} 23 {$ENDIF} 24 {$IFDEF cpux86_64} 25 {$DEFINE BGRASSE_AVAILABLE} 26 {$ENDIF} 27 {$ENDIF} 3 28 {$ENDIF} 4 {$IFDEF cpux86_64}5 {$DEFINE BGRASSE_AVAILABLE}6 {$ENDIF}7 -
GraphicTest/Packages/bgrabitmap/bgrasse.pas
r472 r494 21 21 var UseSSE, UseSSE2, UseSSE3 : boolean; 22 22 23 {$ifdef CPUI386} 24 {$asmmode intel} 25 {$ENDIF} 26 {$ifdef cpux86_64} 27 {$asmmode intel} 28 {$ENDIF} 29 23 30 {$ifdef BGRASSE_AVAILABLE} 24 {$asmmode intel}25 31 //SSE rotate singles 26 32 const Shift231 = 1 + 8; … … 351 357 begin 352 358 asm 353 {$ i sseloadv.inc}359 {$DEFINE SSE_LOADV}{$i bgrasse.inc} 354 360 movaps xmm2, xmm1 355 361 mulps xmm2, xmm2 … … 377 383 rsqrtps xmm2, xmm2 378 384 mulps xmm1, xmm2 //apply 379 {$ i ssesavev.inc}385 {$DEFINE SSE_SAVEV}{$i bgrasse.inc} 380 386 end; 381 387 end; … … 387 393 begin 388 394 asm 389 {$ i sseloadv.inc}395 {$DEFINE SSE_LOADV}{$i bgrasse.inc} 390 396 movaps xmm2, xmm1 391 397 mulps xmm2, xmm2 … … 407 413 rsqrtps xmm2, xmm2 408 414 mulps xmm1, xmm2 //apply 409 {$ i ssesavev.inc}415 {$DEFINE SSE_SAVEV}{$i bgrasse.inc} 410 416 end; 411 417 end; … … 419 425 begin 420 426 asm 421 {$ i sseloadv.inc}427 {$DEFINE SSE_LOADV}{$i bgrasse.inc} 422 428 movaps xmm2, xmm1 423 429 mulps xmm2, xmm2 … … 451 457 rsqrtps xmm2, xmm2 452 458 mulps xmm1, xmm2 //apply 453 {$ i ssesavev.inc}459 {$DEFINE SSE_SAVEV}{$i bgrasse.inc} 454 460 end; 455 461 end -
GraphicTest/Packages/bgrabitmap/bgrastreamlayers.pas
r472 r494 17 17 implementation 18 18 19 uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp; 19 uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp, 20 BGRAUTF8; 20 21 21 22 procedure SaveLayeredBitmapToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap); … … 37 38 StreamMaxLayerCount = 4096; 38 39 StreamMaxHeaderSize = 256; 39 40 {$i winstream.inc}41 40 42 41 function CheckStreamForLayers(AStream: TStream): boolean; … … 91 90 92 91 //header size 93 HeaderSize:= WinReadLongint(AStream);92 HeaderSize:= LEReadLongint(AStream); 94 93 if (HeaderSize < 12) or (HeaderSize > StreamMaxHeaderSize) then 95 94 raise exception.Create('Invalid header size'); 96 95 LayerStackStartPosition := AStream.Position + HeaderSize; 97 96 98 NbLayers:= WinReadLongint(AStream);97 NbLayers:= LEReadLongint(AStream); 99 98 if (NbLayers < 0) or (NbLayers > StreamMaxLayerCount) then 100 99 raise exception.Create('Invalid layer count'); 101 100 102 ASelectedLayerIndex:= WinReadLongint(AStream);101 ASelectedLayerIndex:= LEReadLongint(AStream); 103 102 if (ASelectedLayerIndex < -1) or (ASelectedLayerIndex >= NbLayers) then 104 103 raise exception.Create('Selected layer out of bounds'); 105 104 106 StackOption := WinReadLongint(AStream);105 StackOption := LEReadLongint(AStream); 107 106 result.LinearBlend := (StackOption and 1) = 1; 108 107 if (StackOption and 2) = 2 then Compression := lzpRLE else Compression:= lzpZStream; … … 112 111 for i := 0 to NbLayers-1 do 113 112 begin 114 LayerHeaderSize:= WinReadLongint(AStream);113 LayerHeaderSize:= LEReadLongint(AStream); 115 114 LayerHeaderPosition := AStream.Position; 116 115 LayerBitmapPosition := LayerHeaderPosition + LayerHeaderSize; … … 126 125 if AStream.Position <= LayerBitmapPosition-4 then 127 126 begin 128 LayerOption := WinReadLongint(AStream);127 LayerOption := LEReadLongint(AStream); 129 128 LayerVisible := (LayerOption and 1) = 1; 130 129 end; 131 130 if AStream.Position <= LayerBitmapPosition-4 then 132 LayerBlendOp := TBlendOperation( WinReadLongint(AStream));131 LayerBlendOp := TBlendOperation(LEReadLongint(AStream)); 133 132 134 133 if AStream.Position <= LayerBitmapPosition-8 then 135 134 begin 136 LayerOffset := Point( WinReadLongint(AStream),WinReadLongint(AStream));135 LayerOffset := Point(LEReadLongint(AStream),LEReadLongint(AStream)); 137 136 if AStream.Position <= LayerBitmapPosition-4 then 138 137 begin 139 LayerId := WinReadLongint(AStream);138 LayerId := LEReadLongint(AStream); 140 139 LayerIdFound := true; 141 140 end; 142 141 if AStream.Position <= LayerBitmapPosition-4 then 143 LayerOpacity := WinReadLongint(AStream) shr 8;142 LayerOpacity := LEReadLongint(AStream) shr 8; 144 143 end; 145 144 if AStream.Position <= LayerBitmapPosition-4 then 146 145 begin 147 LayerBitmapSize := WinReadLongint(AStream);146 LayerBitmapSize := LEReadLongint(AStream); 148 147 LayerEndPosition:= LayerBitmapPosition+LayerBitmapSize; 149 148 end; … … 186 185 raise exception.Create('Selected layer out of bounds'); 187 186 AStream.Write(StreamHeader[1], length(StreamHeader)); 188 WinWriteLongint(AStream, 12); //header size189 WinWriteLongint(AStream, ALayers.NbLayers);190 WinWriteLongint(AStream, ASelectedLayerIndex);187 LEWriteLongint(AStream, 12); //header size 188 LEWriteLongint(AStream, ALayers.NbLayers); 189 LEWriteLongint(AStream, ASelectedLayerIndex); 191 190 StackOption := 0; 192 191 if ALayers.LinearBlend then StackOption := StackOption or 1; 193 192 if ACompression = lzpRLE then StackOption:= StackOption or 2; 194 WinWriteLongint(AStream, StackOption);193 LEWriteLongint(AStream, StackOption); 195 194 //end of header 196 195 … … 198 197 begin 199 198 LayerHeaderSizePosition:= AStream.Position; 200 WinWriteLongint(AStream, 0); //header size not computed yet199 LEWriteLongint(AStream, 0); //header size not computed yet 201 200 LayerHeaderPosition := AStream.Position; 202 201 203 202 LayerOption := 0; 204 203 if ALayers.LayerVisible[i] then LayerOption:= LayerOption or 1; 205 WinWriteLongint(AStream, LayerOption);206 WinWriteLongint(AStream, Longint(ALayers.BlendOperation[i]));207 WinWriteLongint(AStream, ALayers.LayerOffset[i].x);208 WinWriteLongint(AStream, ALayers.LayerOffset[i].y);209 WinWriteLongint(AStream, ALayers.LayerUniqueId[i]);210 WinWriteLongint(AStream, integer(ALayers.LayerOpacity[i])*$101);204 LEWriteLongint(AStream, LayerOption); 205 LEWriteLongint(AStream, Longint(ALayers.BlendOperation[i])); 206 LEWriteLongint(AStream, ALayers.LayerOffset[i].x); 207 LEWriteLongint(AStream, ALayers.LayerOffset[i].y); 208 LEWriteLongint(AStream, ALayers.LayerUniqueId[i]); 209 LEWriteLongint(AStream, integer(ALayers.LayerOpacity[i])*$101); 211 210 LayerBitmapSizePosition:=AStream.Position; 212 WinWriteLongint(AStream, 0);211 LEWriteLongint(AStream, 0); 213 212 LayerBitmapPosition:=AStream.Position; 214 213 LayerHeaderSize := LayerBitmapPosition - LayerHeaderPosition; 215 214 AStream.Position:= LayerHeaderSizePosition; 216 WinWriteLongint(AStream, LayerHeaderSize);215 LEWriteLongint(AStream, LayerHeaderSize); 217 216 //end of layer header 218 217 … … 231 230 raise exception.Create('Image too big'); 232 231 AStream.Position:= LayerBitmapSizePosition; 233 WinWriteLongint(AStream, BitmapSize);232 LEWriteLongint(AStream, BitmapSize); 234 233 AStream.Position:= LayerBitmapPosition+BitmapSize; 235 234 end; -
GraphicTest/Packages/bgrabitmap/bgrasvg.pas
r472 r494 6 6 7 7 uses 8 Classes, SysUtils, BGRABitmapTypes, laz2_DOM, BGRAUnits, BGRASVGShapes, BGRACanvas2D; 8 Classes, SysUtils, BGRABitmapTypes, laz2_DOM, BGRAUnits, BGRASVGShapes, 9 BGRACanvas2D; 9 10 10 11 type … … 64 65 function GetPreserveAspectRatio: string; 65 66 function GetViewBox: TSVGViewBox; 67 function GetViewBox(AUnit: TCSSUnit): TSVGViewBox; 68 procedure GetViewBoxIndirect(AUnit: TCSSUnit; out AViewBox: TSVGViewBox); 66 69 function GetWidth: TFloatWithCSSUnit; 67 70 function GetWidthAsCm: single; … … 87 90 FContent: TSVGContent; 88 91 procedure Init(ACreateEmpty: boolean); 89 procedure InternalDraw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit; destDpi: TPointF); overload; 90 procedure InternalDraw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit; destDpi: single); overload; 92 function GetViewBoxAlignment(AHorizAlign: TAlignment; AVertAlign: TTextLayout): TPointF; 91 93 public 92 94 constructor Create; overload; … … 100 102 procedure SaveToFile(AFilenameUTF8: string); 101 103 procedure SaveToStream(AStream: TStream); 104 procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; AUnit: TCSSUnit = cuPixel); overload; 105 procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; destDpi: single); overload; 106 procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; destDpi: TPointF); overload; 107 procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit = cuPixel); overload; 102 108 procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; destDpi: single); overload; 103 109 procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; destDpi: TPointF); overload; 104 procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit); overload; 110 procedure StretchDraw(ACanvas2d: TBGRACanvas2D; x,y,w,h: single); overload; 111 procedure StretchDraw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y,w,h: single); overload; 105 112 property Units: TSVGUnits read FUnits; 106 113 property Width: TFloatWithCSSUnit read GetWidth write SetWidth; … … 112 119 property Zoomable: boolean read GetZoomable write SetZoomable; 113 120 property ViewBox: TSVGViewBox read GetViewBox write SetViewBox; 121 property ViewBoxInUnit[AUnit: TCSSUnit]: TSVGViewBox read GetViewBox; 114 122 property Attribute[AName: string]: string read GetAttribute write SetAttribute; 115 123 property DefaultDpi: single read FDefaultDpi write SetDefaultDpi; //this is not saved in the SVG file … … 121 129 implementation 122 130 123 uses laz2_XMLRead, laz2_XMLWrite, lazutf8classes, BGRATransform;131 uses laz2_XMLRead, laz2_XMLWrite, BGRAUTF8; 124 132 125 133 const SvgNamespace = 'http://www.w3.org/2000/svg'; … … 168 176 function parseNextFloat: single; 169 177 var 170 idxSpace, errPos: integer;178 idxSpace,{%H-}errPos: integer; 171 179 begin 172 180 idxSpace:= pos(' ',viewBoxStr); … … 335 343 end; 336 344 345 function TBGRASVG.GetViewBox(AUnit: TCSSUnit): TSVGViewBox; 346 begin 347 GetViewBoxIndirect(AUnit,result); 348 end; 349 350 procedure TBGRASVG.GetViewBoxIndirect(AUnit: TCSSUnit; out AViewBox: TSVGViewBox); 351 begin 352 with FUnits.ViewBox do 353 begin 354 AViewBox.min := FUnits.ConvertCoord(min,cuCustom,AUnit); 355 AViewBox.size := FUnits.ConvertCoord(size,cuCustom,AUnit); 356 end; 357 end; 358 337 359 function TBGRASVG.GetWidth: TFloatWithCSSUnit; 338 360 begin … … 443 465 end; 444 466 467 function TBGRASVG.GetViewBoxAlignment(AHorizAlign: TAlignment; 468 AVertAlign: TTextLayout): TPointF; 469 var vb: TSVGViewBox; 470 begin 471 GetViewBoxIndirect(cuPixel, vb); 472 with vb do 473 begin 474 case AHorizAlign of 475 taCenter: result.x := -(min.x+size.x*0.5); 476 taRightJustify: result.x := -(min.x+size.x); 477 else 478 {taLeftJustify:} result.x := -min.x; 479 end; 480 case AVertAlign of 481 tlCenter: result.y := -(min.y+size.y*0.5); 482 tlBottom: result.y := -(min.y+size.y); 483 else 484 {tlTop:} result.y := -min.y; 485 end; 486 end; 487 end; 488 445 489 constructor TBGRASVG.Create; 446 490 begin … … 547 591 end; 548 592 549 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; AUnit: TCSSUnit); 593 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; 594 AVertAlign: TTextLayout; x, y: single; AUnit: TCSSUnit); 550 595 var prevMatrix: TAffineMatrix; 551 596 begin 552 if (x<>0) or (y<>0) then553 begin554 prevMatrix := ACanvas2d.matrix;555 ACanvas2d.translate(x,y);556 Content.Draw(ACanvas2d,AUnit);557 ACanvas2d.matrix := prevMatrix;558 end else559 Content.Draw(ACanvas2d,AUnit);560 end;561 562 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: TPointF);563 begin564 InternalDraw(ACanvas2d,x,y,cuPixel,destDpi);565 end;566 567 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: single);568 begin569 InternalDraw(ACanvas2d,x,y,cuPixel,destDpi);570 end;571 572 procedure TBGRASVG.InternalDraw(ACanvas2d: TBGRACanvas2D; x, y: single;573 AUnit: TCSSUnit; destDpi: TPointF);574 var prevMatrix: TAffineMatrix;575 begin576 if (Units.DpiX = 0) or (Units.DpiY = 0) then exit;577 597 prevMatrix := ACanvas2d.matrix; 578 598 ACanvas2d.translate(x,y); 579 if AUnit = cuPixel then 580 ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY); 599 with GetViewBoxAlignment(AHorizAlign,AVertAlign) do ACanvas2d.translate(x,y); 600 Draw(ACanvas2d, 0,0, AUnit); 601 ACanvas2d.matrix := prevMatrix; 602 end; 603 604 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; 605 AVertAlign: TTextLayout; x, y: single; destDpi: single); 606 begin 607 Draw(ACanvas2d, AHorizAlign,AVertAlign, x,y, PointF(destDpi,destDpi)); 608 end; 609 610 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; 611 AVertAlign: TTextLayout; x, y: single; destDpi: TPointF); 612 begin 613 ACanvas2d.save; 614 ACanvas2d.translate(x,y); 615 ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY); 616 ACanvas2d.strokeResetTransform; 617 ACanvas2d.strokeScale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY); 618 with GetViewBoxAlignment(AHorizAlign,AVertAlign) do ACanvas2d.translate(x,y); 619 Draw(ACanvas2d, 0,0, cuPixel); 620 ACanvas2d.restore; 621 end; 622 623 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; AUnit: TCSSUnit); 624 var prevLinearBlend: boolean; 625 begin 626 prevLinearBlend:= ACanvas2d.linearBlend; 627 acanvas2d.linearBlend := true; 628 ACanvas2d.save; 629 ACanvas2d.translate(x,y); 581 630 Content.Draw(ACanvas2d,AUnit); 582 ACanvas2d.matrix := prevMatrix; 583 end; 584 585 procedure TBGRASVG.InternalDraw(ACanvas2d: TBGRACanvas2D; x, y: single; 586 AUnit: TCSSUnit; destDpi: single); 587 begin 588 InternalDraw(ACanvas2d,x,y,AUnit,PointF(destDpi,destDpi)); 631 ACanvas2d.restore; 632 ACanvas2d.linearBlend := prevLinearBlend; 633 end; 634 635 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: single); 636 begin 637 Draw(ACanvas2d, x,y, PointF(destDpi,destDpi)); 638 end; 639 640 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: TPointF); 641 begin 642 ACanvas2d.save; 643 ACanvas2d.translate(x,y); 644 ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY); 645 ACanvas2d.strokeResetTransform; 646 ACanvas2d.strokeScale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY); 647 Draw(ACanvas2d, 0,0, cuPixel); 648 ACanvas2d.restore; 649 end; 650 651 procedure TBGRASVG.StretchDraw(ACanvas2d: TBGRACanvas2D; x, y, w, h: single); 652 var vb: TSVGViewBox; 653 begin 654 ACanvas2d.save; 655 ACanvas2d.translate(x,y); 656 ACanvas2d.strokeResetTransform; 657 GetViewBoxIndirect(cuPixel,vb); 658 with vb do 659 begin 660 ACanvas2d.translate(-min.x,-min.y); 661 if size.x <> 0 then 662 begin 663 ACanvas2d.scale(w/size.x,1); 664 ACanvas2d.strokeScale(w/size.x,1); 665 end; 666 if size.y <> 0 then 667 begin 668 ACanvas2d.scale(1,h/size.y); 669 ACanvas2d.strokeScale(1,h/size.y); 670 end; 671 end; 672 Draw(ACanvas2d, 0,0); 673 ACanvas2d.restore; 674 end; 675 676 procedure TBGRASVG.StretchDraw(ACanvas2d: TBGRACanvas2D; 677 AHorizAlign: TAlignment; AVertAlign: TTextLayout; x, y, w, h: single); 678 var ratio,stretchRatio,zoom: single; 679 vb: TSVGViewBox; 680 sx,sy,sw,sh: single; 681 begin 682 GetViewBoxIndirect(cuPixel,vb); 683 if (h = 0) or (w = 0) or (vb.size.x = 0) or (vb.size.y = 0) then exit; 684 ratio := vb.size.x/vb.size.y; 685 stretchRatio := w/h; 686 if ratio > stretchRatio then 687 zoom := w / vb.size.x 688 else 689 zoom := h / vb.size.y; 690 691 sx := x; 692 sy := y; 693 sw := vb.size.x*zoom; 694 sh := vb.size.y*zoom; 695 696 case AHorizAlign of 697 taCenter: sx += (w - sw)/2; 698 taRightJustify: sx += w - sw; 699 end; 700 case AVertAlign of 701 tlCenter: sy += (h - sh)/2; 702 tlBottom: sy += h - sh; 703 end; 704 StretchDraw(ACanvas2d, sx,sy,sw,sh); 589 705 end; 590 706 -
GraphicTest/Packages/bgrabitmap/bgrasvgshapes.pas
r472 r494 243 243 implementation 244 244 245 uses BGRATransform, Graphics;245 uses BGRATransform, BGRAGraphics; 246 246 247 247 function GetSVGFactory(ATagName: string): TSVGFactory; … … 414 414 if not isStrokeNone then 415 415 begin 416 A Canvas2d.strokeStyle(strokeColor);416 ApplyStrokeStyle(ACanvas2D,AUnit); 417 417 ACanvas2d.stroke; 418 418 end; … … 533 533 if not isStrokeNone then 534 534 begin 535 A Canvas2d.strokeStyle(strokeColor);535 ApplyStrokeStyle(ACanvas2D,AUnit); 536 536 ACanvas2d.stroke; 537 537 end; … … 629 629 if not isStrokeNone then 630 630 begin 631 A Canvas2d.strokeStyle(strokeColor);631 ApplyStrokeStyle(ACanvas2D,AUnit); 632 632 ACanvas2d.stroke; 633 633 end; … … 717 717 if not isStrokeNone then 718 718 begin 719 A Canvas2d.strokeStyle(strokeColor);719 ApplyStrokeStyle(ACanvas2D,AUnit); 720 720 ACanvas2d.stroke; 721 721 end; … … 785 785 if not isStrokeNone then 786 786 begin 787 A Canvas2d.strokeStyle(strokeColor);787 ApplyStrokeStyle(ACanvas2D,AUnit); 788 788 ACanvas2d.stroke; 789 789 end; … … 842 842 if not isStrokeNone then 843 843 begin 844 A Canvas2d.strokeStyle(strokeColor);844 ApplyStrokeStyle(ACanvas2D,AUnit); 845 845 ACanvas2d.stroke; 846 846 end; … … 899 899 if not isStrokeNone then 900 900 begin 901 A Canvas2d.strokeStyle(strokeColor);901 ApplyStrokeStyle(ACanvas2D,AUnit); 902 902 ACanvas2d.beginPath; 903 903 ACanvas2d.moveTo(Units.ConvertWidth(x1,AUnit).value,Units.ConvertWidth(y1,AUnit).value); -
GraphicTest/Packages/bgrabitmap/bgrasvgtype.pas
r472 r494 26 26 function GetIsStrokeNone: boolean; 27 27 function GetMatrix(AUnit: TCSSUnit): TAffineMatrix; 28 function GetOpacity: single; 28 29 function GetOrthoAttributeOrStyleWithUnit(AName: string 29 30 ): TFloatWithCSSUnit; 30 31 function GetStroke: string; 31 32 function GetStrokeColor: TBGRAPixel; 33 function GetStrokeLineCap: string; 34 function GetStrokeLineJoin: string; 35 function GetStrokeMiterLimit: single; 32 36 function GetStrokeOpacity: single; 33 37 function GetStrokeWidth: TFloatWithCSSUnit; … … 50 54 procedure SetHorizAttributeWithUnit(AName: string; AValue: TFloatWithCSSUnit); 51 55 procedure SetMatrix(AUnit: TCSSUnit; const AValue: TAffineMatrix); 56 procedure SetOpacity(AValue: single); 52 57 procedure SetStroke(AValue: string); 53 58 procedure SetStrokeColor(AValue: TBGRAPixel); 59 procedure SetStrokeLineCap(AValue: string); 60 procedure SetStrokeLineJoin(AValue: string); 61 procedure SetStrokeMiterLimit(AValue: single); 54 62 procedure SetStrokeOpacity(AValue: single); 55 63 procedure SetStrokeWidth(AValue: TFloatWithCSSUnit); … … 66 74 procedure InternalDraw({%H-}ACanvas2d: TBGRACanvas2D; {%H-}AUnit: TCSSUnit); virtual; 67 75 procedure LocateStyleDeclaration(AText: string; AProperty: string; out AStartPos,AColonPos,AValueLength: integer); 76 procedure ApplyStrokeStyle(ACanvas2D: TBGRACanvas2D; AUnit: TCSSUnit); 68 77 public 69 78 constructor Create({%H-}ADocument: TXMLDocument; AElement: TDOMElement; AUnits: TCSSUnitConverter); virtual; … … 93 102 property strokeColor: TBGRAPixel read GetStrokeColor write SetStrokeColor; 94 103 property strokeOpacity: single read GetStrokeOpacity write SetStrokeOpacity; 104 property strokeMiterLimit: single read GetStrokeMiterLimit write SetStrokeMiterLimit; 105 property strokeLineJoin: string read GetStrokeLineJoin write SetStrokeLineJoin; 106 property strokeLineCap: string read GetStrokeLineCap write SetStrokeLineCap; 95 107 property fill: string read GetFill write SetFill; 96 108 property fillColor: TBGRAPixel read GetFillColor write SetFillColor; 97 109 property fillOpacity: single read GetFillOpacity write SetFillOpacity; 110 property opacity: single read GetOpacity write SetOpacity; 98 111 end; 99 112 … … 123 136 implementation 124 137 125 uses Math;126 127 138 { TSVGParser } 128 139 … … 264 275 begin 265 276 result := StrToBGRA(fill,BGRABlack); 266 result.alpha := round(result.alpha*fillOpacity );277 result.alpha := round(result.alpha*fillOpacity*opacity); 267 278 if result.alpha = 0 then result := BGRAPixelTransparent; 268 279 end; … … 359 370 begin 360 371 angle := parser.ParseFloat; 361 result *= AffineMatrix(1,tan(angle*Pi/180),0, 362 0, 1, 0); 372 result *= AffineMatrixSkewXDeg(angle); 363 373 end else 364 374 if compareText(kind,'skewy')=0 then 365 375 begin 366 376 angle := parser.ParseFloat; 367 result *= AffineMatrix(1, 0 ,0, 368 tan(angle*Pi/180), 1, 0); 377 result *= AffineMatrixSkewYDeg(angle); 369 378 end; 370 379 parser.SkipUpToSymbol(')'); … … 375 384 end; 376 385 386 function TSVGElement.GetOpacity: single; 387 var errPos: integer; 388 begin 389 val(AttributeOrStyle['opacity'], result, errPos); 390 if errPos <> 0 then result := 1 else 391 if result < 0 then result := 0 else 392 if result > 1 then result := 1; 393 end; 394 377 395 function TSVGElement.GetOrthoAttributeOrStyleWithUnit(AName: string 378 396 ): TFloatWithCSSUnit; … … 390 408 begin 391 409 result := StrToBGRA(stroke); 392 result.alpha := round(result.alpha*strokeOpacity );410 result.alpha := round(result.alpha*strokeOpacity*opacity); 393 411 if result.alpha = 0 then result := BGRAPixelTransparent; 412 end; 413 414 function TSVGElement.GetStrokeLineCap: string; 415 begin 416 result := AttributeOrStyle['stroke-linecap']; 417 if result = '' then result := 'butt'; 418 end; 419 420 function TSVGElement.GetStrokeLineJoin: string; 421 begin 422 result := AttributeOrStyle['stroke-linejoin']; 423 if result = '' then result := 'miter'; 424 end; 425 426 function TSVGElement.GetStrokeMiterLimit: single; 427 var errPos: integer; 428 begin 429 val(AttributeOrStyle['stroke-miterlimit'], result, errPos); 430 if errPos <> 0 then result := 4 else 431 if result < 1 then result := 1; 394 432 end; 395 433 … … 405 443 function TSVGElement.GetStrokeWidth: TFloatWithCSSUnit; 406 444 begin 407 result := HorizAttributeOrStyleWithUnit['stroke-width'];445 result := OrthoAttributeOrStyleWithUnit['stroke-width']; 408 446 end; 409 447 … … 525 563 end; 526 564 565 procedure TSVGElement.SetOpacity(AValue: single); 566 begin 567 Attribute['opacity'] := Units.formatValue(AValue); 568 RemoveStyle('opacity'); 569 end; 570 527 571 procedure TSVGElement.SetStroke(AValue: string); 528 572 begin … … 536 580 AValue.alpha:= 255; 537 581 stroke := BGRAToStr(AValue, CSSColors); 582 end; 583 584 procedure TSVGElement.SetStrokeLineCap(AValue: string); 585 begin 586 Attribute['stroke-linecap'] := AValue; 587 RemoveStyle('stroke-linecap'); 588 end; 589 590 procedure TSVGElement.SetStrokeLineJoin(AValue: string); 591 begin 592 Attribute['stroke-linejoin'] := AValue; 593 RemoveStyle('stroke-linejoin'); 594 end; 595 596 procedure TSVGElement.SetStrokeMiterLimit(AValue: single); 597 begin 598 if AValue < 1 then AValue := 1; 599 Attribute['stroke-miterlimit'] := Units.formatValue(AValue); 600 RemoveStyle('stroke-miterlimit'); 538 601 end; 539 602 … … 671 734 if AText[i] = ';' then 672 735 begin 673 curValueLength := i- curColon;736 curValueLength := i-(curColon+1); 674 737 if CheckShouldReturnResult then exit; 675 738 curStart := -1; … … 680 743 if curColon <> -1 then 681 744 begin 682 curValueLength:= length(AText)- curColon;745 curValueLength:= length(AText)-(curColon+1)+1; 683 746 if CheckShouldReturnResult then exit; 684 747 end; 748 end; 749 750 procedure TSVGElement.ApplyStrokeStyle(ACanvas2D: TBGRACanvas2D; AUnit: TCSSUnit); 751 begin 752 ACanvas2d.strokeStyle(strokeColor); 753 ACanvas2d.lineWidth := Units.ConvertWidth(strokeWidth,AUnit).value; 754 ACanvas2d.lineCap := strokeLineCap; 755 ACanvas2d.lineJoin := strokeLineJoin; 756 ACanvas2d.miterLimit := strokeMiterLimit; 685 757 end; 686 758 -
GraphicTest/Packages/bgrabitmap/bgratext.pas
r472 r494 4 4 5 5 interface 6 7 {$IFDEF LINUX} 8 {$DEFINE LCL_RENDERER_IS_FINE} 9 {$DEFINE LCL_CLEARTYPE_RENDERER_IS_FINE} 10 {$ENDIF} 11 {$IFDEF FREEBSD} 12 {$DEFINE LCL_RENDERER_IS_FINE} 13 {$DEFINE LCL_CLEARTYPE_RENDERER_IS_FINE} 14 {$ENDIF} 15 {$IFDEF DARWIN} 16 {$DEFINE LCL_RENDERER_IS_FINE} 17 {$DEFINE RENDER_TEXT_ON_TBITMAP} 18 {$ENDIF} 6 19 7 20 { 8 21 Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType 9 22 10 This unit provides basic text rendering functions using LCL, and general 11 text definitions. 23 This unit provides basic text rendering functions using LCL. 12 24 13 25 Text functions use a temporary bitmap where the operating system text drawing is used. … … 20 32 21 33 uses 22 Classes, Types, SysUtils, Graphics, BGRABitmapTypes, InterfaceBase, BGRAPen, BGRAGrayscaleMask;34 Classes, Types, SysUtils, BGRAGraphics, BGRABitmapTypes, InterfaceBase, BGRAPen, BGRAGrayscaleMask; 23 35 24 36 type 25 TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;37 TWordBreakHandler = BGRABitmapTypes.TWordBreakHandler; 26 38 27 39 { TCustomLCLFontRenderer } … … 78 90 function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: integer): TSize; 79 91 function BGRAOriginalTextSizeEx(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer; out actualAntialiasingLevel: integer): TSize; 80 procedure BGRADefaultWordBreakHandler(var ABefore,AAfter: string);81 92 82 93 function BGRATextUnderline(ATopLeft: TPointF; AWidth: Single; AMetrics: TFontPixelMetric): ArrayOfTPointF; overload; … … 89 100 function FontFullHeightSign: integer; 90 101 function LCLFontAvailable: boolean; 102 function GetFineClearTypeAuto: TBGRAFontQuality; 91 103 92 104 procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true); 93 105 procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true); 94 106 procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x,y: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; KeepRGBOrder: boolean=true); 95 procedure BGRAInternalRenderText(dest: TBGRACustomBitmap; Quality: TBGRAFontQuality; grayscale: TGrayscaleMask; temp: TBGRACustomBitmap; 96 x,y,xThird: integer; c: TBGRAPixel; tex: IBGRAScanner); 97 98 const FontAntialiasingLevel = {$IFDEF LINUX}3{$ELSE}6{$ENDIF}; //linux rendering is already great 107 108 const FontAntialiasingLevel = {$IFDEF LCL_RENDERER_IS_FINE}3{$ELSE}6{$ENDIF}; 99 109 const FontDefaultQuality = fqAntialiased; 100 110 101 function GetFontPixelMetric(AFont: TFont): TFontPixelMetric; 111 function GetLCLFontPixelMetric(AFont: TFont): TFontPixelMetric; 112 113 var 114 BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode); 102 115 103 116 implementation 104 117 105 uses GraphType, Math, BGRABlend, LCLProc;118 uses GraphType, Math, BGRABlend, BGRAUTF8; 106 119 107 120 const MaxPixelMetricCount = 100; … … 110 123 LCLFontDisabledValue: boolean; 111 124 TempBmp: TBitmap; 125 fqFineClearTypeComputed: boolean; 126 fqFineClearTypeValue: TBGRAFontQuality; 112 127 FontHeightSignComputed: boolean; 113 128 FontHeightSignValue: integer; … … 264 279 end; 265 280 266 function Get FontPixelMetric(AFont: TFont): TFontPixelMetric;281 function GetLCLFontPixelMetric(AFont: TFont): TFontPixelMetric; 267 282 var i,startPos,endPos: integer; 268 283 begin … … 376 391 end; 377 392 393 function GetFineClearTypeAuto: TBGRAFontQuality; 394 var 395 lclBmp: TBitmap; 396 bgra: TBGRACustomBitmap; 397 x,y: integer; 398 begin 399 if fqFineClearTypeComputed then 400 begin 401 result:= fqFineClearTypeValue; 402 exit; 403 end; 404 result := fqFineAntialiasing; 405 if not LCLFontDisabledValue and not (WidgetSet.LCLPlatform = lpNoGUI) then 406 begin 407 lclBmp := TBitmap.Create; 408 lclBmp.Canvas.FillRect(0,0,lclBmp.Width,lclBmp.Height); 409 lclBmp.Canvas.Font.Height := -50; 410 lclBmp.Canvas.Font.Quality := fqCleartype; 411 with lclBmp.Canvas.TextExtent('/') do 412 begin 413 lclBmp.Width := cx; 414 lclBmp.Height := cy; 415 end; 416 lclBmp.Canvas.TextOut(0,0,'/'); 417 bgra:= BGRABitmapFactory.Create(lclBmp); 418 x:= bgra.Width div 2; 419 for y := 0 to bgra.Height-1 do 420 with bgra.GetPixel(x,y) do 421 if (red<>blue) then 422 begin 423 if blue < red then 424 result:= fqFineClearTypeRGB 425 else 426 result:= fqFineClearTypeBGR; 427 break; 428 end else 429 if (green = 0) then break; 430 lclBmp.Free; 431 end; 432 fqFineClearTypeValue := result; 433 fqFineClearTypeComputed:= true; 434 end; 435 378 436 function FontEmHeightSign: integer; 379 437 begin … … 390 448 if not FontHeightSignComputed then GetFontHeightSign; 391 449 result := not LCLFontDisabledValue; 392 end;393 394 procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);395 var396 pdest: PBGRAPixel;397 ClearTypePixel: array[0..2] of byte;398 curThird: integer;399 400 procedure OutputPixel; inline;401 begin402 if texture <> nil then403 color := texture.ScanNextPixel;404 if RGBOrder then405 ClearTypeDrawPixel(pdest, ClearTypePixel[0],ClearTypePixel[1],ClearTypePixel[2], color)406 else407 ClearTypeDrawPixel(pdest, ClearTypePixel[2],ClearTypePixel[1],ClearTypePixel[0], color);408 end;409 410 procedure NextAlpha(alphaValue: byte); inline;411 begin412 ClearTypePixel[curThird] := alphaValue;413 inc(curThird);414 if curThird = 3 then415 begin416 OutputPixel;417 curThird := 0;418 Fillchar(ClearTypePixel, sizeof(ClearTypePixel),0);419 inc(pdest);420 end;421 end;422 423 procedure EndRow; inline;424 begin425 if curThird > 0 then OutputPixel;426 end;427 428 var429 yMask,n: integer;430 a: byte;431 pmask: PByte;432 dx:integer;433 miny,maxy,minx,minxThird,maxx,alphaMinX,alphaMaxX,alphaLineLen: integer;434 leftOnSide, rightOnSide: boolean;435 countBetween: integer;436 v1,v2,v3: byte;437 438 procedure StartRow; inline;439 begin440 pdest := dest.Scanline[yMask+y]+minx;441 if texture <> nil then442 texture.ScanMoveTo(minx,yMask+y);443 444 curThird := minxThird;445 ClearTypePixel[0] := 0;446 ClearTypePixel[1] := 0;447 ClearTypePixel[2] := 0;448 end;449 450 begin451 alphaLineLen := maskWidth+2;452 453 xThird -= 1; //for first subpixel454 455 if xThird >= 0 then dx := xThird div 3456 else dx := -((-xThird+2) div 3);457 x += dx;458 xThird -= dx*3;459 460 if y >= dest.ClipRect.Top then miny := 0461 else miny := dest.ClipRect.Top-y;462 if y+maskHeight-1 < dest.ClipRect.Bottom then463 maxy := maskHeight-1 else464 maxy := dest.ClipRect.Bottom-1-y;465 466 if x >= dest.ClipRect.Left then467 begin468 minx := x;469 minxThird := xThird;470 alphaMinX := 0;471 leftOnSide := false;472 end else473 begin474 minx := dest.ClipRect.Left;475 minxThird := 0;476 alphaMinX := (dest.ClipRect.Left-x)*3 - xThird;477 leftOnSide := true;478 end;479 480 if x*3+xThird+maskWidth-1 < dest.ClipRect.Right*3 then481 begin482 maxx := (x*3+xThird+maskWidth-1) div 3;483 alphaMaxX := alphaLineLen-1;484 rightOnSide := false;485 end else486 begin487 maxx := dest.ClipRect.Right-1;488 alphaMaxX := maxx*3+2 - (x*3+xThird);489 rightOnSide := true;490 end;491 492 countBetween := alphaMaxX-alphaMinX-1;493 494 if (alphaMinX <= alphaMaxX) then495 begin496 for yMask := miny to maxy do497 begin498 StartRow;499 500 if leftOnSide then501 begin502 pmask := maskData + (yMask*maskRowSize)+ (alphaMinX-1)*maskPixelSize;503 a := pmask^ div 3;504 v1 := a+a;505 v2 := a;506 v3 := 0;507 inc(pmask, maskPixelSize);508 end else509 begin510 pmask := maskData + (yMask*maskRowSize);511 v1 := 0;512 v2 := 0;513 v3 := 0;514 end;515 516 for n := countBetween-1 downto 0 do517 begin518 a := pmask^ div 3;519 v1 += a;520 v2 += a;521 v3 += a;522 inc(pmask, maskPixelSize);523 524 NextAlpha(v1);525 v1 := v2;526 v2 := v3;527 v3 := 0;528 end;529 530 if rightOnSide then531 begin532 a := pmask^ div 3;533 v1 += a;534 v2 += a+a;535 end;536 537 NextAlpha(v1);538 NextAlpha(v2);539 540 EndRow;541 end;542 end;543 450 end; 544 451 … … 546 453 y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; 547 454 texture: IBGRAScanner; RGBOrder: boolean); 548 var delta: NativeInt; 549 begin 550 delta := mask.Width; 551 BGRAFillClearTypeMaskPtr(dest,x,y,xThird,mask.ScanLine[0],1,delta,mask.Width,mask.Height,color,texture,RGBOrder); 455 begin 456 BGRAGrayscaleMask.BGRAFillClearTypeGrayscaleMask(dest,x,y,xThird,mask,color,texture,RGBOrder); 552 457 end; 553 458 554 459 procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean); 555 var delta: NativeInt; 556 begin 557 delta := mask.Width*sizeof(TBGRAPixel); 558 if mask.LineOrder = riloBottomToTop then 559 delta := -delta; 560 BGRAFillClearTypeMaskPtr(dest,x,y,xThird,pbyte(mask.ScanLine[0])+1,sizeof(TBGRAPixel),delta,mask.Width,mask.Height,color,texture,RGBOrder); 460 begin 461 BGRABlend.BGRAFillClearTypeMask(dest,x,y,xThird,mask,color,texture,RGBOrder); 561 462 end; 562 463 … … 564 465 mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; 565 466 KeepRGBOrder: boolean); 566 var 567 minx,miny,maxx,maxy,countx,n,yb: integer; 568 pdest,psrc: PBGRAPixel; 569 begin 570 if y >= dest.ClipRect.Top then miny := 0 571 else miny := dest.ClipRect.Top-y; 572 if y+mask.Height-1 < dest.ClipRect.Bottom then 573 maxy := mask.Height-1 else 574 maxy := dest.ClipRect.Bottom-1-y; 575 576 if x >= dest.ClipRect.Left then minx := 0 577 else minx := dest.ClipRect.Left-x; 578 if x+mask.Width-1 < dest.ClipRect.Right then 579 maxx := mask.Width-1 else 580 maxx := dest.ClipRect.Right-1-x; 581 582 countx := maxx-minx+1; 583 if countx <= 0 then exit; 584 585 for yb := miny to maxy do 586 begin 587 pdest := dest.ScanLine[y+yb]+(x+minx); 588 psrc := mask.ScanLine[yb]+minx; 589 if texture <> nil then 590 texture.ScanMoveTo(x+minx, y+yb); 591 if KeepRGBOrder then 592 begin 593 for n := countx-1 downto 0 do 594 begin 595 if texture <> nil then color := texture.ScanNextPixel; 596 ClearTypeDrawPixel(pdest, psrc^.red, psrc^.green, psrc^.blue, color); 597 inc(pdest); 598 inc(psrc); 599 end; 600 end else 601 begin 602 for n := countx-1 downto 0 do 603 begin 604 if texture <> nil then color := texture.ScanNextPixel; 605 ClearTypeDrawPixel(pdest, psrc^.blue, psrc^.green, psrc^.red, color); 606 inc(pdest); 607 inc(psrc); 608 end; 609 end; 610 end; 467 begin 468 BGRABlend.BGRAFillClearTypeRGBMask(dest,x,y,mask,color,texture,KeepRGBOrder); 611 469 end; 612 470 … … 649 507 end; 650 508 651 procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);652 var p: integer;653 begin654 if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then655 begin656 p := length(ABefore);657 while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);658 if p > 1 then //can put the word after659 begin660 AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;661 ABefore := copy(ABefore,1,p-1);662 end else663 begin //cannot put the word after, so before664 665 end;666 end;667 while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);668 while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);669 end;670 671 509 function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer): TSize; 672 510 begin … … 735 573 736 574 function CleanTextOutString(s: string): string; 737 var idxIn, idxOut: integer; 738 begin 739 setlength(result, length(s)); 740 idxIn := 1; 741 idxOut := 1; 742 while IdxIn <= length(s) do 743 begin 744 if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8 745 begin 746 result[idxOut] := s[idxIn]; 747 inc(idxOut); 748 end; 749 inc(idxIn); 750 end; 751 setlength(result, idxOut-1); 575 begin 576 result := BGRABitmapTypes.CleanTextOutString(s); 752 577 end; 753 578 754 579 function RemoveLineEnding(var s: string; indexByte: integer): boolean; 755 begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long 756 //so this function can be applied to UTF8 strings as well 757 result := false; 758 if length(s) >= indexByte then 759 begin 760 if s[indexByte] in[#13,#10] then 761 begin 762 result := true; 763 if length(s) >= indexByte+1 then 764 begin 765 if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then 766 delete(s,indexByte,2) 767 else 768 delete(s,indexByte,1); 769 end 770 else 771 delete(s,indexByte,1); 772 end; 773 end; 580 begin 581 result := BGRABitmapTypes.RemoveLineEnding(s, indexByte); 774 582 end; 775 583 776 584 function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean; 777 var indexByte: integer; 778 pIndex: PChar; 779 begin 780 pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8); 781 if pIndex = nil then 782 begin 783 result := false; 784 exit; 785 end; 786 indexByte := pIndex - @sUTF8[1]; 787 result := RemoveLineEnding(sUTF8, indexByte); 585 begin 586 result := BGRABitmapTypes.RemoveLineEndingUTF8(sUTF8,indexUTF8); 788 587 end; 789 588 … … 816 615 size: TSize; 817 616 temp: TBGRACustomBitmap; 617 {$IFDEF RENDER_TEXT_ON_TBITMAP} 618 tempLCL: TBitmap; 619 {$ENDIF} 818 620 xMargin,xThird: integer; 819 621 tempSize: TSize; … … 835 637 end; 836 638 639 {$IFDEF LCL_RENDERER_IS_FINE} 640 if (Quality in [fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR]) and 641 (BGRATextSize(Font, fqSystem, 'Hg', 1).cy >= 13) then 642 begin 643 if Quality = fqFineAntialiasing then Quality := fqSystem; 644 {$IFDEF LCL_CLEARTYPE_RENDERER_IS_FINE} 645 if Quality = GetFineClearTypeAuto then Quality := fqSystemClearType; 646 {$ENDIF} 647 end; 648 {$ENDIF} 649 837 650 size := BGRAOriginalTextSizeEx(Font,Quality,sUTF8,CustomAntialiasingLevel,sizeFactor); 838 651 if (size.cx = 0) or (size.cy = 0) then … … 890 703 tempSize.cx += xMargin*2; 891 704 705 {$IFDEF RENDER_TEXT_ON_TBITMAP} 706 tempLCL := TBitmap.Create; 707 tempLCL.Width := tempSize.cx; 708 tempLCL.Height := tempSize.cy; 709 tempLCL.Canvas.Brush.Color := clBlack; 710 tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height); 711 with tempLCL do begin 712 {$ELSE} 892 713 temp := bmp.NewBitmap(tempSize.cx, tempSize.cy, BGRABlack); 893 temp.Canvas.Font := Font; 894 temp.Canvas.Font.Height := Font.Height*sizeFactor; 895 temp.Canvas.Font.Color := clWhite; 896 temp.Canvas.Brush.Style := bsClear; 897 temp.Canvas.TextOut(xMargin+subX, subY, sUTF8); 714 with temp do begin 715 {$ENDIF} 716 Canvas.Font := Font; 717 Canvas.Font.Height := Font.Height*sizeFactor; 718 Canvas.Font.Color := clWhite; 719 Canvas.Brush.Style := bsClear; 720 Canvas.TextOut(xMargin+subX, subY, sUTF8); 721 end; 722 {$IFDEF RENDER_TEXT_ON_TBITMAP} 723 temp := BGRABitmapFactory.create(tempLCL,False); 724 tempLCL.Free; 725 {$ENDIF} 898 726 899 727 FilterOriginalText(Quality,CustomAntialiasingLevel, temp, grayscale); … … 921 749 oldOrientation: integer; 922 750 grayscale:TGrayscaleMask; 751 {$IFDEF RENDER_TEXT_ON_TBITMAP} 752 tempLCL: TBitmap; 753 {$ENDIF} 923 754 924 755 procedure rotBoundsAdd(pt: TPointF); … … 994 825 if deltaY <> 0 then rotBounds.Bottom += sizeFactor; 995 826 827 {$IFDEF RENDER_TEXT_ON_TBITMAP} 828 tempLCL := TBitmap.Create; 829 tempLCL.Width := rotBounds.Right-rotBounds.Left; 830 tempLCL.Height := rotBounds.Bottom-rotBounds.Top; 831 tempLCL.Canvas.Brush.Color := clBlack; 832 tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height); 833 with tempLCL do begin 834 {$ELSE} 996 835 temp := bmp.NewBitmap(rotBounds.Right-rotBounds.Left,rotBounds.Bottom-rotBounds.Top, BGRABlack); 997 temp.Canvas.Font := Font; 998 temp.Canvas.Font.Color := clWhite; 999 temp.Canvas.Font.Orientation := orientationTenthDegCCW; 1000 temp.Canvas.Font.Height := round(Font.Height*sizeFactor); 1001 temp.Canvas.Brush.Style := bsClear; 1002 temp.Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, sUTF8); 836 with temp do begin 837 {$ENDIF} 838 Canvas.Font := Font; 839 Canvas.Font.Color := clWhite; 840 Canvas.Font.Orientation := orientationTenthDegCCW; 841 Canvas.Font.Height := round(Font.Height*sizeFactor); 842 Canvas.Brush.Style := bsClear; 843 Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, sUTF8); 844 end; 845 {$IFDEF RENDER_TEXT_ON_TBITMAP} 846 temp := BGRABitmapFactory.create(tempLCL,False); 847 tempLCL.Free; 848 {$ENDIF} 1003 849 1004 850 FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale); … … 1017 863 cr: TRect; 1018 864 grayscale:TGrayscaleMask; 865 {$IFDEF RENDER_TEXT_ON_TBITMAP} 866 tempLCL: TBitmap; 867 {$ENDIF} 1019 868 begin 1020 869 if not LCLFontAvailable then exit; … … 1038 887 exit; 1039 888 889 {$IFDEF LCL_RENDERER_IS_FINE} 890 if (Quality in [fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR]) and 891 (BGRATextSize(Font, fqSystem, 'Hg', 1).cy >= 13) then 892 begin 893 if Quality = fqFineAntialiasing then Quality := fqSystem; 894 {$IFDEF LCL_CLEARTYPE_RENDERER_IS_FINE} 895 if Quality = GetFineClearTypeAuto then Quality := fqSystemClearType; 896 {$ENDIF} 897 end; 898 {$ENDIF} 899 1040 900 if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then 1041 901 sizeFactor := CustomAntialiasingLevel … … 1043 903 sizeFactor := 1; 1044 904 905 {$IFDEF RENDER_TEXT_ON_TBITMAP} 906 tempLCL := TBitmap.Create; 907 tempLCL.Width := tx*sizeFactor; 908 tempLCL.Height := ty*sizeFactor; 909 tempLCL.Canvas.Brush.Color := clBlack; 910 tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height); 911 with tempLCL do begin 912 {$ELSE} 1045 913 temp := bmp.NewBitmap(tx*sizeFactor, ty*sizeFactor, BGRABlack); 1046 temp.Canvas.Font := Font; 1047 temp.Canvas.Font.Orientation := 0; 1048 if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then temp.Canvas.Font.Height := Font.Height*CustomAntialiasingLevel 1049 else temp.Canvas.Font.Height := Font.Height; 1050 temp.Canvas.Font.Color := clWhite; 1051 temp.Canvas.Brush.Style := bsClear; 1052 temp.Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, sUTF8, style); 914 with temp do begin 915 {$ENDIF} 916 Canvas.Font := Font; 917 Canvas.Font.Orientation := 0; 918 if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then Canvas.Font.Height := Font.Height*CustomAntialiasingLevel 919 else Canvas.Font.Height := Font.Height; 920 Canvas.Font.Color := clWhite; 921 Canvas.Brush.Style := bsClear; 922 Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, sUTF8, style); 923 end; 924 {$IFDEF RENDER_TEXT_ON_TBITMAP} 925 temp := BGRABitmapFactory.create(tempLCL,False); 926 tempLCL.Free; 927 {$ENDIF} 1053 928 1054 929 FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale); … … 1163 1038 UpdateFont; 1164 1039 if FontQuality in[fqSystem,fqSystemClearType] then 1165 result := BGRAText.GetFontPixelMetric(FFont)1040 result := GetLCLFontPixelMetric(FFont) 1166 1041 else 1167 1042 begin … … 1169 1044 FxFont.Assign(FFont); 1170 1045 FxFont.Height := fxFont.Height*FontAntialiasingLevel; 1171 Result:= BGRAText.GetFontPixelMetric(FxFont);1046 Result:= GetLCLFontPixelMetric(FxFont); 1172 1047 if Result.Baseline <> -1 then Result.Baseline:= round((Result.Baseline-1)/FontAntialiasingLevel); 1173 1048 if Result.CapLine <> -1 then Result.CapLine:= round(Result.CapLine/FontAntialiasingLevel); … … 1354 1229 1355 1230 function TCustomLCLFontRenderer.TextSize(sUTF8: string): TSize; 1356 begin 1231 var oldOrientation: integer; 1232 begin 1233 oldOrientation:= FontOrientation; 1234 FontOrientation:= 0; 1357 1235 UpdateFont; 1358 1236 result := TextSizeNoUpdateFont(sUTF8); 1237 FontOrientation:= oldOrientation; 1359 1238 end; 1360 1239 -
GraphicTest/Packages/bgrabitmap/bgratextfx.pas
r472 r494 26 26 27 27 uses 28 Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText, BGRAVectorize; 28 Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText, 29 BGRACustomTextFX, BGRAVectorize; 29 30 30 31 type … … 80 81 { TBGRATextEffect } 81 82 82 TBGRATextEffect = class 83 private 84 FShadowQuality: TRadialBlurType; 85 function GetBounds: TRect; 86 function GetMaskHeight: integer; 87 class function GetOutlineWidth: integer; static; 88 function GetShadowBounds(ARadius: integer): TRect; 89 function GetMaskWidth: integer; 90 function GetTextHeight: integer; 91 function GetTextWidth: integer; 92 procedure SetShadowQuality(AValue: TRadialBlurType); 83 TBGRATextEffect = class(TBGRACustomTextEffect) 93 84 protected 94 FTextMask: TBGRACustomBitmap;95 FShadowRadius: integer;96 FOutlineMask, FShadowMask, FShadingMask : TBGRACustomBitmap;97 FShadingAltitude: integer;98 FShadingRounded: boolean;99 FTextSize: TSize;100 FOffset: TPoint;101 function DrawMaskMulticolored(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; const AColors: array of TBGRAPixel): TRect;102 function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; AColor: TBGRAPixel): TRect;103 function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; ATexture: IBGRAScanner): TRect;104 function InternalDrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;105 85 procedure InitImproveReadability(AText: string; Font: TFont; SubOffsetX,SubOffsetY: single); 106 86 procedure Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer); … … 114 94 constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean); 115 95 constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single); 116 constructor Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,AHeight: integer; AOffset: TPoint);117 procedure ApplySphere;118 procedure ApplyVerticalCylinder;119 procedure ApplyHorizontalCylinder;120 function Draw(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;121 function Draw(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;122 function Draw(ADest: TBGRACustomBitmap; X, Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;123 function Draw(ADest: TBGRACustomBitmap; X, Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;124 125 function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ARounded: Boolean = true): TRect;126 function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; ARounded: Boolean = true): TRect;127 function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; AAlign: TAlignment; ARounded: Boolean = true): TRect;128 function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; AAlign: TAlignment; ARounded: Boolean = true): TRect;129 130 function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel): TRect;131 function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;132 function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;133 function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;134 function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;135 function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;136 function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel): TRect;137 function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;138 destructor Destroy; override;139 property TextMask: TBGRACustomBitmap read FTextMask;140 property TextMaskOffset: TPoint read FOffset;141 property Width: integer read GetTextWidth; deprecated;142 property Height: integer read GetTextHeight; deprecated;143 property MaskWidth: integer read GetMaskWidth;144 property MaskHeight: integer read GetMaskHeight;145 property TextSize: TSize read FTextSize;146 property TextWidth: integer read GetTextWidth;147 property TextHeight: integer read GetTextHeight;148 property Bounds: TRect read GetBounds;149 property ShadowBounds[ARadius: integer]: TRect read GetShadowBounds;150 property ShadowQuality: TRadialBlurType read FShadowQuality write SetShadowQuality;151 class property OutlineWidth: integer read GetOutlineWidth;152 96 end; 153 97 … … 160 104 161 105 uses BGRAGradientScanner, GraphType, Math, BGRAGrayscaleMask; 162 163 const DefaultOutlineWidth = 3;164 106 165 107 procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode); … … 190 132 FxFont.Assign(AFont); 191 133 FxFont.Height := fxFont.Height*FontAntialiasingLevel; 192 metric := Get FontPixelMetric(FxFont);134 metric := GetLCLFontPixelMetric(FxFont); 193 135 if not metric.Defined or (metric.Lineheight < 8*FontAntialiasingLevel) or (metric.Lineheight >= 24*FontAntialiasingLevel) then 194 136 begin … … 579 521 { TBGRATextEffect } 580 522 581 function TBGRATextEffect.GetBounds: TRect;582 begin583 if TextMask = nil then584 result := EmptyRect else585 with TextMaskOffset do586 result := rect(X,Y,X+TextMask.Width,Y+TextMask.Height);587 end;588 589 function TBGRATextEffect.GetMaskHeight: integer;590 begin591 if FTextMask = nil then592 result := 0593 else594 result := FTextMask.Height;595 end;596 597 class function TBGRATextEffect.GetOutlineWidth: integer; static;598 begin599 result := DefaultOutlineWidth;600 end;601 602 function TBGRATextEffect.GetShadowBounds(ARadius: integer): TRect;603 begin604 result := Bounds;605 if (ARadius > 0) and not IsRectEmpty(result) then606 begin607 result.left -= ARadius;608 result.top -= ARadius;609 result.right += ARadius;610 result.bottom += ARadius;611 end;612 end;613 614 function TBGRATextEffect.GetMaskWidth: integer;615 begin616 if FTextMask = nil then617 result := 0618 else619 result := FTextMask.Width;620 end;621 622 function TBGRATextEffect.GetTextHeight: integer;623 begin624 result := FTextSize.cy;625 end;626 627 function TBGRATextEffect.GetTextWidth: integer;628 begin629 result := FTextSize.cx;630 end;631 632 procedure TBGRATextEffect.SetShadowQuality(AValue: TRadialBlurType);633 begin634 if FShadowQuality=AValue then Exit;635 FShadowQuality:=AValue;636 FreeAndNil(FShadowMask);637 end;638 639 function TBGRATextEffect.DrawMaskMulticolored(ADest: TBGRACustomBitmap;640 AMask: TBGRACustomBitmap; X, Y: Integer; const AColors: array of TBGRAPixel641 ): TRect;642 var643 scan: TBGRASolidColorMaskScanner;644 xb,yb,startX,numColor: integer;645 p0,p: PBGRAPixel;646 emptyCol, nextCol: boolean;647 begin648 if (AMask = nil) or (length(AColors)=0) then649 begin650 result := EmptyRect;651 exit;652 end;653 if (length(AColors)=0) then654 begin655 result := DrawMask(ADest,AMask,X,Y,AColors[0]);656 exit;657 end;658 scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColors[0]);659 numColor := 0;660 startX := -1;661 p0 := AMask.data;662 for xb := 0 to AMask.Width-1 do663 begin664 p := p0;665 666 if startX=-1 then667 begin668 emptyCol := true;669 for yb := AMask.Height-1 downto 0 do670 begin671 if (p^<>BGRABlack) then672 begin673 emptyCol := false;674 break;675 end;676 inc(p, AMask.Width);677 end;678 679 if not emptyCol then680 begin681 if startX=-1 then682 startX := xb;683 end else684 begin685 if startX<>-1 then686 begin687 ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);688 inc(numColor);689 if numColor = length(AColors) then690 numColor := 0;691 scan.Color := AColors[numColor];692 startX := -1;693 end;694 end;695 696 end else697 begin698 emptyCol := true;699 nextCol := true;700 for yb := AMask.Height-1 downto 0 do701 begin702 if (p^<>BGRABlack) then703 begin704 emptyCol := false;705 if ((p-1)^<>BGRABlack) then706 begin707 nextCol := false;708 break;709 end;710 end;711 inc(p, AMask.Width);712 end;713 if nextCol or emptyCol then714 begin715 ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);716 inc(numColor);717 if numColor = length(AColors) then718 numColor := 0;719 scan.Color := AColors[numColor];720 if emptyCol then startX := -1721 else startX := xb;722 end;723 end;724 725 inc(p0);726 end;727 if startX<>-1 then728 ADest.FillRect(X+startX,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);729 scan.Free;730 result := rect(X,Y,X+AMask.Width,Y+AMask.Height);731 end;732 733 function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;734 AMask: TBGRACustomBitmap; X, Y: Integer; AColor: TBGRAPixel): TRect;735 var736 scan: TBGRACustomScanner;737 begin738 if AMask = nil then739 begin740 result := EmptyRect;741 exit;742 end;743 scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColor);744 ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);745 scan.Free;746 result := rect(X,Y,X+AMask.Width,Y+AMask.Height);747 end;748 749 function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;750 AMask: TBGRACustomBitmap; X, Y: Integer; ATexture: IBGRAScanner): TRect;751 var752 scan: TBGRACustomScanner;753 begin754 if AMask = nil then755 begin756 result := EmptyRect;757 exit;758 end;759 scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),ATexture);760 ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);761 scan.Free;762 result := rect(X,Y,X+AMask.Width,Y+AMask.Height);763 end;764 765 function TBGRATextEffect.InternalDrawShaded(ADest: TBGRACustomBitmap; X,766 Y: integer; Shader: TCustomPhongShading; Altitude: integer;767 AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;768 var769 WithMargin,Map: TBGRACustomBitmap;770 p: PBGRAPixel;771 n,maxv: integer;772 v,blurRadius: single;773 iBlurRadius: integer;774 begin775 if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then776 begin777 result := EmptyRect;778 exit;779 end;780 781 if (FShadingMask <> nil) and ((FShadingAltitude <> Altitude) or (FShadingRounded <> ARounded)) then782 FreeAndNil(FShadingMask);783 784 if FShadingMask = nil then785 begin786 FShadingRounded := ARounded;787 FShadingAltitude := Altitude;788 789 if ARounded then blurRadius := Altitude790 else blurRadius := Altitude*0.5;791 792 iBlurRadius := ceil(blurRadius);793 794 WithMargin := BGRABitmapFactory.Create(FTextMask.Width+iBlurRadius*2, FTextMask.Height+iBlurRadius*2,BGRABlack);795 WithMargin.PutImage(iBlurRadius,iBlurRadius,FTextMask,dmSet);796 if (iBlurRadius <> blurRadius) and (blurRadius < 3) then797 Map := WithMargin.FilterBlurRadial(round(blurRadius*10),rbPrecise)798 else799 Map := WithMargin.FilterBlurRadial(iBlurRadius,rbFast);800 801 p := Map.Data;802 maxv := 0;803 for n := Map.NbPixels-1 downto 0 do804 begin805 if p^.green > maxv then806 maxv := p^.green;807 inc(p);808 end;809 810 if maxv > 0 then811 begin812 p := Map.Data;813 for n := Map.NbPixels-1 downto 0 do814 begin815 v := p^.green/maxv;816 if ARounded then817 begin818 if v <= 0.5 then819 v := v*v*2 else820 v := 1-(1-v)*(1-v)*2;821 end;822 p^ := MapHeightToBGRA( v, p^.alpha);823 inc(p);824 end;825 end;826 827 Map.ApplyMask(WithMargin);828 WithMargin.Free;829 BGRAReplace(Map, Map.GetPart(rect(iBlurRadius,iBlurRadius,Map.Width-iBlurRadius,Map.Height-iBlurRadius)));830 FShadingMask := Map;831 end;832 833 inc(X, FOffset.X);834 Inc(Y, FOffset.Y);835 if ATexture <> nil then836 Shader.DrawScan(ADest,FShadingMask,Altitude,X,Y, ATexture)837 else838 Shader.Draw(ADest,FShadingMask,Altitude,X,Y, AColor);839 result := rect(X,Y, X+FShadingMask.Width,Y+FShadingMask.Height);840 end;841 842 523 procedure TBGRATextEffect.InitImproveReadability(AText: string; Font: TFont; 843 524 SubOffsetX, SubOffsetY: single); … … 859 540 end; 860 541 861 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;862 AColor: TBGRAPixel; AAlign: TAlignment): TRect;863 begin864 Case AAlign of865 taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,AColor);866 taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,AColor);867 else result := Draw(ADest,X,Y,AColor);868 end;869 end;870 871 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;872 ATexture: IBGRAScanner; AAlign: TAlignment): TRect;873 begin874 Case AAlign of875 taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,ATexture);876 taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,ATexture);877 else result := Draw(ADest,X,Y,ATexture);878 end;879 end;880 881 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;882 Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;883 ARounded: Boolean): TRect;884 begin885 result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,AColor,nil,ARounded);886 end;887 888 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;889 Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;890 ARounded: Boolean): TRect;891 begin892 result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,BGRAPixelTransparent,ATexture,ARounded);893 end;894 895 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;896 Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;897 AAlign: TAlignment; ARounded: Boolean): TRect;898 begin899 Case AAlign of900 taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,AColor,ARounded);901 taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,AColor,ARounded);902 taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,AColor,ARounded);903 else904 result := EmptyRect;905 end;906 end;907 908 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;909 Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;910 AAlign: TAlignment; ARounded: Boolean): TRect;911 begin912 Case AAlign of913 taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,ATexture,ARounded);914 taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,ATexture,ARounded);915 taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,ATexture,ARounded);916 else917 result := EmptyRect;918 end;919 end;920 921 542 constructor TBGRATextEffect.Create(AText: string; Font: TFont; 922 543 Antialiasing: boolean; SubOffsetX,SubOffsetY: single); … … 955 576 begin 956 577 InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, SubOffsetX, SubOffsetY); 957 end;958 959 constructor TBGRATextEffect.Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,960 AHeight: integer; AOffset: TPoint);961 begin962 FTextSize := Size(AWidth,AHeight);963 FOffset := AOffset;964 if not AMaskOwner then965 FTextMask := AMask.Duplicate()966 else967 FTextMask := AMask;968 578 end; 969 579 … … 1126 736 end; 1127 737 1128 procedure TBGRATextEffect.ApplySphere;1129 var sphere: TBGRACustomBitmap;1130 begin1131 if FTextMask = nil then exit;1132 FreeAndNil(FOutlineMask);1133 FreeAndNil(FShadowMask);1134 FShadowRadius := 0;1135 sphere := FTextMask.FilterSphere;1136 FTextMask.Fill(BGRABlack);1137 FTextMask.PutImage(0,0,sphere,dmDrawWithTransparency);1138 sphere.Free;1139 end;1140 1141 procedure TBGRATextEffect.ApplyVerticalCylinder;1142 begin1143 if FTextMask = nil then exit;1144 FreeAndNil(FOutlineMask);1145 FreeAndNil(FShadowMask);1146 FShadowRadius := 0;1147 BGRAReplace(FTextMask,FTextMask.FilterCylinder);1148 end;1149 1150 procedure TBGRATextEffect.ApplyHorizontalCylinder;1151 begin1152 if FTextMask = nil then exit;1153 FreeAndNil(FOutlineMask);1154 FreeAndNil(FShadowMask);1155 FShadowRadius := 0;1156 BGRAReplace(FTextMask,FTextMask.RotateCW);1157 BGRAReplace(FTextMask,FTextMask.FilterCylinder);1158 BGRAReplace(FTextMask,FTextMask.RotateCCW);1159 end;1160 1161 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;1162 AColor: TBGRAPixel): TRect;1163 begin1164 result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColor);1165 end;1166 1167 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;1168 ATexture: IBGRAScanner): TRect;1169 begin1170 result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,ATexture);1171 end;1172 1173 function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,1174 Y: integer; const AColors: array of TBGRAPixel): TRect;1175 begin1176 result := DrawMaskMulticolored(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColors);1177 end;1178 1179 function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,1180 Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;1181 begin1182 Case AAlign of1183 taRightJustify: result := DrawMulticolored(ADest,X-TextSize.cx,Y,AColors);1184 taCenter: result := DrawMulticolored(ADest,X-TextSize.cx div 2,Y,AColors);1185 else result := DrawMulticolored(ADest,X,Y,AColors);1186 end;1187 end;1188 1189 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;1190 AColor: TBGRAPixel): TRect;1191 begin1192 if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then1193 begin1194 result := EmptyRect;1195 exit;1196 end;1197 if FOutlineMask = nil then1198 begin1199 FOutlineMask := FTextMask.FilterContour;1200 FOutlineMask.LinearNegative;1201 end;1202 result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,AColor);1203 end;1204 1205 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;1206 ATexture: IBGRAScanner): TRect;1207 begin1208 if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then1209 begin1210 result := EmptyRect;1211 exit;1212 end;1213 if FOutlineMask = nil then1214 begin1215 FOutlineMask := FTextMask.FilterContour;1216 FOutlineMask.LinearNegative;1217 end;1218 result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,ATexture);1219 end;1220 1221 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;1222 AColor: TBGRAPixel; AAlign: TAlignment): TRect;1223 begin1224 Case AAlign of1225 taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,AColor);1226 taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,AColor);1227 else result := DrawOutline(ADest,X,Y,AColor);1228 end;1229 end;1230 1231 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;1232 ATexture: IBGRAScanner; AAlign: TAlignment): TRect;1233 begin1234 Case AAlign of1235 taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,ATexture);1236 taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,ATexture);1237 else result := DrawOutline(ADest,X,Y,ATexture);1238 end;1239 end;1240 1241 function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,1242 Radius: integer; AColor: TBGRAPixel): TRect;1243 begin1244 if (Radius <= 0) or (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then1245 begin1246 result := Draw(ADest,X,Y,AColor);1247 exit;1248 end;1249 if (FShadowRadius <> Radius) or (FShadowMask = nil) then1250 begin1251 FShadowRadius := Radius;1252 FreeAndNil(FShadowMask);1253 FShadowMask := BGRABitmapFactory.Create(FTextMask.Width+Radius*2,FTextMask.Height+Radius*2,BGRABlack);1254 FShadowMask.PutImage(Radius,Radius,FTextMask,dmSet);1255 BGRAReplace(FShadowMask, FShadowMask.FilterBlurRadial(Radius,ShadowQuality));1256 end;1257 Inc(X,FOffset.X-Radius);1258 Inc(Y,FOffset.Y-Radius);1259 DrawMask(ADest,FShadowMask,X,Y,AColor);1260 result := rect(X,Y,X+FShadowMask.Width,Y+FShadowMask.Height);1261 end;1262 1263 function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,1264 Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;1265 begin1266 Case AAlign of1267 taRightJustify: result := DrawShadow(ADest,X-TextSize.cx,Y,Radius,AColor);1268 taCenter: result := DrawShadow(ADest,X-TextSize.cx div 2,Y,Radius,AColor);1269 else result := DrawShadow(ADest,X,Y,Radius,AColor);1270 end;1271 end;1272 1273 destructor TBGRATextEffect.Destroy;1274 begin1275 FShadowMask.free;1276 textMask.Free;1277 FOutlineMask.Free;1278 FShadingMask.Free;1279 inherited Destroy;1280 end;1281 1282 738 initialization 1283 739 -
GraphicTest/Packages/bgrabitmap/bgrathumbnail.pas
r472 r494 2 2 3 3 {$mode objfpc}{$H+} 4 {$i bgrabitmap.inc} 4 5 5 6 interface … … 15 16 function GetOpenRasterThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 16 17 function GetLazPaintThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 18 function GetPhoxoThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 17 19 function GetJpegThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 18 20 function GetPsdThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; … … 20 22 function GetPaintDotNetThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 21 23 function GetBmpThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 24 {$IFDEF BGRABITMAP_USE_LCL} 22 25 function GetIcoThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; 26 {$ENDIF} 23 27 24 28 function GetPcxThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap; … … 34 38 implementation 35 39 36 uses Types, GraphType, Graphics, base64, lazutf8classes, LCLProc,37 DOM, XMLRead, FPReadJPEG, BGRAReadPng, BGRAReadGif, BGRAReadBMP,40 uses Types, base64, BGRAUTF8, {$IFDEF BGRABITMAP_USE_LCL}Graphics, GraphType,{$ENDIF} 41 DOM, XMLRead, BGRAReadJPEG, BGRAReadPng, BGRAReadGif, BGRAReadBMP, 38 42 BGRAReadPSD, BGRAReadIco, UnzipperExt, BGRAReadLzp; 39 43 … … 101 105 ifGif: result := GetGifThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 102 106 ifBmp: result := GetBmpThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 107 {$IFDEF BGRABITMAP_USE_LCL} 103 108 ifIco: result := GetIcoThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 109 {$ENDIF} 104 110 ifPcx: result := GetPcxThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 105 111 ifPaintDotNet: result := GetPaintDotNetThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 106 112 ifLazPaint: result := GetLazPaintThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 107 113 ifOpenRaster: result := GetOpenRasterThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 114 ifPhoxo: result := GetPhoxoThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 108 115 ifPsd: result := GetPsdThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); 109 116 ifTarga: result := GetTargaThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest); … … 175 182 end; 176 183 184 function GetPhoxoThumbnail(AStream: TStream; AWidth, AHeight: integer; 185 ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap; 186 var 187 reader: TFPCustomImageReader; 188 begin 189 if DefaultBGRAImageReader[ifPhoxo] = nil then 190 result := nil 191 else 192 begin 193 reader := CreateBGRAImageReader(ifPhoxo); 194 result := GetStreamThumbnail(AStream, reader, AWidth,AHeight,ABackColor,ACheckers,ADest); 195 reader.Free; 196 end; 197 end; 198 177 199 function GetJpegThumbnail(AStream: TStream; AWidth, AHeight: integer 178 200 ; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap; 179 201 var 180 jpeg: T FPReaderJPEG;181 begin 182 jpeg := T FPReaderJPEG.Create;202 jpeg: TBGRAReaderJpeg; 203 begin 204 jpeg := TBGRAReaderJpeg.Create; 183 205 jpeg.Performance := jpBestSpeed; 184 206 jpeg.MinWidth := AWidth; … … 328 350 end; 329 351 352 {$IFDEF BGRABITMAP_USE_LCL} 330 353 function GetIcoThumbnail(AStream: TStream; AWidth, AHeight: integer; 331 354 ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap; … … 372 395 ico.Free; 373 396 end; 374 375 397 {$ENDIF} 376 398 377 399 function GetPcxThumbnail(AStream: TStream; AWidth, AHeight: integer; -
GraphicTest/Packages/bgrabitmap/bgratransform.pas
r472 r494 12 12 type 13 13 { Contains an affine matrix, i.e. a matrix to transform linearly and translate TPointF coordinates } 14 TAffineMatrix = array[1..2,1..3] of single;14 TAffineMatrix = BGRABitmapTypes.TAffineMatrix; 15 15 16 16 { TAffineBox } … … 49 49 procedure SetMatrix(AMatrix: TAffineMatrix); 50 50 function InternalScanCurrentPixel: TBGRAPixel; virtual; 51 function GetViewMatrix: TAffineMatrix; 52 procedure SetViewMatrix(AValue: TAffineMatrix); 51 53 public 52 54 GlobalOpacity: Byte; … … 66 68 function ScanAt(X, Y: Single): TBGRAPixel; override; 67 69 property Matrix: TAffineMatrix read FMatrix write SetMatrix; 70 property ViewMatrix: TAffineMatrix read GetViewMatrix write SetViewMatrix; 68 71 end; 69 72 … … 81 84 FBuffer: PBGRAPixel; 82 85 FBufferSize: Int32or64; 83 procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear); 86 FIncludeEdges: boolean; 87 procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); 84 88 public 85 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear );86 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear );89 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); 90 constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); 87 91 destructor Destroy; override; 88 92 function InternalScanCurrentPixel: TBGRAPixel; override; … … 90 94 function IsScanPutPixelsDefined: boolean; override; 91 95 procedure Fit(Origin, HAxis, VAxis: TPointF); override; 96 end; 97 98 { TBGRAQuadLinearScanner } 99 100 TBGRAQuadLinearScanner = class(TBGRACustomScanner) 101 private 102 FPoints,FVectors: array[0..3] of TPointF; 103 FInvLengths,FDets: array[0..3] of single; 104 FCoeffs: array[0..3] of TPointF; 105 aa,bb0,cc0,inv2aa: double; 106 FSource: IBGRAScanner; 107 FSourceMatrix: TAffineMatrix; 108 FUVVector: TPointF; 109 110 ScanParaBB, ScanParaCC, ScanParaBBInv: double; 111 112 ScanVertV0,ScanVertVStep0,ScanVertDenom0,ScanVertDenomStep0: double; 113 114 FShowC1, FShowC2: boolean; 115 FScanFunc: TScanNextPixelFunction; 116 FCurXF,FCurYF: single; 117 FBuffer: PBGRAPixel; 118 FBufferSize: Int32or64; 119 FTextureInterpolation: Boolean; 120 function GetCulling: TFaceCulling; 121 function ScanGeneral: TBGRAPixel; 122 procedure PrepareScanVert0; 123 function ScanVert0: TBGRAPixel; 124 procedure PrepareScanPara; 125 function ScanPara: TBGRAPixel; 126 function GetTexColorAt(u,v: Single; detNeg: boolean): TBGRAPixel; inline; 127 procedure ScanMoveToF(X,Y: single); inline; 128 procedure SetCulling(AValue: TFaceCulling); 129 procedure Init(ASource: IBGRAScanner; const APoints: array of TPointF; 130 ATextureInterpolation: boolean); 131 public 132 function ScanAt(X, Y: Single): TBGRAPixel; override; 133 procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override; 134 function IsScanPutPixelsDefined: boolean; override; 135 procedure ScanMoveTo(X, Y: Integer); override; 136 function ScanNextPixel: TBGRAPixel; override; 137 constructor Create(ASource: IBGRAScanner; 138 ASourceMatrix: TAffineMatrix; const APoints: array of TPointF; 139 ATextureInterpolation: boolean = true); 140 constructor Create(ASource: IBGRAScanner; 141 const ATexCoords: array of TPointF; const APoints: array of TPointF; 142 ATextureInterpolation: boolean = true); 143 destructor Destroy; override; 144 property Culling: TFaceCulling read GetCulling write SetCulling; 92 145 end; 93 146 … … 142 195 //matrix multiplication 143 196 operator *(M,N: TAffineMatrix): TAffineMatrix; 197 operator =(M,N: TAffineMatrix): boolean; 144 198 145 199 //matrix multiplication by a vector (apply transformation to that vector) 146 200 operator *(M: TAffineMatrix; V: TPointF): TPointF; 201 operator *(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF; 147 202 148 203 //check if matrix is inversible … … 166 221 //define a scaling matrix 167 222 function AffineMatrixScale(sx,sy: single): TAffineMatrix; 223 224 function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix; 225 function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix; 226 function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix; 227 function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix; 168 228 169 229 //define a linear matrix … … 282 342 end; 283 343 344 { TBGRASphereDeformationScanner } 345 346 TBGRASphereDeformationScanner = Class(TBGRACustomScanner) 347 protected 348 FScanner: IBGRAScanner; 349 FScanAtFunc: TScanAtFunction; 350 FCenter: TPointF; 351 FRadiusX, FRadiusY: Single; 352 public 353 constructor Create(AScanner: IBGRAScanner; ACenter: TPointF; ARadiusX,ARadiusY: single); 354 function ScanAt(X, Y: Single): TBGRAPixel; override; 355 property RadiusX: Single read FRadiusX; 356 property RadiusY: Single read FRadiusY; 357 end; 358 359 { TBGRAVerticalCylinderDeformationScanner } 360 361 TBGRAVerticalCylinderDeformationScanner = Class(TBGRACustomScanner) 362 protected 363 FScanner: IBGRAScanner; 364 FScanAtFunc: TScanAtFunction; 365 FCenterX: single; 366 FRadiusX: Single; 367 public 368 constructor Create(AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single); 369 function ScanAt(X, Y: Single): TBGRAPixel; override; 370 property RadiusX: Single read FRadiusX; 371 end; 372 373 284 374 implementation 285 375 286 uses BGRABlend, GraphType;376 uses BGRABlend, Math; 287 377 288 378 function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix; … … 307 397 end; 308 398 399 operator=(M, N: TAffineMatrix): boolean; 400 begin 401 result := CompareMem(@M,@N,SizeOf(TAffineMatrix)); 402 end; 403 309 404 operator*(M: TAffineMatrix; V: TPointF): TPointF; 310 405 begin 311 result.X := V.X*M[1,1]+V.Y*M[1,2]+M[1,3]; 312 result.Y := V.X*M[2,1]+V.Y*M[2,2]+M[2,3]; 406 if isEmptyPointF(V) then 407 result := EmptyPointF 408 else 409 begin 410 result.X := V.X*M[1,1]+V.Y*M[1,2]+M[1,3]; 411 result.Y := V.X*M[2,1]+V.Y*M[2,2]+M[2,3]; 412 end; 413 end; 414 415 operator*(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF; 416 var 417 i: NativeInt; 418 ofs: TPointF; 419 begin 420 setlength(result, length(A)); 421 if IsAffineMatrixTranslation(M) then 422 begin 423 ofs := PointF(M[1,3],M[2,3]); 424 for i := 0 to high(A) do 425 result[i] := A[i]+ofs; 426 end else 427 for i := 0 to high(A) do 428 result[i] := M*A[i]; 313 429 end; 314 430 … … 320 436 function IsAffineMatrixTranslation(M: TAffineMatrix): boolean; 321 437 begin 322 result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 1) and (m[2,2]=0);438 result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 0) and (m[2,2]=1); 323 439 end; 324 440 … … 355 471 function AffineMatrixScale(sx, sy: single): TAffineMatrix; 356 472 begin 357 result := AffineMatrix(sx, 0, 0, 358 0, sy, 0); 473 result := AffineMatrix(sx, 0, 0, 474 0, sy, 0); 475 end; 476 477 function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix; 478 begin 479 result := AffineMatrix(1,tan(AngleCW*Pi/180),0, 480 0, 1, 0); 481 end; 482 483 function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix; 484 begin 485 result := AffineMatrix(1, 0, 0, 486 tan(AngleCW*Pi/180), 1, 0) 487 end; 488 489 function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix; 490 begin 491 492 result := AffineMatrix(1,tan(-AngleCCW),0, 493 0, 1, 0); 494 end; 495 496 function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix; 497 begin 498 result := AffineMatrix(1, 0, 0, 499 tan(-angleCCW), 1, 0) 359 500 end; 360 501 … … 386 527 begin 387 528 result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0; 529 end; 530 531 { TBGRAVerticalCylinderDeformationScanner } 532 533 constructor TBGRAVerticalCylinderDeformationScanner.Create( 534 AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single); 535 begin 536 FScanner := AScanner; 537 FScanAtFunc := @FScanner.ScanAt; 538 FCenterX := ACenterX; 539 FRadiusX := ARadiusX; 540 end; 541 542 function TBGRAVerticalCylinderDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel; 543 var 544 xn,len,fact: Single; 545 begin 546 xn := (x - FCenterX) / FRadiusX; 547 len := abs(xn); 548 if (len <= 1) then 549 begin 550 if (len > 0) then 551 begin 552 fact := 1 / len * arcsin(len) / (Pi / 2); 553 xn *= fact; 554 end; 555 result := FScanAtFunc(xn * FRadiusX + FCenterX, y); 556 end 557 else 558 result := BGRAPixelTransparent; 559 end; 560 561 { TBGRASphereDeformationScanner } 562 563 constructor TBGRASphereDeformationScanner.Create(AScanner: IBGRAScanner; 564 ACenter: TPointF; ARadiusX, ARadiusY: single); 565 begin 566 FScanner := AScanner; 567 FScanAtFunc := @FScanner.ScanAt; 568 FCenter := ACenter; 569 FRadiusX := ARadiusX; 570 FRadiusY := ARadiusY; 571 end; 572 573 function TBGRASphereDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel; 574 var 575 xn, yn, len,fact: Single; 576 begin 577 xn := (x - FCenter.X) / FRadiusX; 578 yn := (y - FCenter.Y) / FRadiusY; 579 len := sqrt(sqr(xn) + sqr(yn)); 580 if (len <= 1) then 581 begin 582 if (len > 0) then 583 begin 584 fact := 1 / len * arcsin(len) / (Pi / 2); 585 xn *= fact; 586 yn *= fact; 587 end; 588 result := FScanAtFunc(xn * FRadiusX + FCenter.X, yn * FRadiusY + FCenter.Y); 589 end 590 else 591 result := BGRAPixelTransparent; 388 592 end; 389 593 … … 466 670 function TBGRAScannerOffset.ScanAt(X, Y: Single): TBGRAPixel; 467 671 begin 468 Result:=FSource.ScanAt(X ,Y);672 Result:=FSource.ScanAt(X - FOffset.X, Y - FOffset.Y); 469 673 end; 470 674 … … 620 824 end; 621 825 826 function TBGRAAffineScannerTransform.GetViewMatrix: TAffineMatrix; 827 begin 828 if FEmptyMatrix then 829 result := AffineMatrixIdentity 830 else 831 result := AffineMatrixInverse(FMatrix); 832 end; 833 834 procedure TBGRAAffineScannerTransform.SetViewMatrix(AValue: TAffineMatrix); 835 begin 836 Matrix := AValue; 837 Invert; 838 end; 839 622 840 procedure TBGRAAffineScannerTransform.SetMatrix(AMatrix: TAffineMatrix); 623 841 begin … … 711 929 end; 712 930 931 { TBGRAQuadLinearScanner } 932 933 function TBGRAQuadLinearScanner.GetTexColorAt(u, v: Single; detNeg: boolean 934 ): TBGRAPixel; 935 begin 936 if detNeg then 937 begin 938 if not FShowC2 then 939 begin 940 result := BGRAPixelTransparent; 941 exit; 942 end; 943 end else 944 if not FShowC1 then 945 begin 946 result := BGRAPixelTransparent; 947 exit; 948 end; 949 with (FSourceMatrix * PointF(u,v) + FUVVector*(u*v)) do 950 if FTextureInterpolation then 951 result := FSource.ScanAt(x,y) 952 else 953 result := FSource.ScanAtInteger(System.round(x),System.round(y)); 954 end; 955 956 procedure TBGRAQuadLinearScanner.ScanMoveToF(X, Y: single); 957 begin 958 FCurXF := X; 959 FCurYF := Y; 960 if (FVectors[0].x = 0) and (FVectors[2].x = 0) then 961 begin 962 PrepareScanVert0; 963 FScanFunc := @ScanVert0; 964 end else 965 if aa = 0 then 966 begin 967 PrepareScanPara; 968 FScanFunc := @ScanPara 969 end 970 else 971 FScanFunc := @ScanGeneral; 972 end; 973 974 procedure TBGRAQuadLinearScanner.SetCulling(AValue: TFaceCulling); 975 begin 976 FShowC1 := AValue in [fcKeepCW,fcNone]; 977 FShowC2 := AValue in [fcKeepCCW,fcNone]; 978 end; 979 980 procedure TBGRAQuadLinearScanner.Init(ASource: IBGRAScanner; 981 const APoints: array of TPointF; ATextureInterpolation: boolean); 982 var 983 i: NativeInt; 984 v: TPointF; 985 len: single; 986 begin 987 if length(APoints)<>4 then 988 raise exception.Create('Expecting 4 points'); 989 FTextureInterpolation:= ATextureInterpolation; 990 FSource := ASource; 991 FSourceMatrix := AffineMatrixIdentity; 992 FUVVector := PointF(0,0); 993 for i := 0 to 3 do 994 begin 995 FPoints[i] := APoints[i]; 996 v := APoints[(i+1) mod 4] - APoints[i]; 997 len := sqrt(v*v); 998 if len > 0 then FInvLengths[i] := 1/len 999 else FInvLengths[i] := 0; 1000 FVectors[i] := v*FInvLengths[i]; 1001 end; 1002 1003 FCoeffs[0] := FPoints[0]; 1004 FCoeffs[1] := FPoints[1]-FPoints[0]; 1005 FCoeffs[2] := FPoints[3]-FPoints[0]; 1006 FCoeffs[3] := FPoints[0]+FPoints[2]-FPoints[1]-FPoints[3]; 1007 1008 aa := VectDet(FCoeffs[3],FCoeffs[2]); 1009 bb0 := VectDet(FCoeffs[3],FCoeffs[0]) + VectDet(FCoeffs[1],FCoeffs[2]); 1010 cc0 := VectDet(FCoeffs[1],FCoeffs[0]); 1011 for i := 0 to 3 do 1012 FDets[i] := VectDet(FVectors[i],FVectors[(i+1) mod 4]); 1013 if aa <> 0 then inv2aa := 1/(2*aa) else inv2aa := 1; 1014 1015 FShowC1 := true; 1016 FShowC2 := true; 1017 1018 FBuffer := nil; 1019 FBufferSize := 0; 1020 1021 ScanMoveToF(0,0); 1022 end; 1023 1024 function TBGRAQuadLinearScanner.ScanAt(X, Y: Single): TBGRAPixel; 1025 begin 1026 ScanMoveToF(X,Y); 1027 Result:= FScanFunc(); 1028 end; 1029 1030 procedure TBGRAQuadLinearScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer; 1031 mode: TDrawMode); 1032 var 1033 n: NativeInt; 1034 p: PBGRAPixel; 1035 begin 1036 if mode = dmSet then 1037 p := pdest 1038 else 1039 begin 1040 if count > FBufferSize then 1041 begin 1042 FBufferSize := count; 1043 ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel)); 1044 end; 1045 p := FBuffer; 1046 end; 1047 for n := count-1 downto 0 do 1048 begin 1049 p^ := FScanFunc(); 1050 inc(p); 1051 end; 1052 if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255); 1053 end; 1054 1055 function TBGRAQuadLinearScanner.IsScanPutPixelsDefined: boolean; 1056 begin 1057 result := true; 1058 end; 1059 1060 procedure TBGRAQuadLinearScanner.ScanMoveTo(X, Y: Integer); 1061 begin 1062 ScanMoveToF(X,Y); 1063 end; 1064 1065 function TBGRAQuadLinearScanner.ScanNextPixel: TBGRAPixel; 1066 begin 1067 Result:= FScanFunc(); 1068 end; 1069 1070 function TBGRAQuadLinearScanner.ScanGeneral: TBGRAPixel; 1071 var u1,u2,v1,v2,x,y: double; 1072 bb,cc,det,delta,denom: double; 1073 1074 procedure ReturnC1C2; inline; 1075 var c1,c2: TBGRAPixel; 1076 begin 1077 with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do 1078 if FTextureInterpolation then 1079 c1 := FSource.ScanAt(x,y) 1080 else 1081 c1 := FSource.ScanAtInteger(System.round(x),System.round(y)); 1082 with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do 1083 if FTextureInterpolation then 1084 c2 := FSource.ScanAt(x,y) 1085 else 1086 c2 := FSource.ScanAtInteger(System.round(x),System.round(y)); 1087 result := MergeBGRA(c1,c2); 1088 end; 1089 1090 begin 1091 x := FCurXF; 1092 y := FCurYF; 1093 FCurXF += 1; 1094 if (Y = FPoints[0].y) and (FVectors[0].y = 0) then 1095 begin 1096 if FVectors[0].x = 0 then 1097 begin 1098 result := BGRAPixelTransparent; 1099 exit; 1100 end; 1101 u1 := (X - FPoints[0].x)/(FPoints[1].x-FPoints[0].x); 1102 if (u1 >= 0) and (u1 <= 1) then 1103 begin 1104 result := GetTexColorAt(u1,0,FDets[0]<0); 1105 exit; 1106 end; 1107 end; 1108 if (X = FPoints[1].x) and (FVectors[1].x = 0) then 1109 begin 1110 if FVectors[1].y = 0 then 1111 begin 1112 result := BGRAPixelTransparent; 1113 exit; 1114 end; 1115 v1 := (Y - FPoints[1].y)/(FPoints[2].y-FPoints[1].y); 1116 if (v1 >= 0) and (v1 <= 1) then 1117 begin 1118 result := GetTexColorAt(0,v1,FDets[1]<0); 1119 exit; 1120 end; 1121 end; 1122 if (Y = FPoints[2].y) and (FVectors[2].y = 0) then 1123 begin 1124 if FVectors[2].x = 0 then 1125 begin 1126 result := BGRAPixelTransparent; 1127 exit; 1128 end; 1129 u1 := (X - FPoints[3].x)/(FPoints[2].x-FPoints[3].x); 1130 if (u1 >= 0) and (u1 <= 1) then 1131 begin 1132 result := GetTexColorAt(u1,1,FDets[2]<0); 1133 exit; 1134 end; 1135 end; 1136 if (X = FPoints[3].x) and (FVectors[3].x = 0) then 1137 begin 1138 if FVectors[3].y = 0 then 1139 begin 1140 result := BGRAPixelTransparent; 1141 exit; 1142 end; 1143 v1 := (Y - FPoints[0].y)/(FPoints[3].y-FPoints[0].y); 1144 if (v1 >= 0) and (v1 <= 1) then 1145 begin 1146 result := GetTexColorAt(0,v1,FDets[3]<0); 1147 exit; 1148 end; 1149 end; 1150 1151 bb := bb0 + x*FCoeffs[3].y - y*FCoeffs[3].x; 1152 cc := cc0 + x*FCoeffs[1].y - y*FCoeffs[1].x; 1153 if cc = 0 then 1154 begin 1155 v1 := -bb*2*inv2aa; 1156 denom := FCoeffs[1].x+FCoeffs[3].x*v1; 1157 if denom = 0 then 1158 begin 1159 result := BGRAPixelTransparent; 1160 exit; 1161 end 1162 else 1163 u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom; 1164 1165 if (u1>=0) and (u1<=1) and (v1 >= 0) and (v1 <= 1) then 1166 result := GetTexColorAt(u1,v1,bb<0) 1167 else 1168 result := BGRAPixelTransparent; 1169 end else 1170 begin 1171 delta := bb*bb - 4*aa*cc; 1172 1173 if delta < 0 then 1174 begin 1175 result := BGRAPixelTransparent; 1176 exit; 1177 end; 1178 det := sqrt(delta); 1179 v1 := (-bb+det)*inv2aa; 1180 if v1 = 0 then 1181 u1 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0]) 1182 else if v1 = 1 then 1183 u1 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2]) 1184 else 1185 begin 1186 denom := FCoeffs[1].x+FCoeffs[3].x*v1; 1187 if abs(denom)<1e-6 then 1188 begin 1189 u1 := (bb+det)*inv2aa; 1190 denom := FCoeffs[1].y+FCoeffs[3].y*u1; 1191 if denom = 0 then 1192 begin 1193 result := BGRAPixelTransparent; 1194 exit; 1195 end 1196 else v1 := (y-FCoeffs[0].y-FCoeffs[2].y*u1)/denom; 1197 end 1198 else u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom; 1199 end; 1200 1201 v2 := (-bb-det)*inv2aa; 1202 if v2 = 0 then 1203 u2 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0]) 1204 else if v2 = 1 then 1205 u2 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2]) 1206 else 1207 begin 1208 denom := FCoeffs[1].x+FCoeffs[3].x*v2; 1209 if abs(denom)<1e-6 then 1210 begin 1211 u2 := (bb-det)*inv2aa; 1212 denom := FCoeffs[1].y+FCoeffs[3].y*u2; 1213 if denom = 0 then 1214 begin 1215 result := BGRAPixelTransparent; 1216 exit; 1217 end 1218 else v2 := (y-FCoeffs[0].y-FCoeffs[2].y*u2)/denom; 1219 end 1220 else u2 := (x-FCoeffs[0].x-FCoeffs[2].x*v2)/denom; 1221 end; 1222 1223 if (u1 >= 0) and (u1 <= 1) and (v1 >= 0) and (v1 <= 1) and FShowC1 then 1224 begin 1225 if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then 1226 ReturnC1C2 1227 else 1228 with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do 1229 if FTextureInterpolation then 1230 result := FSource.ScanAt(x,y) 1231 else 1232 result := FSource.ScanAtInteger(System.round(x),System.round(y)); 1233 end 1234 else 1235 if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then 1236 begin 1237 with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do 1238 if FTextureInterpolation then 1239 result := FSource.ScanAt(x,y) 1240 else 1241 result := FSource.ScanAtInteger(System.round(x),System.round(y)); 1242 end 1243 else 1244 result := BGRAPixelTransparent; 1245 end; 1246 end; 1247 1248 function TBGRAQuadLinearScanner.GetCulling: TFaceCulling; 1249 begin 1250 if FShowC1 and FShowC2 then 1251 result := fcNone 1252 else if FShowC1 then 1253 result := fcKeepCW 1254 else 1255 result := fcKeepCCW; 1256 end; 1257 1258 procedure TBGRAQuadLinearScanner.PrepareScanVert0; 1259 begin 1260 if (FVectors[1].x <> 0) then 1261 begin 1262 ScanVertVStep0 := 1/(FPoints[2].x-FPoints[1].x); 1263 ScanVertV0 := (FCurXF-FPoints[1].x)*ScanVertVStep0; 1264 ScanVertDenom0 := (FPoints[1].y-FPoints[0].y)*(1-ScanVertV0) + (FPoints[2].y-FPoints[3].y)*ScanVertV0; 1265 ScanVertDenomStep0 := (FPoints[2].y-FPoints[3].y-FPoints[1].y+FPoints[0].y)*ScanVertVStep0; 1266 end 1267 else 1268 begin 1269 ScanVertV0 := 0; 1270 ScanVertVStep0 := EmptySingle; 1271 end; 1272 end; 1273 1274 function TBGRAQuadLinearScanner.ScanVert0: TBGRAPixel; 1275 var u: single; 1276 begin 1277 FCurXF += 1; 1278 if ScanVertVStep0 = EmptySingle then 1279 begin 1280 result := BGRAPixelTransparent; 1281 exit; 1282 end; 1283 if (ScanVertV0 >= 0) and (ScanVertV0 <= 1) then 1284 begin 1285 if ScanVertDenom0 = 0 then 1286 result := BGRAPixelTransparent 1287 else 1288 begin 1289 u := (FCurYF-(FPoints[0].y*(1-ScanVertV0) + FPoints[3].y*ScanVertV0))/ScanVertDenom0; 1290 if (u >= 0) and (u <= 1) then 1291 result := GetTexColorAt(u,ScanVertV0,FDets[0]<0) 1292 else 1293 result := BGRAPixelTransparent; 1294 end; 1295 end else 1296 result := BGRAPixelTransparent; 1297 1298 ScanVertV0 += ScanVertVStep0; 1299 ScanVertDenom0 += ScanVertDenomStep0; 1300 end; 1301 1302 procedure TBGRAQuadLinearScanner.PrepareScanPara; 1303 begin 1304 ScanParaBB := bb0 + FCurXF*FCoeffs[3].y - FCurYF*FCoeffs[3].x; 1305 ScanParaCC := cc0 + FCurXF*FCoeffs[1].y - FCurYF*FCoeffs[1].x; 1306 if ScanParaBB <> 0 then 1307 ScanParaBBInv := 1/ScanParaBB 1308 else 1309 ScanParaBBInv := 1; 1310 end; 1311 1312 function TBGRAQuadLinearScanner.ScanPara: TBGRAPixel; 1313 var 1314 u,v,denom: Single; 1315 begin 1316 FCurXF += 1; 1317 1318 if ScanParaBB = 0 then 1319 result := BGRAPixelTransparent 1320 else 1321 begin 1322 v := -ScanParaCC*ScanParaBBInv; 1323 denom := FCoeffs[1].x+FCoeffs[3].x*v; 1324 if denom = 0 then 1325 result := BGRAPixelTransparent 1326 else 1327 begin 1328 u := (FCurXF-1-FCoeffs[0].x-FCoeffs[2].x*v)/denom; 1329 1330 if (u>=0) and (u<=1) and (v >= 0) and (v <= 1) then 1331 result := GetTexColorAt(u,v,FDets[0]<0) 1332 else 1333 result := BGRAPixelTransparent; 1334 end; 1335 end; 1336 1337 if FCoeffs[3].y <> 0 then 1338 begin 1339 ScanParaBB += FCoeffs[3].y; 1340 if ScanParaBB <> 0 then 1341 ScanParaBBInv := 1/ScanParaBB 1342 else 1343 ScanParaBBInv := 1; 1344 end; 1345 ScanParaCC += FCoeffs[1].y; 1346 end; 1347 1348 constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner; 1349 ASourceMatrix: TAffineMatrix; const APoints: array of TPointF; 1350 ATextureInterpolation: boolean); 1351 begin 1352 Init(ASource, APoints, ATextureInterpolation); 1353 FSourceMatrix := ASourceMatrix; 1354 end; 1355 1356 constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner; 1357 const ATexCoords: array of TPointF; const APoints: array of TPointF; 1358 ATextureInterpolation: boolean); 1359 begin 1360 Init(ASource, APoints, ATextureInterpolation); 1361 FSourceMatrix := AffineMatrixTranslation(ATexCoords[0].x,ATexCoords[0].y)* 1362 AffineMatrixLinear(ATexCoords[1]-ATexCoords[0],ATexCoords[3]-ATexCoords[0]); 1363 FUVVector := ATexCoords[2] - (ATexCoords[1]+ATexCoords[3]-ATexCoords[0]); 1364 end; 1365 1366 destructor TBGRAQuadLinearScanner.Destroy; 1367 begin 1368 freemem(FBuffer); 1369 inherited Destroy; 1370 end; 1371 713 1372 { TBGRAAffineBitmapTransform } 714 1373 715 1374 procedure TBGRAAffineBitmapTransform.Init(ABitmap: TBGRACustomBitmap; 716 1375 ARepeatImageX: Boolean; ARepeatImageY: Boolean; 717 AResampleFilter: TResampleFilter );1376 AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false); 718 1377 begin 719 1378 if (ABitmap.Width = 0) or (ABitmap.Height = 0) then … … 724 1383 FRepeatImageY := ARepeatImageY; 725 1384 FResampleFilter:= AResampleFilter; 1385 FBuffer := nil; 726 1386 FBufferSize:= 0; 1387 FIncludeEdges := AIncludeEdges; 727 1388 end; 728 1389 729 1390 constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap; 730 ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear );731 begin 732 Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter );1391 ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false); 1392 begin 1393 Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter,AIncludeEdges); 733 1394 end; 734 1395 735 1396 constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap; 736 1397 ARepeatImageX: Boolean; ARepeatImageY: Boolean; 737 AResampleFilter: TResampleFilter );738 begin 739 Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter );1398 AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false); 1399 begin 1400 Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter,AIncludeEdges); 740 1401 end; 741 1402 … … 752 1413 procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel; 753 1414 count: integer; mode: TDrawMode); 1415 const PrecisionShift = {$IFDEF CPU64}24{$ELSE}12{$ENDIF}; 1416 Precision = 1 shl PrecisionShift; 754 1417 var p: PBGRAPixel; 755 1418 n: integer; 756 posX 4096, posY4096: Int32or64;757 deltaX 4096,deltaY4096: Int32or64;758 ix,iy,shrMask,w,h: Int32or64;1419 posXPrecision, posYPrecision: NativeInt; 1420 deltaXPrecision,deltaYPrecision: NativeInt; 1421 ix,iy,shrMask,w,h: NativeInt; 759 1422 py0: PByte; 760 deltaRow: Int32or64;1423 deltaRow: NativeInt; 761 1424 begin 762 1425 w := FBitmap.Width; … … 764 1427 if (w = 0) or (h = 0) then exit; 765 1428 766 posX4096 := round(FCurX*4096); 767 deltaX4096:= round(FMatrix[1,1]*4096); 768 posY4096 := round(FCurY*4096); 769 deltaY4096:= round(FMatrix[2,1]*4096); 1429 if GlobalOpacity = 0 then 1430 begin 1431 if mode = dmSet then 1432 FillDWord(pdest^, count, DWord(BGRAPixelTransparent)); 1433 exit; 1434 end; 1435 1436 posXPrecision := round(FCurX*Precision); 1437 deltaXPrecision:= round(FMatrix[1,1]*Precision); 1438 posYPrecision := round(FCurY*Precision); 1439 deltaYPrecision:= round(FMatrix[2,1]*Precision); 770 1440 shrMask := -1; 771 shrMask := shrMask shr 12;1441 shrMask := shrMask shr PrecisionShift; 772 1442 shrMask := not shrMask; 773 1443 … … 786 1456 if FResampleFilter = rfBox then 787 1457 begin 788 posX 4096 += 2048;789 posY 4096 += 2048;1458 posXPrecision += Precision shr 1; 1459 posYPrecision += Precision shr 1; 790 1460 py0 := PByte(FBitmap.ScanLine[0]); 791 1461 if FBitmap.LineOrder = riloTopToBottom then … … 796 1466 for n := count-1 downto 0 do 797 1467 begin 798 if posX 4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;799 if posY 4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;1468 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift; 1469 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift; 800 1470 if FRepeatImageX then ix := PositiveMod(ix,w); 801 1471 if FRepeatImageY then iy := PositiveMod(iy,h); … … 805 1475 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^; 806 1476 inc(p); 807 posX 4096 += deltaX4096;808 posY 4096 += deltaY4096;1477 posXPrecision += deltaXPrecision; 1478 posYPrecision += deltaYPrecision; 809 1479 end; 810 1480 end else … … 812 1482 for n := count-1 downto 0 do 813 1483 begin 814 if posX 4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;815 if posY 4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;1484 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift; 1485 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift; 816 1486 if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then 817 1487 p^ := BGRAPixelTransparent … … 819 1489 p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^; 820 1490 inc(p); 821 posX 4096 += deltaX4096;822 posY 4096 += deltaY4096;1491 posXPrecision += deltaXPrecision; 1492 posYPrecision += deltaYPrecision; 823 1493 end; 824 1494 end; … … 829 1499 for n := count-1 downto 0 do 830 1500 begin 831 if posX 4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;832 if posY 4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;833 p^ := FBitmap.GetPixelCycle256(ix,iy, (posX 4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);1501 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift; 1502 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift; 1503 p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter); 834 1504 inc(p); 835 posX 4096 += deltaX4096;836 posY 4096 += deltaY4096;1505 posXPrecision += deltaXPrecision; 1506 posYPrecision += deltaYPrecision; 837 1507 end; 838 1508 end else … … 841 1511 for n := count-1 downto 0 do 842 1512 begin 843 if posX 4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;844 if posY 4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;845 p^ := FBitmap.GetPixelCycle256(ix,iy, (posX 4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY);1513 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift; 1514 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift; 1515 p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY); 846 1516 inc(p); 847 posX 4096 += deltaX4096;848 posY 4096 += deltaY4096;1517 posXPrecision += deltaXPrecision; 1518 posYPrecision += deltaYPrecision; 849 1519 end; 850 1520 end else … … 852 1522 for n := count-1 downto 0 do 853 1523 begin 854 if posX 4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;855 if posY 4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;856 p^ := FBitmap.GetPixel256(ix,iy, (posX 4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);1524 if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift; 1525 if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift; 1526 p^ := FBitmap.GetPixel256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter); 857 1527 inc(p); 858 posX 4096 += deltaX4096;859 posY 4096 += deltaY4096;1528 posXPrecision += deltaXPrecision; 1529 posYPrecision += deltaYPrecision; 860 1530 end; 861 1531 end; 862 1532 end; 863 1533 1534 if GlobalOpacity < 255 then 1535 begin 1536 if mode = dmSet then 1537 p := pdest 1538 else 1539 p := FBuffer; 1540 for n := count-1 downto 0 do 1541 begin 1542 p^.alpha := ApplyOpacity(p^.alpha,GlobalOpacity); 1543 if p^.alpha = 0 then p^ := BGRAPixelTransparent; 1544 inc(p); 1545 end; 1546 end; 1547 864 1548 if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255); 865 1549 end; … … 872 1556 procedure TBGRAAffineBitmapTransform.Fit(Origin, HAxis, VAxis: TPointF); 873 1557 begin 874 SetMatrix(AffineMatrix((HAxis.X-Origin.X)/FBitmap.Width, (VAxis.X-Origin.X)/FBitmap.Height, 0, 875 (HAxis.Y-Origin.Y)/FBitmap.Width, (VAxis.Y-Origin.Y)/FBitmap.Height, 0)); 1558 if (FBitmap.Width = 0) or (FBitmap.Height = 0) then exit; 1559 Matrix := AffineMatrix(HAxis.X-Origin.X, VAxis.X-Origin.X, Origin.X, 1560 HAxis.Y-Origin.Y, VAxis.Y-Origin.Y, Origin.Y); 876 1561 Invert; 877 Translate(Origin.X,Origin.Y); 1562 if FIncludeEdges then 1563 begin 1564 Matrix := AffineMatrixTranslation(-0.5,-0.5)*AffineMatrixScale(FBitmap.Width,FBitmap.Height)*Matrix; 1565 end else 1566 Matrix := AffineMatrixScale(FBitmap.Width-1,FBitmap.Height-1)*Matrix; 878 1567 end; 879 1568 -
GraphicTest/Packages/bgrabitmap/bgratypewriter.pas
r472 r494 123 123 implementation 124 124 125 uses LCLProc, lazutf8classes; 126 127 {$i winstream.inc} 125 uses BGRAUTF8; 126 127 procedure LEWritePointF(Stream: TStream; AValue: TPointF); 128 begin 129 LEWriteSingle(Stream,AValue.x); 130 LEWriteSingle(Stream,AValue.y); 131 end; 132 133 function LEReadPointF(Stream: TStream): TPointF; 134 begin 135 result.x := LEReadSingle(Stream); 136 result.y := LEReadSingle(Stream); 137 end; 128 138 129 139 function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF; … … 266 276 begin 267 277 inherited WriteContent(AStream); 268 WinWritePointF(AStream, Offset);269 WinWriteLongint(AStream,length(Points));278 LEWritePointF(AStream, Offset); 279 LEWriteLongint(AStream,length(Points)); 270 280 for i := 0 to high(Points) do 271 WinWritePointF(AStream, Points[i]);281 LEWritePointF(AStream, Points[i]); 272 282 end; 273 283 … … 277 287 begin 278 288 inherited ReadContent(AStream); 279 Offset := WinReadPointF(AStream);280 SetLength(tempPts, WinReadLongint(AStream));289 Offset := LEReadPointF(AStream); 290 SetLength(tempPts, LEReadLongint(AStream)); 281 291 for i := 0 to high(tempPts) do 282 tempPts[i] := WinReadPointF(AStream);292 tempPts[i] := LEReadPointF(AStream); 283 293 SetPoints(tempPts); 284 294 end; … … 390 400 AContentSize: longint); 391 401 begin 392 WinWriteByte(AStream, length(AName));402 LEWriteByte(AStream, length(AName)); 393 403 AStream.Write(AName[1],length(AName)); 394 WinWriteLongint(AStream, AContentSize);404 LEWriteLongint(AStream, AContentSize); 395 405 end; 396 406 … … 399 409 var NameLength: integer; 400 410 begin 401 NameLength := WinReadByte(AStream);411 NameLength := LEReadByte(AStream); 402 412 setlength(AName,NameLength); 403 413 AStream.Read(AName[1],length(AName)); 404 AContentSize := WinReadLongint(AStream);414 AContentSize := LEReadLongint(AStream); 405 415 end; 406 416 … … 417 427 procedure TBGRAGlyph.WriteContent(AStream: TStream); 418 428 begin 419 WinWriteLongint(AStream,length(FIdentifier));429 LEWriteLongint(AStream,length(FIdentifier)); 420 430 AStream.Write(FIdentifier[1],length(FIdentifier)); 421 WinWriteSingle(AStream,Width);422 WinWriteSingle(AStream,Height);431 LEWriteSingle(AStream,Width); 432 LEWriteSingle(AStream,Height); 423 433 end; 424 434 … … 426 436 var lIdentifierLength: integer; 427 437 begin 428 lIdentifierLength:= WinReadLongint(AStream);438 lIdentifierLength:= LEReadLongint(AStream); 429 439 setlength(FIdentifier, lIdentifierLength); 430 440 AStream.Read(FIdentifier[1],length(FIdentifier)); 431 Width := WinReadSingle(AStream);432 Height := WinReadSingle(AStream);441 Width := LEReadSingle(AStream); 442 Height := LEReadSingle(AStream); 433 443 end; 434 444 … … 711 721 begin 712 722 for c := AUnicodeFrom to AUnicodeTo do 713 GetGlyph(Unicode ToUTF8(c));723 GetGlyph(UnicodeCharToUTF8(c)); 714 724 end; 715 725 … … 805 815 var Enumerator: TAvgLvlTreeNodeEnumerator; 806 816 begin 807 WinWriteLongint(AStream,CustomHeaderSize);817 LEWriteLongint(AStream,CustomHeaderSize); 808 818 WriteCustomHeader(AStream); 809 819 … … 833 843 GlyphStartPosition: Int64; 834 844 begin 835 HeaderSize := WinReadLongint(AStream);845 HeaderSize := LEReadLongint(AStream); 836 846 GlyphStartPosition:= AStream.Position+HeaderSize; 837 847 Header := ReadCustomTypeWriterHeader(AStream); … … 919 929 begin 920 930 lHeaderName:= HeaderName; 921 WinWriteByte(AStream,length(lHeaderName));931 LEWriteByte(AStream,length(lHeaderName)); 922 932 AStream.Write(lHeaderName[1],length(lHeaderName)); 923 WinWriteLongint(AStream,FGlyphs.Count);933 LEWriteLongint(AStream,FGlyphs.Count); 924 934 end; 925 935 … … 927 937 ): TBGRACustomTypeWriterHeader; 928 938 begin 929 setlength(result.HeaderName, WinReadByte(AStream));939 setlength(result.HeaderName, LEReadByte(AStream)); 930 940 AStream.Read(result.HeaderName[1],length(result.HeaderName)); 931 result.NbGlyphs:= WinReadLongint(AStream);941 result.NbGlyphs:= LEReadLongint(AStream); 932 942 end; 933 943 -
GraphicTest/Packages/bgrabitmap/bgravectorize.pas
r472 r494 190 190 implementation 191 191 192 uses LCLProc, FileUtil, lazutf8classes; 193 194 {$i winstream.inc} 192 uses BGRAUTF8; 193 195 194 function VectorizeMonochrome(ASource: TBGRACustomBitmap; zoom: single; PixelCenteredCoordinates: boolean): ArrayOfTPointF; 196 195 const unitShift = 6; … … 1285 1284 if not FFontPixelMetricComputed and (FFont <> nil) then 1286 1285 begin 1287 FFontPixelMetric := BGRAText.Get FontPixelMetric(FFont);1286 FFontPixelMetric := BGRAText.GetLCLFontPixelMetric(FFont); 1288 1287 FFontPixelMetricComputed := true; 1289 1288 end; … … 1916 1915 end; 1917 1916 until FindNext(SearchRec) <> 0; 1917 FindClose(SearchRec); 1918 1918 SetLength(FDirectoryContent,NbFiles); 1919 1919 end; … … 2010 2010 begin 2011 2011 inherited WriteCustomHeader(AStream); 2012 WinWriteLongint(AStream, length(FName));2012 LEWriteLongint(AStream, length(FName)); 2013 2013 AStream.Write(FName[1],length(FName)); 2014 WinWriteLongint(AStream, integer(FStyle));2015 WinWriteSingle(AStream, FontEmHeightRatio);2016 WinWriteLongint(AStream, Resolution);2014 LEWriteLongint(AStream, integer(FStyle)); 2015 LEWriteSingle(AStream, FontEmHeightRatio); 2016 LEWriteLongint(AStream, Resolution); 2017 2017 metric := FontPixelMetric; 2018 WinWriteLongint(AStream, metric.Baseline);2019 WinWriteLongint(AStream, metric.xLine);2020 WinWriteLongint(AStream, metric.CapLine);2021 WinWriteLongint(AStream, metric.DescentLine);2022 WinWriteLongint(AStream, metric.Lineheight);2018 LEWriteLongint(AStream, metric.Baseline); 2019 LEWriteLongint(AStream, metric.xLine); 2020 LEWriteLongint(AStream, metric.CapLine); 2021 LEWriteLongint(AStream, metric.DescentLine); 2022 LEWriteLongint(AStream, metric.Lineheight); 2023 2023 end; 2024 2024 … … 2048 2048 var lNameLength: integer; 2049 2049 begin 2050 lNameLength := WinReadLongint(AStream);2050 lNameLength := LEReadLongint(AStream); 2051 2051 setlength(result.Name, lNameLength); 2052 2052 AStream.Read(result.Name[1],length(result.Name)); 2053 result.Style := TFontStyles( WinReadLongint(AStream));2054 result.EmHeightRatio:= WinReadSingle(AStream);2055 result.Resolution := WinReadLongint(AStream);2056 result.PixelMetric.Baseline := WinReadLongint(AStream);2057 result.PixelMetric.xLine := WinReadLongint(AStream);2058 result.PixelMetric.CapLine := WinReadLongint(AStream);2059 result.PixelMetric.DescentLine := WinReadLongint(AStream);2060 result.PixelMetric.Lineheight := WinReadLongint(AStream);2053 result.Style := TFontStyles(LEReadLongint(AStream)); 2054 result.EmHeightRatio:= LEReadSingle(AStream); 2055 result.Resolution := LEReadLongint(AStream); 2056 result.PixelMetric.Baseline := LEReadLongint(AStream); 2057 result.PixelMetric.xLine := LEReadLongint(AStream); 2058 result.PixelMetric.CapLine := LEReadLongint(AStream); 2059 result.PixelMetric.DescentLine := LEReadLongint(AStream); 2060 result.PixelMetric.Lineheight := LEReadLongint(AStream); 2061 2061 result.PixelMetric.Defined := result.PixelMetric.Lineheight > 0; 2062 2062 end; -
GraphicTest/Packages/bgrabitmap/bgrawinbitmap.pas
r472 r494 29 29 30 30 uses 31 Classes, SysUtils, BGRA DefaultBitmap, Windows, Graphics, GraphType;31 Classes, SysUtils, BGRALCLBitmap, Windows, Graphics, GraphType; 32 32 33 33 type 34 34 { TBGRAWinBitmap } 35 35 36 TBGRAWinBitmap = class(TBGRA DefaultBitmap)36 TBGRAWinBitmap = class(TBGRALCLBitmap) 37 37 private 38 38 procedure AlphaCorrectionNeeded; 39 39 protected 40 40 DIB_SectionHandle: HBITMAP; 41 FReversed: boolean; 41 42 function DIBitmapInfo(AWidth, AHeight: integer): TBitmapInfo; 42 43 … … 48 49 49 50 procedure Init; override; 51 function GetBitmap: TBitmap; override; 50 52 51 53 public 54 procedure LoadFromBitmapIfNeeded; override; 55 procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean=True); override; 56 procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override; 52 57 procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer; 53 58 ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override; … … 56 61 57 62 implementation 63 64 uses BGRADefaultBitmap, BGRABitmapTypes; 58 65 59 66 type … … 118 125 end; 119 126 127 function TBGRAWinBitmap.GetBitmap: TBitmap; 128 begin 129 Result:=inherited GetBitmap; 130 if (LineOrder = riloTopToBottom) and not FReversed then 131 begin 132 VerticalFlip; 133 FReversed:= true; 134 end; 135 end; 136 137 procedure TBGRAWinBitmap.LoadFromBitmapIfNeeded; 138 begin 139 if FReversed then 140 begin 141 FReversed := false; 142 VerticalFlip; 143 end; 144 if FAlphaCorrectionNeeded then 145 begin 146 DoAlphaCorrection; 147 end; 148 end; 149 150 procedure TBGRAWinBitmap.Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean); 151 begin 152 if self = nil then exit; 153 Draw(ACanvas, Classes.Rect(x,y,x+Width,y+Height), Opaque); 154 end; 155 156 procedure TBGRAWinBitmap.Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean); 157 var 158 info: TBITMAPINFO; 159 begin 160 if (self = nil) or (Width = 0) or (Height = 0) then exit; 161 if TBGRAPixel_RGBAOrder then SwapRedBlue; 162 if Opaque then 163 begin 164 info := DIBitmapInfo(Width, Height); 165 if LineOrder = riloTopToBottom then 166 StretchDIBits(ACanvas.Handle, Rect.Left, Rect.Bottom, Rect.Right - 167 Rect.Left, Rect.Top - Rect.Bottom, 168 0, 0, Width, Height, Data, info, DIB_RGB_COLORS, SRCCOPY) 169 else 170 StretchDIBits(ACanvas.Handle, Rect.Left, Rect.Top, Rect.Right - 171 Rect.Left, Rect.Bottom - Rect.Top, 172 0, 0, Width, Height, Data, info, DIB_RGB_COLORS, SRCCOPY); 173 end 174 else 175 begin 176 if Empty then exit; 177 if LineOrder = riloTopToBottom then VerticalFlip; 178 LoadFromBitmapIfNeeded; 179 ACanvas.StretchDraw(Rect, Bitmap); 180 if LineOrder = riloTopToBottom then VerticalFlip; 181 end; 182 if TBGRAPixel_RGBAOrder then SwapRedBlue; 183 end; 184 120 185 procedure TBGRAWinBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; 121 186 AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); … … 133 198 IsFlipped := True; 134 199 end; 200 if TBGRAPixel_RGBAOrder then 201 begin 202 if Temp = nil then 203 Temp := TBGRAPtrBitmap.Create(AWidth, AHeight, AData); 204 Temp.SwapRedBlue; 205 end; 135 206 136 207 info := DIBitmapInfo(AWidth, AHeight); … … 141 212 if Temp <> nil then 142 213 begin 214 if TBGRAPixel_RGBAOrder then Temp.SwapRedBlue; 143 215 if IsFlipped then 144 216 Temp.VerticalFlip; … … 154 226 function TBGRAWinBitmap.DIBitmapInfo(AWidth, AHeight: integer): TBitmapInfo; 155 227 begin 156 with Result.bmiHeader do228 with {%H-}Result.bmiHeader do 157 229 begin 158 230 biSize := sizeof(Result.bmiHeader); -
GraphicTest/Packages/bgrabitmap/bgrawritelzp.pas
r472 r494 33 33 implementation 34 34 35 uses BGRACompressableBitmap , FPWritePNG;35 uses BGRACompressableBitmap; 36 36 37 37 { TBGRAWriterLazPaint } … … 42 42 OldResampleFilter: TResampleFilter; 43 43 thumbnail: TBGRACustomBitmap; 44 p: PBGRAPixel;45 n: integer;46 44 begin 47 45 result := false; … … 68 66 thumbnail := TBGRACustomBitmap(Img).Resample(w,h,rmFineResample); 69 67 TBGRACustomBitmap(Img).ResampleFilter := OldResampleFilter; 70 71 p := thumbnail.data; //avoid PNG bug with black color transformed into transparent72 for n := thumbnail.NbPixels-1 downto 0 do73 begin74 if (p^.alpha <> 0) and (p^.red = 0) and (p^.green = 0) and (p^.blue = 0) then75 p^.blue := 1;76 inc(p);77 end;78 68 79 69 try … … 89 79 finally 90 80 thumbnail.Free; 81 end; 82 end else 83 begin 84 thumbStream := TMemoryStream.Create; 85 try 86 TBGRACustomBitmap(Img).SaveToStreamAsPng(thumbStream); 87 thumbStream.Position:= 0; 88 Str.CopyFrom(thumbStream, thumbStream.Size); 89 result := true; 90 finally 91 thumbStream.Free; 91 92 end; 92 93 end; -
GraphicTest/Packages/bgrabitmap/blendpixelinline.inc
r472 r494 914 914 begin 915 915 result := ((not a)*b shr 7 + a)*a div 255; 916 { SVG specification would be :917 918 if b <= 128 then919 result := a - (((256 - b-b)*a shr 8)*(not a) shr 8)920 else if a <= 64 then921 result := a + ((b+b - 256)*((((a shl 2)*(a shl 2 + 256) shr 8)*integer(a - 256) shr 8) + a*7) shr 8)922 else923 result := a + ((b+b - 255)*(ByteSqrt(a)-a) shr 8);}924 916 end; 925 917 … … 946 938 c.green * (not destalpha)) shr 8; 947 939 dest^.blue := (ByteSoftLightInline(dest^.blue, c.blue) * destalpha + 940 c.blue * (not destalpha)) shr 8; 941 dest^.alpha := c.alpha; 942 end; 943 end; 944 945 function ByteSvgSoftLightInline(a,b: byte): byte; inline; 946 begin 947 if b <= 128 then 948 result := a - (((256 - b-b)*a shr 8)*(not a) shr 8) 949 else 950 begin 951 dec(b, 128); 952 if a <= 64 then 953 result := a + ((b+b) * NativeUInt(a*7 - ((a shl 2)*(a shl 2 + 256)*NativeUInt(256 - a) shr 16)) shr 8) 954 else 955 result := a + ((b+b+1) * NativeUInt(ByteSqrt(a)-a) shr 8); 956 end; 957 end; 958 959 procedure SvgSoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline; 960 var 961 destalpha: byte; 962 begin 963 destalpha := dest^.alpha; 964 if destalpha = 0 then 965 begin 966 dest^ := c 967 end else 968 if destalpha = 255 then 969 begin 970 dest^.red := ByteSvgSoftLightInline(dest^.red, c.red); 971 dest^.green := ByteSvgSoftLightInline(dest^.green, c.green); 972 dest^.blue := ByteSvgSoftLightInline(dest^.blue, c.blue); 973 dest^.alpha := c.alpha; 974 end else 975 begin 976 dest^.red := (ByteSvgSoftLightInline(dest^.red, c.red) * destalpha + 977 c.red * (not destalpha)) shr 8; 978 dest^.green := (ByteSvgSoftLightInline(dest^.green, c.green) * destalpha + 979 c.green * (not destalpha)) shr 8; 980 dest^.blue := (ByteSvgSoftLightInline(dest^.blue, c.blue) * destalpha + 948 981 c.blue * (not destalpha)) shr 8; 949 982 dest^.alpha := c.alpha; -
GraphicTest/Packages/bgrabitmap/blendpixels.inc
r452 r494 290 290 begin 291 291 SoftLightPixelInline(pdest, psrc^); 292 Inc(pdest); 293 Inc(psrc); 294 Dec(Count); 295 end; 296 end; 297 298 procedure SvgSoftLightPixels(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer); 299 begin 300 while Count > 0 do 301 begin 302 SvgSoftLightPixelInline(pdest, psrc^); 292 303 Inc(pdest); 293 304 Inc(psrc); … … 327 338 @GlowPixels, @ReflectPixels, @LinearOverlayPixels, @OverlayPixels, @DarkenPixels, @LinearMultiplyPixels, @ColorBurnPixels, 328 339 @DifferencePixels, @LinearDifferencePixels, @ExclusionPixels, @LinearExclusionPixels, @SubtractPixels, @LinearSubtractPixels, 329 @SubtractInversePixels, @LinearSubtractInversePixels, @NegationPixels, @LinearNegationPixels, @BlendXorPixels );340 @SubtractInversePixels, @LinearSubtractInversePixels, @NegationPixels, @LinearNegationPixels, @BlendXorPixels, @SvgSoftLightPixels); 330 341 331 342 procedure BlendPixels(pdest: PBGRAPixel; psrc: PBGRAPixel; -
GraphicTest/Packages/bgrabitmap/blendpixelsover.inc
r452 r494 709 709 end; 710 710 711 procedure SvgSoftLightPixelsLinearOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte); 712 var temp: TBGRAPixel; 713 begin 714 while Count > 0 do 715 begin 716 temp := pdest^; 717 SvgSoftLightPixelInline(@temp, psrc^); 718 FastBlendPixelInline(pdest, temp, opacity); 719 Inc(pdest); 720 Inc(psrc); 721 Dec(Count); 722 end; 723 end; 724 711 725 procedure SoftLightPixelsDrawOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte); 712 726 var temp: TBGRAPixel; … … 716 730 temp := pdest^; 717 731 SoftLightPixelInline(@temp, psrc^); 732 DrawPixelInlineWithAlphaCheck(pdest, temp, opacity); 733 Inc(pdest); 734 Inc(psrc); 735 Dec(Count); 736 end; 737 end; 738 739 procedure SvgSoftLightPixelsDrawOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte); 740 var temp: TBGRAPixel; 741 begin 742 while Count > 0 do 743 begin 744 temp := pdest^; 745 SvgSoftLightPixelInline(@temp, psrc^); 718 746 DrawPixelInlineWithAlphaCheck(pdest, temp, opacity); 719 747 Inc(pdest); … … 790 818 @GlowPixelsDrawOver, @ReflectPixelsDrawOver, @LinearOverlayPixelsDrawOver, @OverlayPixelsDrawOver, @DarkenPixelsDrawOver, @LinearMultiplyPixelsDrawOver, @ColorBurnPixelsDrawOver, 791 819 @DifferencePixelsDrawOver, @LinearDifferencePixelsDrawOver, @ExclusionPixelsDrawOver, @LinearExclusionPixelsDrawOver, @SubtractPixelsDrawOver, @LinearSubtractPixelsDrawOver, 792 @SubtractInversePixelsDrawOver, @LinearSubtractInversePixelsDrawOver, @NegationPixelsDrawOver, @LinearNegationPixelsDrawOver, @BlendXorPixelsDrawOver ),820 @SubtractInversePixelsDrawOver, @LinearSubtractInversePixelsDrawOver, @NegationPixelsDrawOver, @LinearNegationPixelsDrawOver, @BlendXorPixelsDrawOver, @SvgSoftLightPixelsDrawOver), 793 821 (@FastBlendPixelsWithOpacity, @FastBlendPixelsWithOpacity, 794 822 @LightenPixelsLinearOver, @ScreenPixelsLinearOver, @AddPixelsLinearOver, @LinearAddPixelsLinearOver, @ColorDodgePixelsLinearOver, @DividePixelsLinearOver, @NiceGlowPixelsLinearOver, @SoftLightPixelsLinearOver, @HardLightPixelsLinearOver, 795 823 @GlowPixelsLinearOver, @ReflectPixelsLinearOver, @LinearOverlayPixelsLinearOver, @OverlayPixelsLinearOver, @DarkenPixelsLinearOver, @LinearMultiplyPixelsLinearOver, @ColorBurnPixelsLinearOver, 796 824 @DifferencePixelsLinearOver, @LinearDifferencePixelsLinearOver, @ExclusionPixelsLinearOver, @LinearExclusionPixelsLinearOver, @SubtractPixelsLinearOver, @LinearSubtractPixelsLinearOver, 797 @SubtractInversePixelsLinearOver, @LinearSubtractInversePixelsLinearOver, @NegationPixelsLinearOver, @LinearNegationPixelsLinearOver, @BlendXorPixelsLinearOver ));825 @SubtractInversePixelsLinearOver, @LinearSubtractInversePixelsLinearOver, @NegationPixelsLinearOver, @LinearNegationPixelsLinearOver, @BlendXorPixelsLinearOver, @SvgSoftLightPixelsLinearOver)); 798 826 799 827 {************************* calling procedure ***************************} -
GraphicTest/Packages/bgrabitmap/blurfast.inc
r472 r494 1 1 2 2 var 3 blurRow: array of UInt32or64; 3 blurRowY,blurRowX: packed array of NativeUInt; 4 iRadiusX,iRadiusY: NativeInt; 5 weightFactor: NativeUInt; 4 6 5 7 { Compute weights of pixels in a row } 6 8 procedure ComputeBlurRow; 7 9 var 8 i: Integer; 9 begin 10 SetLength(blurRow, 2*radius+1); 11 for i := 0 to radius do 12 begin 13 blurRow[i] := i+1; 14 blurRow[high(blurRow)-i] := blurRow[i]; 10 i: NativeInt; 11 ofs: single; 12 begin 13 SetLength(blurRowX, 2*iRadiusX+1); 14 if frac(radiusX)=0 then ofs := 1 else ofs := frac(radiusX); 15 for i := 0 to iRadiusX do 16 begin 17 blurRowX[i] := round((i+ofs)*weightFactor); 18 blurRowX[high(blurRowX)-i] := blurRowX[i]; 19 end; 20 SetLength(blurRowY, 2*iRadiusY+1); 21 if frac(radiusY)=0 then ofs := 1 else ofs := frac(radiusY); 22 for i := 0 to iRadiusY do 23 begin 24 blurRowY[i] := round((i+ofs)*weightFactor); 25 blurRowY[high(blurRowY)-i] := blurRowY[i]; 15 26 end; 16 27 end; … … 19 30 var 20 31 srcDelta, 21 verticalWeightShift, horizontalWeightShift: integer; 32 verticalWeightShift, horizontalWeightShift: NativeInt; 33 ys1,ys2: NativeInt; 22 34 23 35 { Compute blur result in a vertical direction } 24 procedure ComputeVerticalRow(psrc: PBGRAPixel; var sums: TRowSum; ys1,ys2: integer); inline; 25 var ys: integer; 26 c: TBGRAPixel; 27 w,aw: cardinal; 28 begin 29 for ys := ys1 to ys2 do 36 procedure ComputeVerticalRow(psrc: PBGRAPixel; var sums: TRowSum; pw: PNativeUInt; count: NativeInt); 37 var w: NativeUInt; 38 c: DWord; 39 begin 40 while count > 0 do 30 41 with sums do 31 42 begin 32 c := psrc^; 33 w := blurRow[ys]; //apply pixel weight 34 aw := c.alpha*w; 35 sumA += aw; 43 dec(count); 44 w := pw^; //apply pixel weight 45 inc(pw); 46 c := PDWord(psrc)^; 47 inc(PByte(psrc),srcDelta); 36 48 aDiv += w; 37 38 aw := aw shr verticalWeightShift; 49 w *= ((c shr TBGRAPixel_AlphaShift) and $ff); 50 sumA += w; 51 w := w shr verticalWeightShift; 52 rgbDiv += w; 39 53 {$hints off} 40 sumR += c.red*aw; 41 sumG += c.green*aw; 42 sumB += c.blue*aw; 43 rgbDiv += aw; 54 sumR += ((c shr TBGRAPixel_RedShift) and $ff)*w; 55 sumG += ((c shr TBGRAPixel_GreenShift) and $ff)*w; 56 sumB += ((c shr TBGRAPixel_BlueShift) and $ff)*w; 44 57 {$hints on} 45 inc(psrc,srcDelta);46 58 end; 47 59 end; 48 60 49 61 var 50 sums: array of TRowSum; 51 sumStartIndex,curIndex: integer; 62 psum, psumEnd: PRowSum; 63 sums: packed array of TRowSum; 64 sumStartIndex: NativeInt; 52 65 total: TRowSum; 53 66 extendedTotal: TExtendedRowSum; 54 yb,xb,xs,ys1,ys2,x: integer; 55 w: cardinal; 56 pdest: PBGRAPixel; 57 bmpWidth,bmpHeight : integer; 67 yb,xb,xs,x,xEnd: NativeInt; 68 w: NativeUInt; 69 pw: PNativeUInt; 70 psrc,pdest: PBGRAPixel; 71 bmpWidth,bmpHeight : NativeInt; 58 72 accumulationFactor: double; 59 73 bounds: TRect; 74 highSum: NativeInt; 75 tempDest: TBGRACustomBitmap; 60 76 61 77 begin 62 if radius = 0 then 78 radiusX := round(radiusX*10)*0.1; 79 radiusY := round(radiusY*10)*0.1; 80 if (radiusX <= 0) and (radiusY <= 0) then 63 81 begin 64 82 ADestination.PutImage(0,0,bmp,dmSet); 65 83 exit; 66 84 end; 85 iRadiusX := ceil(radiusX); 86 iRadiusY := ceil(radiusY); 87 if (frac(radiusX)=0) and (frac(radiusY)=0) then 88 weightFactor:= 1 89 else 90 weightFactor:= 10; 67 91 bmpWidth := bmp.Width; 68 92 bmpHeight := bmp.Height; … … 72 96 bounds := bmp.GetImageBounds; 73 97 if IsRectEmpty(bounds) then exit; 74 bounds.Left := max(0, bounds.Left - radius);75 bounds.Top := max(0, bounds.Top - radius);76 bounds.Right := min(bmp.Width, bounds.Right + radius);77 bounds.Bottom := min(bmp.Height, bounds.Bottom + radius);98 bounds.Left := max(0, bounds.Left - iRadiusX); 99 bounds.Top := max(0, bounds.Top - iRadiusY); 100 bounds.Right := min(bmp.Width, bounds.Right + iRadiusX); 101 bounds.Bottom := min(bmp.Height, bounds.Bottom + iRadiusY); 78 102 if not IntersectRect(bounds,bounds,ABounds) then exit; 79 103 80 accumulationFactor := (radius+2)*(radius+1) div 2 + (radius+1)*radius div 2; 104 if radiusX*radiusY >= 100 then 105 begin 106 tempDest := ADestination.NewBitmap(ADestination.Width,ADestination.Height); 107 FilterBlurBox(bmp,bounds,radiusX/3.2,radiusY/3.2,tempDest); 108 FilterBlurBox(tempDest,bounds,radiusX/2.9,radiusY/2.9,ADestination); 109 FilterBlurBox(ADestination,bounds,radiusX/3.2,radiusY/3.2,tempDest); 110 FilterBlurBox(tempDest,bounds,radiusX/2.3,radiusY/2.3,ADestination, ACheckShouldStop); 111 tempDest.Free; 112 exit; 113 end; 114 115 accumulationFactor := (iRadiusY+2)*(iRadiusY+1) div 2 + (iRadiusY+1)*iRadiusY div 2; 116 accumulationFactor *= sqr(weightFactor); 81 117 verticalWeightShift := 0; 82 while accumulationFactor > (high( UInt32or64) shr 16) + 1 do118 while accumulationFactor > (high(NativeUInt) shr 16) + 1 do 83 119 begin 84 120 inc(verticalWeightShift); … … 86 122 end; 87 123 horizontalWeightShift:= 0; 88 accumulationFactor *= ((radius+2)*(radius+1) div 2 + (radius+1)*radius div 2); 89 while accumulationFactor > (high(UInt32or64) shr 16) + 1 do 124 accumulationFactor *= ((iRadiusX+2)*(iRadiusX+1) div 2 + (iRadiusX+1)*iRadiusX div 2); 125 accumulationFactor *= sqr(weightFactor); 126 while accumulationFactor > (high(NativeUInt) shr 16) + 1 do 90 127 begin 91 128 inc(horizontalWeightShift); … … 94 131 ComputeBlurRow; 95 132 //current vertical sums 96 setlength(sums, 2*radius+1); 133 setlength(sums, 2*iRadiusX+1); 134 highSum := high(Sums); 135 psumEnd := @sums[highSum]; 136 inc(psumEnd); 97 137 if bmp.LineOrder = riloTopToBottom then 98 srcDelta := bmpWidth else 99 srcDelta := -bmpWidth; 138 srcDelta := bmpWidth*sizeof(TBGRAPixel) else 139 srcDelta := -bmpWidth*sizeof(TBGRAPixel); 140 141 xEnd := bounds.left-iRadiusX+highSum; 142 if xEnd >= bmpWidth then xEnd := bmpWidth-1; 100 143 //loop through destination bitmap 101 144 for yb := bounds.top to bounds.bottom-1 do … … 103 146 if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break; 104 147 //evalute available vertical range 105 if yb - radius< 0 then106 ys1 := radius- yb148 if yb - iRadiusY < 0 then 149 ys1 := iRadiusY - yb 107 150 else 108 151 ys1 := 0; 109 if yb + radius>= bmpHeight then110 ys2 := bmpHeight - yb + radius - 1152 if yb + iRadiusY >= bmpHeight then 153 ys2 := bmpHeight-1 - yb + iRadiusY 111 154 else 112 ys2 := high(sums);155 ys2 := 2*iRadiusY; 113 156 114 157 { initial vertical rows are computed here. Later, 115 158 for each pixel, vertical sums are shifted, so there 116 159 is only one vertical sum to calculate } 117 for xs := 0 to high(sums) do 118 begin 119 fillchar(sums[xs],sizeof(TRowSum),0); 120 x := bounds.left-radius+xs; 121 if (x >= 0) and (x < bmpWidth) then 122 ComputeVerticalRow(bmp.ScanLine[yb-radius+ys1]+x,sums[xs],ys1,ys2); 160 fillchar(sums[0],sizeof(TRowSum)*length(sums),0); 161 x := bounds.left-iRadiusX; 162 if x < 0 then 163 begin 164 xs := -x; 165 x := 0; 166 end else 167 xs := 0; 168 psrc := bmp.ScanLine[yb-iRadiusY+ys1]+x; 169 psum := @sums[xs]; 170 pw := @blurRowY[ys1]; 171 while true do 172 begin 173 ComputeVerticalRow(psrc,psum^,pw,ys2-ys1+1); 174 inc(x); 175 inc(psrc); 176 if x > xEnd then break; 177 inc(psum); 123 178 end; 124 179 sumStartIndex := 0; … … 128 183 begin 129 184 //add vertical rows 130 curIndex:= sumStartIndex; 185 pw := @blurRowX[0]; 186 psum := @sums[sumStartIndex]; 131 187 if horizontalWeightShift > 4 then 132 188 begin //we don't want to loose too much precision 133 {$hints off} 134 fillchar(extendedTotal,sizeof(extendedTotal),0); 135 {$hints on} 136 for xs := 0 to high(sums) do 137 with sums[curIndex] do 189 fillchar({%H-}extendedTotal,sizeof(extendedTotal),0); 190 for xs := highSum downto 0 do 191 with psum^ do 138 192 begin 139 w := blurRow[xs]; 193 w := pw^; 194 inc(pw); 140 195 extendedTotal.sumA += TExtendedRowValue(sumA)*w; 141 196 extendedTotal.aDiv += TExtendedRowValue(aDiv)*w; … … 144 199 extendedTotal.sumB += TExtendedRowValue(sumB)*w; 145 200 extendedTotal.rgbDiv += TExtendedRowValue(rgbDiv)*w; 146 inc( curIndex);147 if curIndex = length(sums) then curIndex := 0;201 inc(psum); 202 if psum >= psumEnd then pSum := @sums[0]; 148 203 end; 149 204 if (extendedTotal.aDiv > 0) and (extendedTotal.rgbDiv > 0) then … … 154 209 if horizontalWeightShift > 0 then 155 210 begin //lossy but efficient way 156 {$hints off} 157 fillchar(total,sizeof(total),0); 158 {$hints on} 159 for xs := 0 to high(sums) do 160 with sums[curIndex] do 211 fillchar({%H-}total,sizeof(total),0); 212 for xs := highSum downto 0 do 213 with psum^ do 161 214 begin 162 w := blurRow[xs]; 215 w := pw^; 216 inc(pw); 163 217 total.sumA += sumA*w shr horizontalWeightShift; 164 218 total.aDiv += aDiv*w shr horizontalWeightShift; … … 167 221 total.sumB += sumB*w shr horizontalWeightShift; 168 222 total.rgbDiv += rgbDiv*w shr horizontalWeightShift; 169 inc( curIndex);170 if curIndex = length(sums) then curIndex := 0;223 inc(psum); 224 if psum >= psumEnd then pSum := @sums[0]; 171 225 end; 172 226 if (total.aDiv > 0) and (total.rgbDiv > 0) then … … 179 233 fillchar(total,sizeof(total),0); 180 234 {$hints on} 181 for xs := 0 to high(sums)do182 with sums[curIndex]do235 for xs := highSum downto 0 do 236 with psum^ do 183 237 begin 184 w := blurRow[xs]; 238 w := pw^; 239 inc(pw); 185 240 total.sumA += sumA*w; 186 241 total.aDiv += aDiv*w; … … 189 244 total.sumB += sumB*w; 190 245 total.rgbDiv += rgbDiv*w; 191 inc( curIndex);192 if curIndex = length(sums) then curIndex := 0;246 inc(psum); 247 if psum >= psumEnd then pSum := @sums[0]; 193 248 end; 194 249 if (total.aDiv > 0) and (total.rgbDiv > 0) then 195 pdest^ := ComputeAverage(total)250 pdest^ := ComputeAverage(total) 196 251 else 197 252 pdest^:= BGRAPixelTransparent; … … 199 254 inc(pdest); 200 255 //shift vertical rows 201 fillchar(sums[sumStartIndex],sizeof(TRowSum),0); 202 x := xb+1-radius+high(sums); 203 if (x >= 0) and (x < bmpWidth) then 204 ComputeVerticalRow(bmp.ScanLine[yb-radius+ys1]+x,sums[sumStartIndex],ys1,ys2); 256 psum := @sums[sumStartIndex]; 257 fillchar(psum^,sizeof(TRowSum),0); 258 if x < bmpWidth then 259 begin 260 ComputeVerticalRow(psrc,psum^,@blurRowY[ys1],ys2-ys1+1); 261 inc(x); 262 inc(psrc); 263 end; 205 264 inc(sumStartIndex); 206 if sumStartIndex = length(sums)then sumStartIndex := 0;265 if sumStartIndex > highSum then sumStartIndex := 0; 207 266 end; 208 267 end; -
GraphicTest/Packages/bgrabitmap/blurnormal.inc
r472 r494 1 type 2 PWeightedPixel = ^TWeightedPixel; 3 TWeightedPixel = packed record 4 Coord: TPoint; 5 Weight: NativeInt; 6 PtrOfs: NativeInt; 7 end; 8 1 9 var 2 10 maskWidth,maskHeight: integer; 3 blurOfs: 4 PixelWeight: array of integer;5 Pixel Ofs: array of TPoint;11 blurOfs: TPoint; 12 ppixel: PWeightedPixel; 13 Pixel: array of TWeightedPixel; 6 14 PixelArrayLineStart: array of integer; 7 DiffPixelWeight: array of integer; 8 DiffPixelOfs: array of TPoint; 15 DiffPixel: array of TWeightedPixel; 9 16 DiffPixelArrayLineStart: array of integer; 10 17 11 procedure LoadMask; 12 var x,y,n: integer; 13 tempWeight: integer; 14 diffMask: array of array of integer; 18 bmpWidth,bmpHeight,lineDelta: NativeInt; 19 20 procedure LoadMask(out ABlurOfs: TPoint); 21 var x,y,n,i: NativeInt; 22 tempWeight: NativeInt; 23 diffMask: array of packed array of NativeInt; 24 p: PBGRAPixel; 15 25 begin 16 blurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1);26 ABlurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1); 17 27 18 28 //count number of non empty pixels … … 20 30 maskHeight := blurMask.Height; 21 31 n := 0; 22 for y := 0 to maskHeight - 1 do 23 for x := 0 to maskWidth - 1 do 24 if blurMask.GetPixel(x, y).red <> 0 then Inc(n); 32 p := blurMask.Data; 33 for i := blurMask.NbPixels-1 downto 0 do 34 begin 35 if p^.red <> 0 then inc(n); 36 inc(p); 37 end; 25 38 26 39 //initialize arrays 27 40 setlength(diffMask, maskHeight, maskWidth+1); 28 41 for y := 0 to maskHeight - 1 do 29 for x := 0 to maskWidth do 30 diffMask[y,x] := 0; 31 32 setlength(PixelWeight, n); 33 setlength(PixelOfs, n); 42 fillchar(diffMask[y,0], (maskWidth+1)*sizeof(NativeInt), 0); 43 44 setlength(Pixel, n); 34 45 setlength(PixelArrayLineStart, maskHeight+1); //stores the first pixel of each line 35 46 n := 0; … … 38 49 begin 39 50 PixelArrayLineStart[y] := n; 51 p := blurMask.ScanLine[y]; 40 52 for x := 0 to maskWidth - 1 do 41 53 begin 42 tempWeight := blurMask.GetPixel(x, y).red; 54 tempWeight := p^.red; 55 inc(p); 43 56 diffMask[y,x] -= tempWeight; 44 57 diffMask[y,x+1] += tempWeight; … … 46 59 if tempWeight <> 0 then 47 60 begin 48 PixelWeight[n] := tempWeight; 49 PixelOfs[n] := Point(x,y); 61 Pixel[n].Weight := tempWeight; 62 Pixel[n].Coord := Point(x,y); 63 Pixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X)*sizeof(TBGRAPixel); 50 64 Inc(n); 51 65 end; … … 61 75 62 76 //initialize arrays 63 setlength(DiffPixelWeight, n); 64 setlength(DiffPixelOfs, n); 77 setlength(DiffPixel, n); 65 78 setlength(DiffPixelArrayLineStart, maskHeight+1); //stores the first pixel of each diff line 66 79 n := 0; … … 74 87 if tempWeight <> 0 then 75 88 begin 76 DiffPixelWeight[n] := tempWeight; 77 DiffPixelOfs[n] := Point(x-1,y); 89 DiffPixel[n].Weight := tempWeight; 90 DiffPixel[n].Coord := Point(x-1,y); 91 DiffPixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X-1)*sizeof(TBGRAPixel); 78 92 Inc(n); 79 93 end; … … 83 97 end; 84 98 85 var 86 curScans: array of PBGRAPixel; 87 bounds: TRect; 88 89 {procedure ShowCurScans; 90 var str: string; 91 i: Integer; 92 begin 93 str := ''; 94 for i := 0 to high(curScans) do 95 begin 96 if i <> 0 then str += ', '; 97 if curScans[i]=nil then str += 'nil' else 98 str += 'bmp['+inttostr(curScans[i]-bmp.Data)+']'; 99 end; 100 ShowMessage(str); 101 end;} 102 103 function PrepareScan: boolean; 104 var 105 bmpY: integer; 106 y : Integer; 99 function PrepareScan(AWantedBounds: TRect; out AClippedBounds: TRect): boolean; 107 100 begin 108 101 //evaluate required bounds taking blur radius into acount 109 bounds := bmp.GetImageBounds;110 if IsRectEmpty( bounds) then102 AClippedBounds := bmp.GetImageBounds; 103 if IsRectEmpty(AClippedBounds) then 111 104 begin 112 105 result := false; 113 106 exit; 114 107 end; 115 bounds.Left := max(0, bounds.Left - blurOfs.X);116 bounds.Top := max(0, bounds.Top - blurOfs.Y);117 bounds.Right := min(bmp.Width, bounds.Right + maskWidth - 1 - blurOfs.X);118 bounds.Bottom := min(bmp.Height, bounds.Bottom + maskHeight - 1 - blurOfs.Y);119 if not IntersectRect( bounds, bounds, ABounds) then108 AClippedBounds.Left := max(0, AClippedBounds.Left - blurOfs.X); 109 AClippedBounds.Top := max(0, AClippedBounds.Top - blurOfs.Y); 110 AClippedBounds.Right := min(bmpWidth, AClippedBounds.Right + maskWidth - 1 - blurOfs.X); 111 AClippedBounds.Bottom := min(bmpHeight, AClippedBounds.Bottom + maskHeight - 1 - blurOfs.Y); 112 if not IntersectRect(AClippedBounds, AClippedBounds, AWantedBounds) then 120 113 begin 121 114 result := false; … … 123 116 end; 124 117 125 //init scanlines126 setlength(curScans, maskHeight);127 for y := 0 to maskHeight-1 do128 begin129 bmpY := y+bounds.Top-blurOfs.Y;130 if (bmpY < 0) or (bmpY >= bmp.Height) then131 curScans[y] := nil else132 curScans[y] := bmp.ScanLine[bmpY];133 end;134 //ShowCurScans;135 118 result := true; 136 119 end; 137 120 138 procedure ShiftScan(NewY: integer); inline;139 var y: integer;140 begin141 for y := 0 to maskHeight-2 do142 curScans[y] := curScans[y+1];143 144 //get next scanline145 if newY >= bmp.Height then146 curScans[maskHeight-1] := nil147 else148 curScans[maskHeight-1] := bmp.ScanLine[newY];149 //ShowCurScans;150 end;151 152 121 var 153 yb, xb: integer; 154 mindy, maxdy, n: integer; 155 bmpWidth,bmpX: integer; 156 pixMaskAlpha, maskAlpha: integer; 122 bounds: TRect; 123 yb, xb: NativeInt; 124 mindy, maxdy, n, nStart, nCount, nDiffStart, nDiffCount: NativeInt; 125 bmpX,bmpXBase,bmpYBase: NativeInt; 126 pixMaskAlpha, maskAlpha: NativeInt; 157 127 tempPixel: TBGRAPixel; 158 128 pdest : PBGRAPixel; 159 p t: TPoint;129 psrc : PByte; 160 130 161 131 begin 162 LoadMask; 163 164 if (ADestination.Width <> bmp.Width) or (ADestination.Height <> bmp.Height) then 132 bmpWidth := bmp.Width; 133 bmpHeight:= bmp.Height; 134 if bmp.LineOrder = riloTopToBottom then 135 lineDelta := bmpWidth*sizeof(TBGRAPixel) else 136 lineDelta := -bmpWidth*sizeof(TBGRAPixel); 137 138 if (ADestination.Width <> bmpWidth) or (ADestination.Height <> bmpHeight) then 165 139 raise exception.Create('Dimension mismatch'); 166 140 167 if not PrepareScan then exit; //nothing to do 168 169 bmpWidth := bmp.Width; 141 LoadMask(blurOfs); 142 if not PrepareScan(ABounds, bounds) then exit; //nothing to do 143 144 bmpYBase := bounds.Top - blurOfs.Y; 145 170 146 //loop through destination 171 147 for yb := bounds.Top to bounds.Bottom - 1 do 172 148 begin 173 149 if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break; 150 psrc := PByte(bmp.ScanLine[yb]+bounds.Left); 174 151 pdest := ADestination.ScanLine[yb] + bounds.Left; 175 152 //compute vertical range 176 153 mindy := max(-blurOfs.Y, -yb); 177 maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmp .Height - 1 - yb);154 maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmpHeight - 1 - yb); 178 155 179 156 sumR := 0; … … 186 163 {$endif} 187 164 165 bmpXBase := bounds.Left-blurOfs.X; 166 nStart := PixelArrayLineStart[mindy+blurOfs.Y]; 167 nCount := PixelArrayLineStart[maxdy+blurOfs.Y+1]-nStart; 168 ppixel:= @Pixel[nStart]; 188 169 //go through pixel list of the current vertical range 189 for n := PixelArrayLineStart[mindy+blurOfs.Y] to PixelArrayLineStart[maxdy+blurOfs.Y+1]-1 do 190 begin 191 pt := PixelOfs[n]; 192 bmpX := bounds.Left-blurOfs.X+pt.x; 170 for n := nCount-1 downto 0 do 171 begin 172 bmpX := bmpXBase+ppixel^.Coord.x; 193 173 //check horizontal range 194 174 if (bmpX >= 0) and (bmpX < bmpWidth) then 195 175 begin 196 tempPixel := (curScans[pt.y]+bmpX)^;197 maskAlpha := PixelWeight[n];176 tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^; 177 maskAlpha := ppixel^.Weight; 198 178 pixMaskAlpha := maskAlpha * tempPixel.alpha; 199 179 sumA += pixMaskAlpha; … … 209 189 {$hints on} 210 190 end; 211 end; 212 213 for xb := bounds.Left to Bounds.Right - 1 do 214 begin 215 if xb > bounds.left then 216 begin 217 for n := DiffPixelArrayLineStart[mindy+blurOfs.Y] to DiffPixelArrayLineStart[maxdy+blurOfs.Y+1]-1 do 218 begin 219 pt := DiffPixelOfs[n]; 220 bmpX := xb-blurOfs.X+pt.x; 191 inc(ppixel); 192 end; 193 194 //compute average 195 if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then 196 pdest^ := BGRAPixelTransparent 197 else 198 pdest^ := computeAverage; 199 200 nDiffStart := DiffPixelArrayLineStart[mindy+blurOfs.Y]; 201 nDiffCount := DiffPixelArrayLineStart[maxdy+blurOfs.Y+1]-nDiffStart; 202 203 if nDiffCount < nCount then 204 begin 205 for xb := bounds.Left+1 to Bounds.Right - 1 do 206 begin 207 Inc(pdest); 208 inc(bmpXBase); 209 inc(psrc,sizeof(TBGRAPixel)); 210 211 ppixel:= @DiffPixel[nDiffStart]; 212 for n := nDiffCount-1 downto 0 do 213 begin 214 bmpX := bmpXBase+ppixel^.Coord.x; 221 215 if (bmpX >= 0) and (bmpX < bmpWidth) then 222 216 begin 223 tempPixel := (curScans[pt.y]+bmpX)^;224 maskAlpha := DiffPixelWeight[n];217 tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^; 218 maskAlpha := ppixel^.Weight; 225 219 pixMaskAlpha := maskAlpha * tempPixel.alpha; 226 220 sumA += pixMaskAlpha; … … 236 230 {$hints on} 237 231 end; 238 end; 239 end; 240 241 //compute average 242 if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then 243 pdest^ := BGRAPixelTransparent 244 else 245 pdest^ := computeAverage; 246 247 Inc(pdest); 248 end; 249 250 ShiftScan(yb-blurOfs.Y+maskHeight); 232 inc(ppixel); 233 end; 234 235 //compute average 236 if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then 237 pdest^ := BGRAPixelTransparent 238 else 239 pdest^ := ComputeAverage; 240 end; 241 end else 242 begin 243 for xb := bounds.Left+1 to Bounds.Right - 1 do 244 begin 245 Inc(pdest); 246 inc(bmpXBase); 247 inc(psrc,sizeof(TBGRAPixel)); 248 249 sumR := 0; 250 sumG := 0; 251 sumB := 0; 252 sumA := 0; 253 Adiv := 0; 254 {$ifdef PARAM_MASKSHIFT} 255 RGBdiv := 0; 256 {$endif} 257 258 ppixel:= @Pixel[nStart]; 259 for n := nCount-1 downto 0 do 260 begin 261 bmpX := bmpXBase+ppixel^.Coord.x; 262 //check horizontal range 263 if (bmpX >= 0) and (bmpX < bmpWidth) then 264 begin 265 tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^; 266 maskAlpha := ppixel^.Weight; 267 pixMaskAlpha := maskAlpha * tempPixel.alpha; 268 sumA += pixMaskAlpha; 269 Adiv += maskAlpha; 270 {$ifdef PARAM_MASKSHIFT} 271 pixMaskAlpha := pixMaskAlpha shr maskShift; 272 RGBdiv += pixMaskAlpha; 273 {$endif} 274 {$hints off} 275 sumR += tempPixel.red * pixMaskAlpha; 276 sumG += tempPixel.green * pixMaskAlpha; 277 sumB += tempPixel.blue * pixMaskAlpha; 278 {$hints on} 279 end; 280 inc(ppixel); 281 end; 282 283 //compute average 284 if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then 285 pdest^ := BGRAPixelTransparent 286 else 287 pdest^ := computeAverage; 288 end; 289 end; 290 291 inc(bmpYBase); 251 292 end; 252 293 ADestination.InvalidateBitmap; -
GraphicTest/Packages/bgrabitmap/csscolorconst.inc
r472 r494 1 {$IFDEF INCLUDE_COLOR_CONST} 2 {$UNDEF INCLUDE_COLOR_CONST} 1 {=== Color definitions ===} 2 3 {$IFDEF INCLUDE_INTERFACE} 4 {$UNDEF INCLUDE_INTERFACE} 5 var 6 {* This is the value used for transparent pixels. In theory, any 7 color with alpha = 0 is transparent, however it is recommended to 8 use all other channels to zero as well. } 9 BGRAPixelTransparent: TBGRAPixel; 10 11 {* [#FFFFFF] White opaque } 12 BGRAWhite: TBGRAPixel; 13 {* [#000000] Black opaque } 14 BGRABlack: TBGRAPixel; 15 3 16 const 17 {* This color [#000001] looks just like black. It is needed for drawing black 18 shapes using the ''Canvas'' property of ''TBGRABitmap''. This is a standard 19 ''TCanvas'' and when drawing with pure black (''clBlack''), there is no way to know if 20 something has been drawn or if it is transparent } 21 clBlackOpaque = TColor($010000); 22 23 var 4 24 //VGA colors 5 VGABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255); 6 VGAGray: TBGRAPixel = (blue: 128; green: 128; red: 128; alpha: 255); 7 VGASilver: TBGRAPixel = (blue: 192; green: 192; red: 192; alpha: 255); 8 VGAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255); 9 VGAMaroon: TBGRAPixel = (blue: 0; green: 0; red: 128; alpha: 255); 10 VGARed: TBGRAPixel = (blue: 0; green: 0; red: 255; alpha: 255); 11 VGAPurple: TBGRAPixel = (blue: 128; green: 0; red: 128; alpha: 255); 12 VGAFuchsia: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255); 13 VGAGreen: TBGRAPixel = (blue: 0; green: 128; red: 0; alpha: 255); 14 VGALime: TBGRAPixel = (blue: 0; green: 255; red: 0; alpha: 255); 15 VGAOlive: TBGRAPixel = (blue: 0; green: 128; red: 128; alpha: 255); 16 VGAYellow: TBGRAPixel = (blue: 0; green: 255; red: 255; alpha: 255); 17 VGANavy: TBGRAPixel = (blue: 128; green: 0; red: 0; alpha: 255); 18 VGABlue: TBGRAPixel = (blue: 255; green: 0; red: 0; alpha: 255); 19 VGATeal: TBGRAPixel = (blue: 128; green: 128; red: 0; alpha: 255); 20 VGAAqua: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255); 25 VGABlack,VGAGray,VGASilver,VGAWhite, 26 VGAMaroon,VGARed,VGAPurple,VGAFuchsia, 27 VGAGreen,VGALime,VGAOlive,VGAYellow, 28 VGANavy,VGABlue,VGATeal,VGAAqua: TBGRAPixel; 21 29 22 30 //Red colors 23 CSSIndianRed: TBGRAPixel = (blue: 92; green: 92; red: 205; alpha: 255); 24 CSSLightCoral: TBGRAPixel = (blue: 128; green: 128; red: 240; alpha: 255); 25 CSSSalmon: TBGRAPixel = (blue: 114; green: 128; red: 250; alpha: 255); 26 CSSDarkSalmon: TBGRAPixel = (blue: 122; green: 150; red: 233; alpha: 255); 27 CSSRed: TBGRAPixel = (blue: 0; green: 0; red: 255; alpha: 255); 28 CSSCrimson: TBGRAPixel = (blue: 60; green: 20; red: 220; alpha: 255); 29 CSSFireBrick: TBGRAPixel = (blue: 34; green: 34; red: 178; alpha: 255); 30 CSSDarkRed: TBGRAPixel = (blue: 0; green: 0; red: 139; alpha: 255); 31 31 CSSIndianRed,CSSLightCoral,CSSSalmon,CSSDarkSalmon, 32 CSSRed,CSSCrimson,CSSFireBrick,CSSDarkRed: TBGRAPixel; 32 33 //Pink colors 33 CSSPink: TBGRAPixel = (blue: 203; green: 192; red: 255; alpha: 255); 34 CSSLightPink: TBGRAPixel = (blue: 193; green: 182; red: 255; alpha: 255); 35 CSSHotPink: TBGRAPixel = (blue: 180; green: 105; red: 255; alpha: 255); 36 CSSDeepPink: TBGRAPixel = (blue: 147; green: 20; red: 255; alpha: 255); 37 CSSMediumVioletRed: TBGRAPixel = (blue: 133; green: 21; red: 199; alpha: 255); 38 CSSPaleVioletRed: TBGRAPixel = (blue: 147; green: 112; red: 219; alpha: 255); 39 34 CSSPink,CSSLightPink,CSSHotPink,CSSDeepPink, 35 CSSMediumVioletRed,CSSPaleVioletRed: TBGRAPixel; 40 36 //Orange colors 41 CSSLightSalmon: TBGRAPixel = (blue: 122; green: 160; red: 255; alpha: 255); 42 CSSCoral: TBGRAPixel = (blue: 80; green: 127; red: 255; alpha: 255); 43 CSSTomato: TBGRAPixel = (blue: 71; green: 99; red: 255; alpha: 255); 44 CSSOrangeRed: TBGRAPixel = (blue: 0; green: 69; red: 255; alpha: 255); 45 CSSDarkOrange: TBGRAPixel = (blue: 0; green: 140; red: 255; alpha: 255); 46 CSSOrange: TBGRAPixel = (blue: 0; green: 165; red: 255; alpha: 255); 47 37 CSSLightSalmon,CSSCoral,CSSTomato,CSSOrangeRed, 38 CSSDarkOrange,CSSOrange: TBGRAPixel; 48 39 //Yellow colors 49 CSSGold: TBGRAPixel = (blue: 0; green: 215; red: 255; alpha: 255); 50 CSSYellow: TBGRAPixel = (blue: 0; green: 255; red: 255; alpha: 255); 51 CSSLightYellow: TBGRAPixel = (blue: 224; green: 255; red: 255; alpha: 255); 52 CSSLemonChiffon: TBGRAPixel = (blue: 205; green: 250; red: 255; alpha: 255); 53 CSSLightGoldenrodYellow: TBGRAPixel = (blue: 210; green: 250; red: 250; alpha: 255); 54 CSSPapayaWhip: TBGRAPixel = (blue: 213; green: 239; red: 255; alpha: 255); 55 CSSMoccasin: TBGRAPixel = (blue: 181; green: 228; red: 255; alpha: 255); 56 CSSPeachPuff: TBGRAPixel = (blue: 185; green: 218; red: 255; alpha: 255); 57 CSSPaleGoldenrod: TBGRAPixel = (blue: 170; green: 232; red: 238; alpha: 255); 58 CSSKhaki: TBGRAPixel = (blue: 140; green: 230; red: 240; alpha: 255); 59 CSSDarkKhaki: TBGRAPixel = (blue: 107; green: 183; red: 189; alpha: 255); 60 40 CSSGold,CSSYellow,CSSLightYellow,CSSLemonChiffon, 41 CSSLightGoldenrodYellow,CSSPapayaWhip,CSSMoccasin,CSSPeachPuff, 42 CSSPaleGoldenrod,CSSKhaki,CSSDarkKhaki: TBGRAPixel; 61 43 //Purple colors 62 CSSLavender: TBGRAPixel = (blue: 250; green: 230; red: 230; alpha: 255); 63 CSSThistle: TBGRAPixel = (blue: 216; green: 191; red: 216; alpha: 255); 64 CSSPlum: TBGRAPixel = (blue: 221; green: 160; red: 221; alpha: 255); 65 CSSViolet: TBGRAPixel = (blue: 238; green: 130; red: 238; alpha: 255); 66 CSSOrchid: TBGRAPixel = (blue: 214; green: 112; red: 218; alpha: 255); 67 CSSFuchsia: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255); 68 CSSMagenta: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255); 69 CSSMediumOrchid: TBGRAPixel = (blue: 211; green: 85; red: 186; alpha: 255); 70 CSSMediumPurple: TBGRAPixel = (blue: 219; green: 112; red: 147; alpha: 255); 71 CSSBlueViolet: TBGRAPixel = (blue: 226; green: 43; red: 138; alpha: 255); 72 CSSDarkViolet: TBGRAPixel = (blue: 211; green: 0; red: 148; alpha: 255); 73 CSSDarkOrchid: TBGRAPixel = (blue: 204; green: 50; red: 153; alpha: 255); 74 CSSDarkMagenta: TBGRAPixel = (blue: 139; green: 0; red: 139; alpha: 255); 75 CSSPurple: TBGRAPixel = (blue: 128; green: 0; red: 128; alpha: 255); 76 CSSIndigo: TBGRAPixel = (blue: 130; green: 0; red: 75; alpha: 255); 77 CSSDarkSlateBlue: TBGRAPixel = (blue: 139; green: 61; red: 72; alpha: 255); 78 CSSSlateBlue: TBGRAPixel = (blue: 205; green: 90; red: 106; alpha: 255); 79 CSSMediumSlateBlue: TBGRAPixel = (blue: 238; green: 104; red: 123; alpha: 255); 80 44 CSSLavender,CSSThistle,CSSPlum,CSSViolet, 45 CSSOrchid,CSSFuchsia,CSSMagenta,CSSMediumOrchid, 46 CSSMediumPurple,CSSBlueViolet,CSSDarkViolet,CSSDarkOrchid, 47 CSSDarkMagenta,CSSPurple,CSSIndigo,CSSDarkSlateBlue, 48 CSSSlateBlue,CSSMediumSlateBlue: TBGRAPixel; 81 49 //Green colors 82 CSSGreenYellow: TBGRAPixel = (blue: 47; green: 255; red: 173; alpha: 255); 83 CSSChartreuse: TBGRAPixel = (blue: 0; green: 255; red: 127; alpha: 255); 84 CSSLawnGreen: TBGRAPixel = (blue: 0; green: 252; red: 124; alpha: 255); 85 CSSLime: TBGRAPixel = (blue: 0; green: 255; red: 0; alpha: 255); 86 CSSLimeGreen: TBGRAPixel = (blue: 50; green: 205; red: 50; alpha: 255); 87 CSSPaleGreen: TBGRAPixel = (blue: 152; green: 251; red: 152; alpha: 255); 88 CSSLightGreen: TBGRAPixel = (blue: 144; green: 238; red: 144; alpha: 255); 89 CSSMediumSpringGreen: TBGRAPixel = (blue: 154; green: 250; red: 0; alpha: 255); 90 CSSSpringGreen: TBGRAPixel = (blue: 127; green: 255; red: 0; alpha: 255); 91 CSSMediumSeaGreen: TBGRAPixel = (blue: 113; green: 179; red: 60; alpha: 255); 92 CSSSeaGreen: TBGRAPixel = (blue: 87; green: 139; red: 46; alpha: 255); 93 CSSForestGreen: TBGRAPixel = (blue: 34; green: 139; red: 34; alpha: 255); 94 CSSGreen: TBGRAPixel = (blue: 0; green: 128; red: 0; alpha: 255); 95 CSSDarkGreen: TBGRAPixel = (blue: 0; green: 100; red: 0; alpha: 255); 96 CSSYellowGreen: TBGRAPixel = (blue: 50; green: 205; red: 154; alpha: 255); 97 CSSOliveDrab: TBGRAPixel = (blue: 35; green: 142; red: 107; alpha: 255); 98 CSSOlive: TBGRAPixel = (blue: 0; green: 128; red: 128; alpha: 255); 99 CSSDarkOliveGreen: TBGRAPixel = (blue: 47; green: 107; red: 85; alpha: 255); 100 CSSMediumAquamarine: TBGRAPixel = (blue: 170; green: 205; red: 102; alpha: 255); 101 CSSDarkSeaGreen: TBGRAPixel = (blue: 143; green: 188; red: 143; alpha: 255); 102 CSSLightSeaGreen: TBGRAPixel = (blue: 170; green: 178; red: 32; alpha: 255); 103 CSSDarkCyan: TBGRAPixel = (blue: 139; green: 139; red: 0; alpha: 255); 104 CSSTeal: TBGRAPixel = (blue: 128; green: 128; red: 0; alpha: 255); 105 50 CSSGreenYellow,CSSChartreuse,CSSLawnGreen,CSSLime, 51 CSSLimeGreen,CSSPaleGreen,CSSLightGreen,CSSMediumSpringGreen, 52 CSSSpringGreen,CSSMediumSeaGreen,CSSSeaGreen,CSSForestGreen, 53 CSSGreen,CSSDarkGreen,CSSYellowGreen,CSSOliveDrab, 54 CSSOlive,CSSDarkOliveGreen,CSSMediumAquamarine,CSSDarkSeaGreen, 55 CSSLightSeaGreen,CSSDarkCyan,CSSTeal: TBGRAPixel; 106 56 //Blue/Cyan colors 107 CSSAqua: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255); 108 CSSCyan: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255); 109 CSSLightCyan: TBGRAPixel = (blue: 255; green: 255; red: 224; alpha: 255); 110 CSSPaleTurquoise: TBGRAPixel = (blue: 238; green: 238; red: 175; alpha: 255); 111 CSSAquamarine: TBGRAPixel = (blue: 212; green: 255; red: 127; alpha: 255); 112 CSSTurquoise: TBGRAPixel = (blue: 208; green: 224; red: 64; alpha: 255); 113 CSSMediumTurquoise: TBGRAPixel = (blue: 204; green: 209; red: 72; alpha: 255); 114 CSSDarkTurquoise: TBGRAPixel = (blue: 209; green: 206; red: 0; alpha: 255); 115 CSSCadetBlue: TBGRAPixel = (blue: 160; green: 158; red: 95; alpha: 255); 116 CSSSteelBlue: TBGRAPixel = (blue: 180; green: 130; red: 70; alpha: 255); 117 CSSLightSteelBlue: TBGRAPixel = (blue: 222; green: 196; red: 176; alpha: 255); 118 CSSPowderBlue: TBGRAPixel = (blue: 230; green: 224; red: 176; alpha: 255); 119 CSSLightBlue: TBGRAPixel = (blue: 230; green: 216; red: 173; alpha: 255); 120 CSSSkyBlue: TBGRAPixel = (blue: 235; green: 206; red: 135; alpha: 255); 121 CSSLightSkyBlue: TBGRAPixel = (blue: 250; green: 206; red: 135; alpha: 255); 122 CSSDeepSkyBlue: TBGRAPixel = (blue: 255; green: 191; red: 0; alpha: 255); 123 CSSDodgerBlue: TBGRAPixel = (blue: 255; green: 144; red: 30; alpha: 255); 124 CSSCornflowerBlue: TBGRAPixel = (blue: 237; green: 149; red: 100; alpha: 255); 125 CSSRoyalBlue: TBGRAPixel = (blue: 255; green: 105; red: 65; alpha: 255); 126 CSSBlue: TBGRAPixel = (blue: 255; green: 0; red: 0; alpha: 255); 127 CSSMediumBlue: TBGRAPixel = (blue: 205; green: 0; red: 0; alpha: 255); 128 CSSDarkBlue: TBGRAPixel = (blue: 139; green: 0; red: 0; alpha: 255); 129 CSSNavy: TBGRAPixel = (blue: 128; green: 0; red: 0; alpha: 255); 130 CSSMidnightBlue: TBGRAPixel = (blue: 112; green: 25; red: 25; alpha: 255); 131 57 CSSAqua,CSSCyan,CSSLightCyan,CSSPaleTurquoise, 58 CSSAquamarine,CSSTurquoise,CSSMediumTurquoise,CSSDarkTurquoise, 59 CSSCadetBlue,CSSSteelBlue,CSSLightSteelBlue,CSSPowderBlue, 60 CSSLightBlue,CSSSkyBlue,CSSLightSkyBlue,CSSDeepSkyBlue, 61 CSSDodgerBlue,CSSCornflowerBlue,CSSRoyalBlue,CSSBlue, 62 CSSMediumBlue,CSSDarkBlue,CSSNavy,CSSMidnightBlue: TBGRAPixel; 132 63 //Brown colors 133 CSSCornsilk: TBGRAPixel = (blue: 220; green: 248; red: 255; alpha: 255); 134 CSSBlanchedAlmond: TBGRAPixel = (blue: 205; green: 235; red: 255; alpha: 255); 135 CSSBisque: TBGRAPixel = (blue: 196; green: 228; red: 255; alpha: 255); 136 CSSNavajoWhite: TBGRAPixel = (blue: 173; green: 222; red: 255; alpha: 255); 137 CSSWheat: TBGRAPixel = (blue: 179; green: 222; red: 245; alpha: 255); 138 CSSBurlyWood: TBGRAPixel = (blue: 135; green: 184; red: 222; alpha: 255); 139 CSSTan: TBGRAPixel = (blue: 140; green: 180; red: 210; alpha: 255); 140 CSSRosyBrown: TBGRAPixel = (blue: 143; green: 143; red: 188; alpha: 255); 141 CSSSandyBrown: TBGRAPixel = (blue: 96; green: 164; red: 244; alpha: 255); 142 CSSGoldenrod: TBGRAPixel = (blue: 32; green: 165; red: 218; alpha: 255); 143 CSSDarkGoldenrod: TBGRAPixel = (blue: 11; green: 134; red: 184; alpha: 255); 144 CSSPeru: TBGRAPixel = (blue: 63; green: 133; red: 205; alpha: 255); 145 CSSChocolate: TBGRAPixel = (blue: 30; green: 105; red: 210; alpha: 255); 146 CSSSaddleBrown: TBGRAPixel = (blue: 19; green: 69; red: 139; alpha: 255); 147 CSSSienna: TBGRAPixel = (blue: 45; green: 82; red: 160; alpha: 255); 148 CSSBrown: TBGRAPixel = (blue: 42; green: 42; red: 165; alpha: 255); 149 CSSMaroon: TBGRAPixel = (blue: 0; green: 0; red: 128; alpha: 255); 150 64 CSSCornsilk, CSSBlanchedAlmond, CSSBisque, CSSNavajoWhite, 65 CSSWheat, CSSBurlyWood, CSSTan, CSSRosyBrown, 66 CSSSandyBrown, CSSGoldenrod, CSSDarkGoldenrod, CSSPeru, 67 CSSChocolate, CSSSaddleBrown, CSSSienna, CSSBrown, 68 CSSMaroon: TBGRAPixel; 151 69 //White colors 152 CSSWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255); 153 CSSSnow: TBGRAPixel = (blue: 250; green: 250; red: 255; alpha: 255); 154 CSSHoneydew: TBGRAPixel = (blue: 240; green: 255; red: 250; alpha: 255); 155 CSSMintCream: TBGRAPixel = (blue: 250; green: 255; red: 245; alpha: 255); 156 CSSAzure: TBGRAPixel = (blue: 255; green: 255; red: 240; alpha: 255); 157 CSSAliceBlue: TBGRAPixel = (blue: 255; green: 248; red: 240; alpha: 255); 158 CSSGhostWhite: TBGRAPixel = (blue: 255; green: 248; red: 248; alpha: 255); 159 CSSWhiteSmoke: TBGRAPixel = (blue: 245; green: 245; red: 245; alpha: 255); 160 CSSSeashell: TBGRAPixel = (blue: 255; green: 245; red: 238; alpha: 255); 161 CSSBeige: TBGRAPixel = (blue: 220; green: 245; red: 245; alpha: 255); 162 CSSOldLace: TBGRAPixel = (blue: 230; green: 245; red: 253; alpha: 255); 163 CSSFloralWhite: TBGRAPixel = (blue: 240; green: 250; red: 255; alpha: 255); 164 CSSIvory: TBGRAPixel = (blue: 240; green: 255; red: 255; alpha: 255); 165 CSSAntiqueWhite: TBGRAPixel = (blue: 215; green: 235; red: 250; alpha: 255); 166 CSSLinen: TBGRAPixel = (blue: 230; green: 240; red: 250; alpha: 255); 167 CSSLavenderBlush: TBGRAPixel = (blue: 245; green: 240; red: 255; alpha: 255); 168 CSSMistyRose: TBGRAPixel = (blue: 255; green: 228; red: 255; alpha: 255); 169 70 CSSWhite, CSSSnow, CSSHoneydew, CSSMintCream, 71 CSSAzure, CSSAliceBlue, CSSGhostWhite, CSSWhiteSmoke, 72 CSSSeashell, CSSBeige, CSSOldLace, CSSFloralWhite, 73 CSSIvory, CSSAntiqueWhite, CSSLinen, CSSLavenderBlush, 74 CSSMistyRose: TBGRAPixel; 170 75 //Gray colors 171 CSSGainsboro: TBGRAPixel = (blue: 220; green: 220; red: 220; alpha: 255); 172 CSSLightGray: TBGRAPixel = (blue: 211; green: 211; red: 211; alpha: 255); 173 CSSSilver: TBGRAPixel = (blue: 192; green: 192; red: 192; alpha: 255); 174 CSSDarkGray: TBGRAPixel = (blue: 169; green: 169; red: 169; alpha: 255); 175 CSSGray: TBGRAPixel = (blue: 128; green: 128; red: 128; alpha: 255); 176 CSSDimGray: TBGRAPixel = (blue: 105; green: 105; red: 105; alpha: 255); 177 CSSLightSlateGray: TBGRAPixel = (blue: 153; green: 136; red: 119; alpha: 255); 178 CSSSlateGray: TBGRAPixel = (blue: 144; green: 128; red: 112; alpha: 255); 179 CSSDarkSlateGray: TBGRAPixel = (blue: 79; green: 79; red: 47; alpha: 255); 180 CSSBlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255); 76 CSSGainsboro, CSSLightGray, CSSSilver, CSSDarkGray, 77 CSSGray, CSSDimGray, CSSLightSlateGray, CSSSlateGray, 78 CSSDarkSlateGray, CSSBlack: TBGRAPixel; 79 80 type 81 TBGRAColorDefinition = record 82 Name: string; 83 Color: TBGRAPixel; 84 end; 85 86 { TBGRAColorList } 87 {* Contains a fixed list of colors } 88 TBGRAColorList = class 89 protected 90 FFinished: boolean; 91 FNbColors: integer; 92 FColors: array of TBGRAColorDefinition; 93 function GetByIndex(Index: integer): TBGRAPixel; 94 function GetByName(Name: string): TBGRAPixel; 95 function GetName(Index: integer): string; 96 procedure Add(Name: string; out Color: TBGRAPixel; red,green,blue: byte); overload; 97 public 98 {** Creates an empty color list } 99 constructor Create; 100 {** Add a color to the list } 101 procedure Add(Name: string; const Color: TBGRAPixel); 102 {** Ends the color list and prevents further modifications } 103 procedure Finished; 104 {** Returns the index of a color with a given name } 105 function IndexOf(Name: string): integer; 106 {** Returns the index of a color. Colors are considered to match if 107 the difference is less than or equal to ''AMaxDiff'' } 108 function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer; 109 110 {** Gets the color associated with a color name } 111 property ByName[Name: string]: TBGRAPixel read GetByName; 112 {** Gets the color at the specified index } 113 property ByIndex[Index: integer]: TBGRAPixel read GetByIndex; default; 114 {** Gets the name of the color at the specified index } 115 property Name[Index: integer]: string read GetName; 116 {** Gets the number of colors } 117 property Count: integer read FNbColors; 118 end; 119 120 var 121 {* List of VGA colors: 122 * [#000000] Black, [#808080] Gray, [#C0C0C0] Silver, [#FFFFFF] White, 123 * [#800000] Maroon, [#FF0000] Red, [#800080] Purple, [#FF00FF] Fuchsia, 124 * [#008000] Green, [#00FF00] Lime, [#808000] Olive, [#FFFF00] Yellow, 125 * [#000080] Navy, [#0000FF] Blue, [#008080] Teal, [#00FFFF] Aqua. 126 * Shortcut constants are provided: [#000000] ''VGABlack'', [#808080] ''VGAGray''... } 127 VGAColors: TBGRAColorList; 128 {* List of [http://www.w3schools.com/cssref/css_colornames.asp web colors]. 129 Shortcut constants are provided: [#000000] ''CSSBlack'', [#FF0000] ''CSSRed''... } 130 CSSColors: TBGRAColorList; 131 132 {------------------- string conversion ------------------------} 133 134 {* Converts a ''TBGRAPixel'' value into a string, using color names provided in ''AColorList'', and 135 considering that a color matches in the color list if its difference is within ''AMaxDiff'' } 136 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string; 137 {* Converts a fully defined string into a ''TBGRAPixel'' value. Color names from ''VGAColors'' and ''CSSColors'' 138 are used if there is an exact match } 139 function StrToBGRA(str: string): TBGRAPixel; 140 {* Converts a string into a ''TBGRAPixel'' value. If the value is not fully defined or that 141 there is an error, ''DefaultColor'' is returned. 142 Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. } 143 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; 144 {* Converts a string into a ''TBGRAPixel'' value. If the value is not fully defined, missing channels (expressed with '?') 145 are filled with fallbackValues. You can check if there was an error with the provided boolean. 146 Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. } 147 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel; 148 {* Converts a string into a ''TBGRAPixel'' value into ''parsedValue''. ''parsedValue'' is not changed if 149 some channels are missing (expressed with '?'). You can check if there was an error with the provided boolean. 150 Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. } 151 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean); 181 152 {$ENDIF} 182 {$IFDEF INCLUDE_COLOR_LIST} 183 {$UNDEF INCLUDE_COLOR_LIST} 153 154 {$IFDEF INCLUDE_IMPLEMENTATION} 155 {$UNDEF INCLUDE_IMPLEMENTATION} 156 { TBGRAColorList } 157 158 function TBGRAColorList.GetByIndex(Index: integer): TBGRAPixel; 159 begin 160 if (Index < 0) or (Index >= FNbColors) then 161 result := BGRAPixelTransparent 162 else 163 result := FColors[Index].Color; 164 end; 165 166 function TBGRAColorList.GetByName(Name: string): TBGRAPixel; 167 var i: integer; 168 begin 169 i := IndexOf(Name); 170 if i = -1 then 171 result := BGRAPixelTransparent 172 else 173 result := FColors[i].Color; 174 end; 175 176 function TBGRAColorList.GetName(Index: integer): string; 177 begin 178 if (Index < 0) or (Index >= FNbColors) then 179 result := '' 180 else 181 result := FColors[Index].Name; 182 end; 183 184 procedure TBGRAColorList.Add(Name: string; out Color: TBGRAPixel; red, green, 185 blue: byte); 186 begin 187 Color := BGRA(red,green,blue); 188 Add(Name,Color); 189 end; 190 191 constructor TBGRAColorList.Create; 192 begin 193 FNbColors:= 0; 194 FColors := nil; 195 FFinished:= false; 196 end; 197 198 procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel); 199 begin 200 if FFinished then 201 raise Exception.Create('This list is already finished'); 202 if length(FColors) = FNbColors then 203 SetLength(FColors, FNbColors*2+1); 204 FColors[FNbColors].Name := Name; 205 FColors[FNbColors].Color := Color; 206 inc(FNbColors); 207 end; 208 209 procedure TBGRAColorList.Finished; 210 begin 211 if FFinished then exit; 212 FFinished := true; 213 SetLength(FColors, FNbColors); 214 end; 215 216 function TBGRAColorList.IndexOf(Name: string): integer; 217 var i: integer; 218 begin 219 for i := 0 to FNbColors-1 do 220 if CompareText(Name, FColors[i].Name) = 0 then 221 begin 222 result := i; 223 exit; 224 end; 225 result := -1; 226 end; 227 228 function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer; 229 var i: integer; 230 MinDiff,CurDiff: Word; 231 begin 232 if AMaxDiff = 0 then 233 begin 234 for i := 0 to FNbColors-1 do 235 if AColor = FColors[i].Color then 236 begin 237 result := i; 238 exit; 239 end; 240 result := -1; 241 end else 242 begin 243 MinDiff := AMaxDiff; 244 result := -1; 245 for i := 0 to FNbColors-1 do 246 begin 247 CurDiff := BGRAWordDiff(AColor,FColors[i].Color); 248 if CurDiff <= MinDiff then 249 begin 250 result := i; 251 MinDiff := CurDiff; 252 if MinDiff = 0 then exit; 253 end; 254 end; 255 end; 256 end; 257 258 {------------------- string conversion ---------------------------------} 259 260 { Write a color in hexadecimal format RRGGBBAA or using the name in a color list } 261 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string; 262 var idx: integer; 263 begin 264 if Assigned(AColorList) then 265 begin 266 idx := AColorList.IndexOfColor(c, AMaxDiff); 267 if idx<> -1 then 268 begin 269 result := AColorList.Name[idx]; 270 exit; 271 end; 272 end; 273 result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2); 274 end; 275 276 type 277 arrayOfString = array of string; 278 279 function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString; 280 var idxOpen,start,cur: integer; 281 begin 282 result := nil; 283 idxOpen := pos('(',str); 284 if idxOpen = 0 then 285 begin 286 start := 1; 287 //find first space 288 while (start <= length(str)) and (str[start]<>' ') do inc(start); 289 end else 290 start := idxOpen+1; 291 cur := start; 292 while cur <= length(str) do 293 begin 294 if str[cur] in[',',')'] then 295 begin 296 setlength(result,length(result)+1); 297 result[high(result)] := trim(copy(str,start,cur-start)); 298 start := cur+1; 299 if str[cur] = ')' then exit; 300 end; 301 inc(cur); 302 end; 303 if idxOpen <> 0 then flagError := true; //should exit on ')' 304 if start <= length(str) then 305 begin 306 setlength(result,length(result)+1); 307 result[high(result)] := copy(str,start,length(str)-start+1); 308 end; 309 end; 310 311 function ParseColorValue(str: string; var flagError: boolean): byte; 312 var pourcent,unclipped,{%H-}errPos: integer; 313 begin 314 if str = '' then result := 0 else 315 begin 316 if str[length(str)]='%' then 317 begin 318 val(copy(str,1,length(str)-1),pourcent,errPos); 319 if errPos <> 0 then flagError := true; 320 if pourcent < 0 then result := 0 else 321 if pourcent > 100 then result := 255 else 322 result := pourcent*255 div 100; 323 end else 324 begin 325 val(str,unclipped,errPos); 326 if errPos <> 0 then flagError := true; 327 if unclipped < 0 then result := 0 else 328 if unclipped > 255 then result := 255 else 329 result := unclipped; 330 end; 331 end; 332 end; 333 334 //this function returns the parsed value only if it contains no error nor missing values, otherwise 335 //it returns BGRAPixelTransparent 336 function StrToBGRA(str: string): TBGRAPixel; 337 var missingValues, error: boolean; 338 begin 339 result := BGRABlack; 340 TryStrToBGRA(str, result, missingValues, error); 341 if missingValues or error then result := BGRAPixelTransparent; 342 end; 343 344 //this function changes the content of parsedValue depending on available and parsable information. 345 //set parsedValue to the fallback values before calling this function. 346 //missing values are expressed by empty string or by '?', for example 'rgb(255,?,?,?)' will change only the red value. 347 //note that if alpha is not expressed by the string format, it will be opaque. So 'rgb(255,?,?)' will change the red value and the alpha value. 348 //the last parameter of rgba() is a floating point number where 1 is opaque and 0 is transparent. 349 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean); 350 var errPos: integer; 351 values: array of string; 352 alphaF: single; 353 idx: integer; 354 begin 355 str := Trim(str); 356 error := false; 357 if (str = '') or (str = '?') then 358 begin 359 missingValues := true; 360 exit; 361 end else 362 missingValues := false; 363 str := StringReplace(lowerCase(str),'grey','gray',[]); 364 365 //VGA color names 366 idx := VGAColors.IndexOf(str); 367 if idx <> -1 then 368 begin 369 parsedValue := VGAColors[idx]; 370 exit; 371 end; 372 if str='transparent' then parsedValue := BGRAPixelTransparent else 373 begin 374 //check CSS color 375 idx := CSSColors.IndexOf(str); 376 if idx <> -1 then 377 begin 378 parsedValue := CSSColors[idx]; 379 exit; 380 end; 381 382 //CSS RGB notation 383 if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') or 384 (copy(str,1,4)='rgb ') or (copy(str,1,5)='rgba ') then 385 begin 386 values := SimpleParseFuncParam(str,error); 387 if (length(values)=3) or (length(values)=4) then 388 begin 389 if (values[0] <> '') and (values[0] <> '?') then 390 parsedValue.red := ParseColorValue(values[0], error) 391 else 392 missingValues := true; 393 if (values[1] <> '') and (values[1] <> '?') then 394 parsedValue.green := ParseColorValue(values[1], error) 395 else 396 missingValues := true; 397 if (values[2] <> '') and (values[2] <> '?') then 398 parsedValue.blue := ParseColorValue(values[2], error) 399 else 400 missingValues := true; 401 if length(values)=4 then 402 begin 403 if (values[3] <> '') and (values[3] <> '?') then 404 begin 405 val(values[3],alphaF,errPos); 406 if errPos <> 0 then 407 begin 408 parsedValue.alpha := 255; 409 error := true; 410 end 411 else 412 begin 413 if alphaF < 0 then 414 parsedValue.alpha := 0 else 415 if alphaF > 1 then 416 parsedValue.alpha := 255 417 else 418 parsedValue.alpha := round(alphaF*255); 419 end; 420 end else 421 missingValues := true; 422 end else 423 parsedValue.alpha := 255; 424 end else 425 error := true; 426 exit; 427 end; 428 429 //remove HTML notation header 430 if str[1]='#' then delete(str,1,1); 431 432 //add alpha if missing (if you want an undefined alpha use '??' or '?') 433 if length(str)=6 then str += 'FF'; 434 if length(str)=3 then str += 'F'; 435 436 //hex notation 437 if length(str)=8 then 438 begin 439 if copy(str,1,2) <> '??' then 440 begin 441 val('$'+copy(str,1,2),parsedValue.red,errPos); 442 if errPos <> 0 then error := true; 443 end else missingValues := true; 444 if copy(str,3,2) <> '??' then 445 begin 446 val('$'+copy(str,3,2),parsedValue.green,errPos); 447 if errPos <> 0 then error := true; 448 end else missingValues := true; 449 if copy(str,5,2) <> '??' then 450 begin 451 val('$'+copy(str,5,2),parsedValue.blue,errPos); 452 if errPos <> 0 then error := true; 453 end else missingValues := true; 454 if copy(str,7,2) <> '??' then 455 begin 456 val('$'+copy(str,7,2),parsedValue.alpha,errPos); 457 if errPos <> 0 then 458 begin 459 error := true; 460 parsedValue.alpha := 255; 461 end; 462 end else missingValues := true; 463 end else 464 if length(str)=4 then 465 begin 466 if str[1] <> '?' then 467 begin 468 val('$'+str[1],parsedValue.red,errPos); 469 if errPos <> 0 then error := true; 470 parsedValue.red *= $11; 471 end else missingValues := true; 472 if str[2] <> '?' then 473 begin 474 val('$'+str[2],parsedValue.green,errPos); 475 if errPos <> 0 then error := true; 476 parsedValue.green *= $11; 477 end else missingValues := true; 478 if str[3] <> '?' then 479 begin 480 val('$'+str[3],parsedValue.blue,errPos); 481 if errPos <> 0 then error := true; 482 parsedValue.blue *= $11; 483 end else missingValues := true; 484 if str[4] <> '?' then 485 begin 486 val('$'+str[4],parsedValue.alpha,errPos); 487 if errPos <> 0 then 488 begin 489 error := true; 490 parsedValue.alpha := 255; 491 end else 492 parsedValue.alpha *= $11; 493 end else missingValues := true; 494 end else 495 error := true; //string format not recognised 496 end; 497 498 end; 499 500 //this function returns the values that can be read from the string, otherwise 501 //it fills the gaps with the fallback values. The error boolean is True only 502 //if there was invalid values, it is not set to True if there was missing values. 503 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out 504 error: boolean): TBGRAPixel; 505 var missingValues: boolean; 506 begin 507 result := fallbackValues; 508 TryStrToBGRA(str, result, missingValues, error); 509 end; 510 511 { Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. } 512 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; 513 var missingValues, error: boolean; 514 begin 515 result := BGRABlack; 516 TryStrToBGRA(str, result, missingValues, error); 517 if missingValues or error then result := DefaultColor; 518 end; 519 520 function BlueGreenRedToBGRA(blue,green,red: byte): TBGRAPixel; 521 begin 522 result := BGRA(red,green,blue); 523 end; 524 525 {$ENDIF} 526 527 {$IFDEF INCLUDE_INIT} 528 {$UNDEF INCLUDE_INIT} 529 BGRAPixelTransparent := BGRA(0,0,0,0); 530 BGRAWhite := BGRA(255,255,255); 531 BGRABlack := BGRA(0,0,0); 532 184 533 VGAColors := TBGRAColorList.Create; 185 VGAColors.Add('Black',VGABlack );186 VGAColors.Add('Gray',VGAGray );187 VGAColors.Add('Silver',VGASilver );188 VGAColors.Add('White',VGAWhite );189 VGAColors.Add('Maroon',VGAMaroon );190 VGAColors.Add('Red',VGARed );191 VGAColors.Add('Purple',VGAPurple );192 VGAColors.Add('Fuchsia',VGAFuchsia );193 VGAColors.Add('Green',VGAGreen );194 VGAColors.Add('Lime',VGALime );195 VGAColors.Add('Olive',VGAOlive );196 VGAColors.Add('Yellow',VGAYellow );197 VGAColors.Add('Navy',VGANavy );198 VGAColors.Add('Blue',VGABlue );199 VGAColors.Add('Teal',VGATeal );200 VGAColors.Add('Aqua',VGAAqua );534 VGAColors.Add('Black',VGABlack,0,0,0); 535 VGAColors.Add('Gray',VGAGray,128,128,128); 536 VGAColors.Add('Silver',VGASilver,192,192,192); 537 VGAColors.Add('White',VGAWhite,255,255,255); 538 VGAColors.Add('Maroon',VGAMaroon,128,0,0); 539 VGAColors.Add('Red',VGARed,255,0,0); 540 VGAColors.Add('Purple',VGAPurple,128,0,128); 541 VGAColors.Add('Fuchsia',VGAFuchsia,255,0,255); 542 VGAColors.Add('Green',VGAGreen,0,128,0); 543 VGAColors.Add('Lime',VGALime,0,255,0); 544 VGAColors.Add('Olive',VGAOlive,128,128,0); 545 VGAColors.Add('Yellow',VGAYellow,255,255,0); 546 VGAColors.Add('Navy',VGANavy,0,0,128); 547 VGAColors.Add('Blue',VGABlue,0,0,255); 548 VGAColors.Add('Teal',VGATeal,0,128,128); 549 VGAColors.Add('Aqua',VGAAqua,0,255,255); 201 550 VGAColors.Finished; 551 552 //Red colors 553 CSSIndianRed:= BlueGreenRedToBGRA(92, 92, 205); 554 CSSLightCoral:= BlueGreenRedToBGRA(128, 128, 240); 555 CSSSalmon:= BlueGreenRedToBGRA(114, 128, 250); 556 CSSDarkSalmon:= BlueGreenRedToBGRA(122, 150, 233); 557 CSSRed:= BlueGreenRedToBGRA(0, 0, 255); 558 CSSCrimson:= BlueGreenRedToBGRA(60, 20, 220); 559 CSSFireBrick:= BlueGreenRedToBGRA(34, 34, 178); 560 CSSDarkRed:= BlueGreenRedToBGRA(0, 0, 139); 561 562 //Pink colors 563 CSSPink:= BlueGreenRedToBGRA(203, 192, 255); 564 CSSLightPink:= BlueGreenRedToBGRA(193, 182, 255); 565 CSSHotPink:= BlueGreenRedToBGRA(180, 105, 255); 566 CSSDeepPink:= BlueGreenRedToBGRA(147, 20, 255); 567 CSSMediumVioletRed:= BlueGreenRedToBGRA(133, 21, 199); 568 CSSPaleVioletRed:= BlueGreenRedToBGRA(147, 112, 219); 569 570 //Orange colors 571 CSSLightSalmon:= BlueGreenRedToBGRA(122, 160, 255); 572 CSSCoral:= BlueGreenRedToBGRA(80, 127, 255); 573 CSSTomato:= BlueGreenRedToBGRA(71, 99, 255); 574 CSSOrangeRed:= BlueGreenRedToBGRA(0, 69, 255); 575 CSSDarkOrange:= BlueGreenRedToBGRA(0, 140, 255); 576 CSSOrange:= BlueGreenRedToBGRA(0, 165, 255); 577 578 //Yellow colors 579 CSSGold:= BlueGreenRedToBGRA(0, 215, 255); 580 CSSYellow:= BlueGreenRedToBGRA(0, 255, 255); 581 CSSLightYellow:= BlueGreenRedToBGRA(224, 255, 255); 582 CSSLemonChiffon:= BlueGreenRedToBGRA(205, 250, 255); 583 CSSLightGoldenrodYellow:= BlueGreenRedToBGRA(210, 250, 250); 584 CSSPapayaWhip:= BlueGreenRedToBGRA(213, 239, 255); 585 CSSMoccasin:= BlueGreenRedToBGRA(181, 228, 255); 586 CSSPeachPuff:= BlueGreenRedToBGRA(185, 218, 255); 587 CSSPaleGoldenrod:= BlueGreenRedToBGRA(170, 232, 238); 588 CSSKhaki:= BlueGreenRedToBGRA(140, 230, 240); 589 CSSDarkKhaki:= BlueGreenRedToBGRA(107, 183, 189); 590 591 //Purple colors 592 CSSLavender:= BlueGreenRedToBGRA(250, 230, 230); 593 CSSThistle:= BlueGreenRedToBGRA(216, 191, 216); 594 CSSPlum:= BlueGreenRedToBGRA(221, 160, 221); 595 CSSViolet:= BlueGreenRedToBGRA(238, 130, 238); 596 CSSOrchid:= BlueGreenRedToBGRA(214, 112, 218); 597 CSSFuchsia:= BlueGreenRedToBGRA(255, 0, 255); 598 CSSMagenta:= BlueGreenRedToBGRA(255, 0, 255); 599 CSSMediumOrchid:= BlueGreenRedToBGRA(211, 85, 186); 600 CSSMediumPurple:= BlueGreenRedToBGRA(219, 112, 147); 601 CSSBlueViolet:= BlueGreenRedToBGRA(226, 43, 138); 602 CSSDarkViolet:= BlueGreenRedToBGRA(211, 0, 148); 603 CSSDarkOrchid:= BlueGreenRedToBGRA(204, 50, 153); 604 CSSDarkMagenta:= BlueGreenRedToBGRA(139, 0, 139); 605 CSSPurple:= BlueGreenRedToBGRA(128, 0, 128); 606 CSSIndigo:= BlueGreenRedToBGRA(130, 0, 75); 607 CSSDarkSlateBlue:= BlueGreenRedToBGRA(139, 61, 72); 608 CSSSlateBlue:= BlueGreenRedToBGRA(205, 90, 106); 609 CSSMediumSlateBlue:= BlueGreenRedToBGRA(238, 104, 123); 610 611 //Green colors 612 CSSGreenYellow:= BlueGreenRedToBGRA(47, 255, 173); 613 CSSChartreuse:= BlueGreenRedToBGRA(0, 255, 127); 614 CSSLawnGreen:= BlueGreenRedToBGRA(0, 252, 124); 615 CSSLime:= BlueGreenRedToBGRA(0, 255, 0); 616 CSSLimeGreen:= BlueGreenRedToBGRA(50, 205, 50); 617 CSSPaleGreen:= BlueGreenRedToBGRA(152, 251, 152); 618 CSSLightGreen:= BlueGreenRedToBGRA(144, 238, 144); 619 CSSMediumSpringGreen:= BlueGreenRedToBGRA(154, 250, 0); 620 CSSSpringGreen:= BlueGreenRedToBGRA(127, 255, 0); 621 CSSMediumSeaGreen:= BlueGreenRedToBGRA(113, 179, 60); 622 CSSSeaGreen:= BlueGreenRedToBGRA(87, 139, 46); 623 CSSForestGreen:= BlueGreenRedToBGRA(34, 139, 34); 624 CSSGreen:= BlueGreenRedToBGRA(0, 128, 0); 625 CSSDarkGreen:= BlueGreenRedToBGRA(0, 100, 0); 626 CSSYellowGreen:= BlueGreenRedToBGRA(50, 205, 154); 627 CSSOliveDrab:= BlueGreenRedToBGRA(35, 142, 107); 628 CSSOlive:= BlueGreenRedToBGRA(0, 128, 128); 629 CSSDarkOliveGreen:= BlueGreenRedToBGRA(47, 107, 85); 630 CSSMediumAquamarine:= BlueGreenRedToBGRA(170, 205, 102); 631 CSSDarkSeaGreen:= BlueGreenRedToBGRA(143, 188, 143); 632 CSSLightSeaGreen:= BlueGreenRedToBGRA(170, 178, 32); 633 CSSDarkCyan:= BlueGreenRedToBGRA(139, 139, 0); 634 CSSTeal:= BlueGreenRedToBGRA(128, 128, 0); 635 636 //Blue/Cyan colors 637 CSSAqua:= BlueGreenRedToBGRA(255, 255, 0); 638 CSSCyan:= BlueGreenRedToBGRA(255, 255, 0); 639 CSSLightCyan:= BlueGreenRedToBGRA(255, 255, 224); 640 CSSPaleTurquoise:= BlueGreenRedToBGRA(238, 238, 175); 641 CSSAquamarine:= BlueGreenRedToBGRA(212, 255, 127); 642 CSSTurquoise:= BlueGreenRedToBGRA(208, 224, 64); 643 CSSMediumTurquoise:= BlueGreenRedToBGRA(204, 209, 72); 644 CSSDarkTurquoise:= BlueGreenRedToBGRA(209, 206, 0); 645 CSSCadetBlue:= BlueGreenRedToBGRA(160, 158, 95); 646 CSSSteelBlue:= BlueGreenRedToBGRA(180, 130, 70); 647 CSSLightSteelBlue:= BlueGreenRedToBGRA(222, 196, 176); 648 CSSPowderBlue:= BlueGreenRedToBGRA(230, 224, 176); 649 CSSLightBlue:= BlueGreenRedToBGRA(230, 216, 173); 650 CSSSkyBlue:= BlueGreenRedToBGRA(235, 206, 135); 651 CSSLightSkyBlue:= BlueGreenRedToBGRA(250, 206, 135); 652 CSSDeepSkyBlue:= BlueGreenRedToBGRA(255, 191, 0); 653 CSSDodgerBlue:= BlueGreenRedToBGRA(255, 144, 30); 654 CSSCornflowerBlue:= BlueGreenRedToBGRA(237, 149, 100); 655 CSSRoyalBlue:= BlueGreenRedToBGRA(255, 105, 65); 656 CSSBlue:= BlueGreenRedToBGRA(255, 0, 0); 657 CSSMediumBlue:= BlueGreenRedToBGRA(205, 0, 0); 658 CSSDarkBlue:= BlueGreenRedToBGRA(139, 0, 0); 659 CSSNavy:= BlueGreenRedToBGRA(128, 0, 0); 660 CSSMidnightBlue:= BlueGreenRedToBGRA(112, 25, 25); 661 662 //Brown colors 663 CSSCornsilk:= BlueGreenRedToBGRA(220, 248, 255); 664 CSSBlanchedAlmond:= BlueGreenRedToBGRA(205, 235, 255); 665 CSSBisque:= BlueGreenRedToBGRA(196, 228, 255); 666 CSSNavajoWhite:= BlueGreenRedToBGRA(173, 222, 255); 667 CSSWheat:= BlueGreenRedToBGRA(179, 222, 245); 668 CSSBurlyWood:= BlueGreenRedToBGRA(135, 184, 222); 669 CSSTan:= BlueGreenRedToBGRA(140, 180, 210); 670 CSSRosyBrown:= BlueGreenRedToBGRA(143, 143, 188); 671 CSSSandyBrown:= BlueGreenRedToBGRA(96, 164, 244); 672 CSSGoldenrod:= BlueGreenRedToBGRA(32, 165, 218); 673 CSSDarkGoldenrod:= BlueGreenRedToBGRA(11, 134, 184); 674 CSSPeru:= BlueGreenRedToBGRA(63, 133, 205); 675 CSSChocolate:= BlueGreenRedToBGRA(30, 105, 210); 676 CSSSaddleBrown:= BlueGreenRedToBGRA(19, 69, 139); 677 CSSSienna:= BlueGreenRedToBGRA(45, 82, 160); 678 CSSBrown:= BlueGreenRedToBGRA(42, 42, 165); 679 CSSMaroon:= BlueGreenRedToBGRA(0, 0, 128); 680 681 //White colors 682 CSSWhite:= BlueGreenRedToBGRA(255, 255, 255); 683 CSSSnow:= BlueGreenRedToBGRA(250, 250, 255); 684 CSSHoneydew:= BlueGreenRedToBGRA(240, 255, 250); 685 CSSMintCream:= BlueGreenRedToBGRA(250, 255, 245); 686 CSSAzure:= BlueGreenRedToBGRA(255, 255, 240); 687 CSSAliceBlue:= BlueGreenRedToBGRA(255, 248, 240); 688 CSSGhostWhite:= BlueGreenRedToBGRA(255, 248, 248); 689 CSSWhiteSmoke:= BlueGreenRedToBGRA(245, 245, 245); 690 CSSSeashell:= BlueGreenRedToBGRA(255, 245, 238); 691 CSSBeige:= BlueGreenRedToBGRA(220, 245, 245); 692 CSSOldLace:= BlueGreenRedToBGRA(230, 245, 253); 693 CSSFloralWhite:= BlueGreenRedToBGRA(240, 250, 255); 694 CSSIvory:= BlueGreenRedToBGRA(240, 255, 255); 695 CSSAntiqueWhite:= BlueGreenRedToBGRA(215, 235, 250); 696 CSSLinen:= BlueGreenRedToBGRA(230, 240, 250); 697 CSSLavenderBlush:= BlueGreenRedToBGRA(245, 240, 255); 698 CSSMistyRose:= BlueGreenRedToBGRA(255, 228, 255); 699 700 //Gray colors 701 CSSGainsboro:= BlueGreenRedToBGRA(220, 220, 220); 702 CSSLightGray:= BlueGreenRedToBGRA(211, 211, 211); 703 CSSSilver:= BlueGreenRedToBGRA(192, 192, 192); 704 CSSDarkGray:= BlueGreenRedToBGRA(169, 169, 169); 705 CSSGray:= BlueGreenRedToBGRA(128, 128, 128); 706 CSSDimGray:= BlueGreenRedToBGRA(105, 105, 105); 707 CSSLightSlateGray:= BlueGreenRedToBGRA(153, 136, 119); 708 CSSSlateGray:= BlueGreenRedToBGRA(144, 128, 112); 709 CSSDarkSlateGray:= BlueGreenRedToBGRA(79, 79, 47); 710 CSSBlack:= BlueGreenRedToBGRA(0, 0, 0); 202 711 203 712 CSSColors := TBGRAColorList.Create; … … 345 854 {$ENDIF} 346 855 856 {$IFDEF INCLUDE_FINAL} 857 {$UNDEF INCLUDE_FINAL} 858 CSSColors.Free; 859 VGAColors.Free; 860 {$ENDIF} -
GraphicTest/Packages/bgrabitmap/face3d.inc
r472 r494 8 8 ColorOverride: boolean; 9 9 TexCoordOverride: boolean; 10 ActualColor: TBGRAPixel; 11 ActualTexCoord: TPointF; 10 12 end; 11 13 … … 16 18 FVertices: packed array of TBGRAFaceVertexDescription; 17 19 FVertexCount: integer; 18 FTexture : IBGRAScanner;20 FTexture, FActualTexture: IBGRAScanner; 19 21 FMaterial: IBGRAMaterial3D; 22 FActualMaterial: TBGRAMaterial3D; 20 23 FMaterialName: string; 21 24 FParentTexture: boolean; … … 30 33 function GetVertexDescription(AIndex : integer): PBGRAFaceVertexDescription; 31 34 procedure SetCustomFlags(AValue: DWord); 35 procedure ComputeActualVertexColor(AIndex: integer); 36 procedure ComputeActualTexCoord(AMinIndex, AMaxIndex: integer); 37 procedure UpdateTexture; 32 38 public 33 39 function GetObject3D: IBGRAObject3D; 34 40 constructor Create(AObject3D: IBGRAObject3D; AVertices: array of IBGRAVertex3D); 35 41 destructor Destroy; override; 42 procedure ComputeVertexColors; 43 procedure UpdateMaterial; 44 procedure FlipFace; 36 45 function AddVertex(AVertex: IBGRAVertex3D): integer; 37 46 function GetParentTexture: boolean; … … 89 98 property LightThroughFactorOverride: boolean read GetLightThroughFactorOverride write SetLightThroughFactorOverride; 90 99 property Material: IBGRAMaterial3D read GetMaterial write SetMaterial; 100 property ActualMaterial: TBGRAMaterial3D read FActualMaterial; 101 property ActualTexture: IBGRAScanner read FActualTexture; 91 102 property VertexDescription[AIndex : integer]: PBGRAFaceVertexDescription read GetVertexDescription; 92 103 property CustomFlags: DWord read GetCustomFlags write SetCustomFlags; … … 114 125 begin 115 126 FCustomFlags:= AValue; 127 end; 128 129 procedure TBGRAFace3D.ComputeActualVertexColor(AIndex: integer); 130 begin 131 with FVertices[AIndex] do 132 begin 133 if ColorOverride then 134 ActualColor := Color 135 else 136 if Vertex.ParentColor then 137 ActualColor := FObject3D.Color 138 else 139 ActualColor := Vertex.Color; 140 end; 141 end; 142 143 procedure TBGRAFace3D.ComputeActualTexCoord(AMinIndex, AMaxIndex: integer); 144 var 145 i: Integer; 146 zoom: TPointF; 147 m: IBGRAMaterial3D; 148 begin 149 m := ActualMaterial; 150 if m <> nil then zoom := m.TextureZoom 151 else zoom := PointF(1,1); 152 for i := AMinIndex to AMaxIndex do 153 with FVertices[i] do 154 begin 155 if TexCoordOverride then 156 ActualTexCoord := TexCoord 157 else 158 ActualTexCoord := Vertex.TexCoord; 159 ActualTexCoord.x *= zoom.x; 160 ActualTexCoord.y *= zoom.y; 161 end; 162 end; 163 164 procedure TBGRAFace3D.UpdateTexture; 165 begin 166 if FParentTexture then 167 begin 168 FActualTexture := nil; 169 if FActualMaterial <> nil then 170 FActualTexture := FActualMaterial.GetTexture; 171 if FActualTexture = nil then 172 FActualTexture := FObject3D.Texture 173 end 174 else 175 FActualTexture := FTexture; 116 176 end; 117 177 … … 131 191 i: Integer; 132 192 begin 133 SetLength(FVertices, length(AVertices));134 for i:= 0 to high(AVertices) do135 AddVertex(AVertices[i]);136 193 FObject3D := AObject3D; 137 194 FBiface := false; … … 139 196 FLightThroughFactor:= 0; 140 197 FLightThroughFactorOverride:= false; 198 199 UpdateMaterial; 200 201 SetLength(FVertices, length(AVertices)); 202 for i:= 0 to high(AVertices) do 203 AddVertex(AVertices[i]); 141 204 end; 142 205 143 206 destructor TBGRAFace3D.Destroy; 144 207 begin 208 FMaterial := nil; 145 209 fillchar(FTexture,sizeof(FTexture),0); 210 fillchar(FActualTexture,sizeof(FActualTexture),0); 146 211 inherited Destroy; 212 end; 213 214 procedure TBGRAFace3D.ComputeVertexColors; 215 var 216 i: Integer; 217 begin 218 for i := 0 to FVertexCount-1 do 219 ComputeActualVertexColor(i); 220 end; 221 222 procedure TBGRAFace3D.UpdateMaterial; 223 begin 224 if Material <> nil then 225 FActualMaterial := TBGRAMaterial3D(Material.GetAsObject) 226 else if FObject3D.Material <> nil then 227 FActualMaterial := TBGRAMaterial3D(FObject3D.Material.GetAsObject) 228 else if TBGRAScene3D(FObject3D.Scene).DefaultMaterial <> nil then 229 FActualMaterial := TBGRAMaterial3D(TBGRAScene3D(FObject3D.Scene).DefaultMaterial.GetAsObject); 230 231 UpdateTexture; 232 233 ComputeActualTexCoord(0,FVertexCount-1); 234 end; 235 236 procedure TBGRAFace3D.FlipFace; 237 var i: integer; 238 temp: TBGRAFaceVertexDescription; 239 begin 240 for i := 0 to (VertexCount div 2)-1 do 241 begin 242 temp := FVertices[i]; 243 FVertices[i] := FVertices[VertexCount-1-i]; 244 FVertices[VertexCount-1-i] := temp; 245 end; 147 246 end; 148 247 … … 161 260 Normal := nil; 162 261 end; 262 ComputeActualVertexColor(result); 263 ComputeActualTexCoord(result,result); 163 264 inc(FVertexCount); 164 265 end; … … 186 287 raise Exception.Create('Index out of bounds'); 187 288 FVertices[AIndex].Vertex := AValue; 289 ComputeActualVertexColor(AIndex); 188 290 end; 189 291 … … 192 294 if (AIndex < 0) or (AIndex >= FVertexCount) then 193 295 raise Exception.Create('Index out of bounds'); 194 result := FVertices[AIndex]. Color;296 result := FVertices[AIndex].ActualColor; 195 297 end; 196 298 … … 220 322 begin 221 323 FParentTexture := AValue; 324 UpdateTexture; 222 325 end; 223 326 … … 226 329 FTexture := AValue; 227 330 FParentTexture := false; 331 UpdateTexture; 228 332 end; 229 333 … … 245 349 ColorOverride := true; 246 350 end; 351 ComputeActualVertexColor(AIndex); 247 352 end; 248 353 … … 253 358 raise Exception.Create('Index out of bounds'); 254 359 FVertices[AIndex].ColorOverride := AValue; 360 ComputeActualVertexColor(AIndex); 255 361 end; 256 362 … … 275 381 FVertices[AIndex].TexCoord := AValue; 276 382 FVertices[AIndex].TexCoordOverride := true; 383 ComputeActualTexCoord(AIndex, AIndex); 277 384 end; 278 385 … … 374 481 procedure TBGRAFace3D.SetMaterial(const AValue: IBGRAMaterial3D); 375 482 begin 376 FMaterial := AValue; 483 if AValue <> FMaterial then 484 begin 485 FMaterial := AValue; 486 UpdateMaterial; 487 end; 377 488 end; 378 489 … … 382 493 begin 383 494 FMaterialName := AValue; 384 FObject3D.Scene.UseMaterial(FMaterialName, self);495 TBGRAScene3D(FObject3D.Scene).UseMaterial(FMaterialName, self); 385 496 end; 386 497 end; -
GraphicTest/Packages/bgrabitmap/lightingclasses3d.inc
r472 r494 1 1 type 2 3 { TBGRAMaterial3D }4 5 TBGRAMaterial3D = class(TInterfacedObject, IBGRAMaterial3D)6 private7 FName: string;8 FTexture: IBGRAScanner;9 FAutoSimpleColor,FAutoAmbiantColor,FAutoDiffuseColor,FAutoSpecularColor: boolean;10 FSimpleColorInt, FAmbiantColorInt, FDiffuseColorInt: TColorInt65536;11 FDiffuseLightness: integer;12 FTextureZoom: TPointF;13 14 FSpecularColorInt: TColorInt65536;15 FSpecularIndex: integer;16 FSpecularOn: boolean;17 18 FSaturationLowF: single;19 FSaturationHighF: single;20 FLightThroughFactor: single;21 22 //phong precalc23 FPowerTable: array of single;24 FPowerTableSize, FPowerTableExp2: integer;25 FPowerTableSizeF: single;26 27 procedure UpdateSpecular;28 procedure UpdateSimpleColor;29 procedure ComputePowerTable;30 public31 constructor Create;32 destructor Destroy; override;33 34 function GetAutoAmbiantColor: boolean;35 function GetAutoDiffuseColor: boolean;36 function GetAutoSpecularColor: boolean;37 function GetAutoSimpleColor: boolean;38 function GetAmbiantAlpha: byte;39 function GetAmbiantColor: TBGRAPixel;40 function GetAmbiantColorF: TColorF;41 function GetAmbiantColorInt: TColorInt65536;42 function GetDiffuseAlpha: byte;43 function GetDiffuseColor: TBGRAPixel;44 function GetDiffuseColorF: TColorF;45 function GetDiffuseColorInt: TColorInt65536;46 function GetLightThroughFactor: single;47 function GetSpecularColor: TBGRAPixel;48 function GetSpecularColorF: TColorF;49 function GetSpecularColorInt: TColorInt65536;50 function GetSpecularIndex: integer;51 function GetSaturationHigh: single;52 function GetSaturationLow: single;53 function GetSimpleAlpha: byte;54 function GetSimpleColor: TBGRAPixel;55 function GetSimpleColorF: TColorF;56 function GetSimpleColorInt: TColorInt65536;57 function GetTexture: IBGRAScanner;58 function GetTextureZoom: TPointF;59 procedure SetAutoAmbiantColor(const AValue: boolean);60 procedure SetAutoDiffuseColor(const AValue: boolean);61 procedure SetAutoSpecularColor(const AValue: boolean);62 procedure SetAmbiantAlpha(AValue: byte);63 procedure SetAmbiantColor(const AValue: TBGRAPixel);64 procedure SetAmbiantColorF(const AValue: TColorF);65 procedure SetAmbiantColorInt(const AValue: TColorInt65536);66 procedure SetDiffuseAlpha(AValue: byte);67 procedure SetDiffuseColor(const AValue: TBGRAPixel);68 procedure SetDiffuseColorF(const AValue: TColorF);69 procedure SetDiffuseColorInt(const AValue: TColorInt65536);70 procedure SetLightThroughFactor(const AValue: single);71 procedure SetSpecularColor(const AValue: TBGRAPixel);72 procedure SetSpecularColorF(const AValue: TColorF);73 procedure SetSpecularColorInt(const AValue: TColorInt65536);74 procedure SetSpecularIndex(const AValue: integer);75 procedure SetSaturationHigh(const AValue: single);76 procedure SetSaturationLow(const AValue: single);77 procedure SetSimpleAlpha(AValue: byte);78 procedure SetSimpleColor(AValue: TBGRAPixel);79 procedure SetSimpleColorF(AValue: TColorF);80 procedure SetSimpleColorInt(AValue: TColorInt65536);81 procedure SetTexture(AValue: IBGRAScanner);82 procedure SetTextureZoom(AValue: TPointF);83 function GetName: string;84 procedure SetName(const AValue: string);85 86 function GetSpecularOn: boolean;87 function GetAsObject: TObject;88 procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext; DiffuseIntensity, SpecularIntensity, SpecularCosine: single; const ALightColor: TColorInt65536);89 procedure ComputeDiffuseColor(Context: PSceneLightingContext; const DiffuseIntensity: single; const ALightColor: TColorInt65536);90 procedure ComputeDiffuseLightness(Context: PSceneLightingContext; DiffuseLightnessTerm32768: integer; ALightLightness: integer);91 92 end;93 94 { TBGRAMaterial3D }95 96 procedure TBGRAMaterial3D.UpdateSpecular;97 begin98 FAutoSpecularColor := (FSpecularColorInt.r = 65536) and (FSpecularColorInt.g = 65536) and (FSpecularColorInt.b = 65536) and (FSpecularColorInt.a = 65536);99 FSpecularOn := (FSpecularIndex > 0) and ((FSpecularColorInt.r <> 0) or (FSpecularColorInt.g <> 0) or (FSpecularColorInt.b <> 0) or100 FAutoSpecularColor);101 end;102 103 procedure TBGRAMaterial3D.UpdateSimpleColor;104 begin105 FSimpleColorInt := (FAmbiantColorInt+FDiffuseColorInt)*32768;106 FAutoSimpleColor := (FSimpleColorInt.r = 65536) and (FSimpleColorInt.g = 65536) and (FSimpleColorInt.b = 65536) and (FSimpleColorInt.a = 65536);107 end;108 109 procedure TBGRAMaterial3D.ComputePowerTable;110 var i: integer;111 Exponent: single;112 begin113 //exponent computed by squares114 Exponent := 1;115 FPowerTableExp2 := 0;116 While Exponent*FPowerTableSize/16 < FSpecularIndex do117 begin118 Exponent *= 2;119 Inc(FPowerTableExp2);120 end;121 122 //remaining exponent123 setlength(FPowerTable,FPowerTableSize+3);124 FPowerTable[0] := 0; //out of bound125 FPowerTable[1] := 0; //image of zero126 for i := 1 to FPowerTableSize do // ]0;1]127 FPowerTable[i+1] := Exp(ln(i/(FPowerTableSize-1))*FSpecularIndex/Exponent);128 FPowerTable[FPowerTableSize+2] := 1; //out of bound129 end;130 131 constructor TBGRAMaterial3D.Create;132 begin133 SetAmbiantColorInt(ColorInt65536(65536,65536,65536));134 SetDiffuseColorInt(ColorInt65536(65536,65536,65536));135 FSpecularIndex := 10;136 SetSpecularColorInt(ColorInt65536(0,0,0));137 FLightThroughFactor:= 0;138 SetSaturationLow(2);139 SetSaturationHigh(3);140 141 FTexture := nil;142 FTextureZoom := PointF(1,1);143 144 FPowerTableSize := 128;145 FPowerTableSizeF := FPowerTableSize;146 FPowerTable := nil;147 end;148 149 destructor TBGRAMaterial3D.Destroy;150 begin151 inherited Destroy;152 end;153 154 function TBGRAMaterial3D.GetAutoAmbiantColor: boolean;155 begin156 result := FAutoAmbiantColor;157 end;158 159 procedure TBGRAMaterial3D.SetDiffuseAlpha(AValue: byte);160 begin161 if AValue = 0 then162 FDiffuseColorInt.a := 0163 else164 FDiffuseColorInt.a := AValue*257+1;165 UpdateSimpleColor;166 end;167 168 function TBGRAMaterial3D.GetAutoDiffuseColor: boolean;169 begin170 result := FAutoDiffuseColor;171 end;172 173 function TBGRAMaterial3D.GetAutoSpecularColor: boolean;174 begin175 result := FAutoSpecularColor;176 end;177 178 function TBGRAMaterial3D.GetAutoSimpleColor: boolean;179 begin180 result := FAutoSimpleColor;181 end;182 183 function TBGRAMaterial3D.GetAmbiantAlpha: byte;184 var v: integer;185 begin186 if FAmbiantColorInt.a < 128 then187 result := 0188 else189 begin190 v := (FAmbiantColorInt.a-128) shr 8;191 if v > 255 then v := 255;192 result := v;193 end;194 end;195 196 function TBGRAMaterial3D.GetAmbiantColor: TBGRAPixel;197 begin198 result := ColorIntToBGRA(FAmbiantColorInt);199 end;200 201 function TBGRAMaterial3D.GetAmbiantColorF: TColorF;202 begin203 result := ColorInt65536ToColorF(FAmbiantColorInt);204 end;205 206 function TBGRAMaterial3D.GetAmbiantColorInt: TColorInt65536;207 begin208 result := FAmbiantColorInt;209 end;210 211 function TBGRAMaterial3D.GetDiffuseAlpha: byte;212 var v: integer;213 begin214 if FDiffuseColorInt.a < 128 then215 result := 0216 else217 begin218 v := (FDiffuseColorInt.a-128) shr 8;219 if v > 255 then v := 255;220 result := v;221 end;222 end;223 224 function TBGRAMaterial3D.GetDiffuseColor: TBGRAPixel;225 begin226 result := ColorIntToBGRA(FDiffuseColorInt);227 end;228 229 function TBGRAMaterial3D.GetDiffuseColorF: TColorF;230 begin231 result := ColorInt65536ToColorF(FDiffuseColorInt);232 end;233 234 function TBGRAMaterial3D.GetDiffuseColorInt: TColorInt65536;235 begin236 result := FDiffuseColorInt;237 end;238 239 function TBGRAMaterial3D.GetLightThroughFactor: single;240 begin241 result := FLightThroughFactor;242 end;243 244 function TBGRAMaterial3D.GetSpecularColor: TBGRAPixel;245 begin246 result := ColorIntToBGRA(FSpecularColorInt);247 end;248 249 function TBGRAMaterial3D.GetSpecularColorF: TColorF;250 begin251 result := ColorInt65536ToColorF(FSpecularColorInt);252 end;253 254 function TBGRAMaterial3D.GetSpecularColorInt: TColorInt65536;255 begin256 result := FSpecularColorInt;257 end;258 259 function TBGRAMaterial3D.GetSpecularIndex: integer;260 begin261 result := FSpecularIndex;262 end;263 264 function TBGRAMaterial3D.GetSaturationHigh: single;265 begin266 result := FSaturationHighF;267 end;268 269 function TBGRAMaterial3D.GetSaturationLow: single;270 begin271 result := FSaturationLowF;272 end;273 274 function TBGRAMaterial3D.GetSimpleAlpha: byte;275 begin276 result := (GetAmbiantAlpha + GetDiffuseAlpha) shr 1;277 end;278 279 function TBGRAMaterial3D.GetSimpleColor: TBGRAPixel;280 begin281 result := ColorIntToBGRA(GetSimpleColorInt);282 end;283 284 function TBGRAMaterial3D.GetSimpleColorF: TColorF;285 begin286 result := ColorInt65536ToColorF(GetSimpleColorInt);287 end;288 289 function TBGRAMaterial3D.GetSimpleColorInt: TColorInt65536;290 begin291 result := (GetAmbiantColorInt + GetDiffuseColorInt)*32768;292 end;293 294 function TBGRAMaterial3D.GetTexture: IBGRAScanner;295 begin296 result := FTexture;297 end;298 299 function TBGRAMaterial3D.GetTextureZoom: TPointF;300 begin301 result := FTextureZoom;302 end;303 304 procedure TBGRAMaterial3D.SetAutoAmbiantColor(const AValue: boolean);305 begin306 If AValue then307 SetAmbiantColorInt(ColorInt65536(65536,65536,65536));308 end;309 310 procedure TBGRAMaterial3D.SetAutoDiffuseColor(const AValue: boolean);311 begin312 If AValue then313 SetDiffuseColorInt(ColorInt65536(65536,65536,65536));314 end;315 316 procedure TBGRAMaterial3D.SetAutoSpecularColor(const AValue: boolean);317 begin318 If AValue then319 SetSpecularColorInt(ColorInt65536(65536,65536,65536));320 end;321 322 procedure TBGRAMaterial3D.SetAmbiantAlpha(AValue: byte);323 begin324 if AValue = 0 then325 FAmbiantColorInt.a := 0326 else327 FAmbiantColorInt.a := AValue*257+1;328 UpdateSimpleColor;329 end;330 331 procedure TBGRAMaterial3D.SetAmbiantColor(const AValue: TBGRAPixel);332 begin333 FAmbiantColorInt := BGRAToColorInt(AValue);334 FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);335 UpdateSimpleColor;336 end;337 338 procedure TBGRAMaterial3D.SetAmbiantColorF(const AValue: TColorF);339 begin340 FAmbiantColorInt := ColorFToColorInt65536(AValue);341 FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);342 UpdateSimpleColor;343 end;344 345 procedure TBGRAMaterial3D.SetAmbiantColorInt(const AValue: TColorInt65536);346 begin347 FAmbiantColorInt := AValue;348 FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);349 UpdateSimpleColor;350 end;351 352 procedure TBGRAMaterial3D.SetDiffuseColor(const AValue: TBGRAPixel);353 begin354 FDiffuseColorInt := BGRAToColorInt(AValue);355 FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;356 FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);357 UpdateSimpleColor;358 end;359 360 procedure TBGRAMaterial3D.SetDiffuseColorF(const AValue: TColorF);361 begin362 FDiffuseColorInt := ColorFToColorInt65536(AValue);363 FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;364 FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);365 UpdateSimpleColor;366 end;367 368 procedure TBGRAMaterial3D.SetDiffuseColorInt(const AValue: TColorInt65536);369 begin370 FDiffuseColorInt := AValue;371 FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;372 FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);373 UpdateSimpleColor;374 end;375 376 procedure TBGRAMaterial3D.SetLightThroughFactor(const AValue: single);377 begin378 FLightThroughFactor:= AValue;379 end;380 381 procedure TBGRAMaterial3D.SetSpecularColor(const AValue: TBGRAPixel);382 begin383 FSpecularColorInt := BGRAToColorInt(AValue);384 UpdateSpecular;385 end;386 387 procedure TBGRAMaterial3D.SetSpecularColorF(const AValue: TColorF);388 begin389 FSpecularColorInt := ColorFToColorInt65536(AValue);390 UpdateSpecular;391 end;392 393 procedure TBGRAMaterial3D.SetSpecularColorInt(const AValue: TColorInt65536);394 begin395 FSpecularColorInt := AValue;396 UpdateSpecular;397 end;398 399 procedure TBGRAMaterial3D.SetSpecularIndex(const AValue: integer);400 begin401 FSpecularIndex := AValue;402 FPowerTable := nil;403 UpdateSpecular;404 end;405 406 procedure TBGRAMaterial3D.SetSaturationHigh(const AValue: single);407 begin408 FSaturationHighF:= AValue;409 end;410 411 procedure TBGRAMaterial3D.SetSaturationLow(const AValue: single);412 begin413 FSaturationLowF:= AValue;414 end;415 416 procedure TBGRAMaterial3D.SetSimpleAlpha(AValue: byte);417 begin418 SetAmbiantAlpha(AValue);419 SetDiffuseAlpha(AValue);420 end;421 422 procedure TBGRAMaterial3D.SetSimpleColor(AValue: TBGRAPixel);423 begin424 SetAmbiantColor(AValue);425 SetDiffuseColor(AValue);426 end;427 428 procedure TBGRAMaterial3D.SetSimpleColorF(AValue: TColorF);429 begin430 SetAmbiantColorF(AValue);431 SetDiffuseColorF(AValue);432 end;433 434 procedure TBGRAMaterial3D.SetSimpleColorInt(AValue: TColorInt65536);435 begin436 SetAmbiantColorInt(AValue);437 SetDiffuseColorInt(AValue);438 end;439 440 procedure TBGRAMaterial3D.SetTexture(AValue: IBGRAScanner);441 begin442 FTexture := AValue;443 end;444 445 procedure TBGRAMaterial3D.SetTextureZoom(AValue: TPointF);446 begin447 FTextureZoom := AValue;448 end;449 450 function TBGRAMaterial3D.GetName: string;451 begin452 result := FName;453 end;454 455 procedure TBGRAMaterial3D.SetName(const AValue: string);456 begin457 FName := AValue;458 end;459 460 function TBGRAMaterial3D.GetSpecularOn: boolean;461 begin462 result := FSpecularOn;463 end;464 465 function TBGRAMaterial3D.GetAsObject: TObject;466 begin467 result := self;468 end;469 470 procedure TBGRAMaterial3D.ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext; DiffuseIntensity, SpecularIntensity, SpecularCosine: single; const ALightColor: TColorInt65536);471 var472 NH,PowerTablePos: single; //keep first for asm473 474 NnH: single;475 PowerTableFPos: single;476 PowerTableIPos,i: integer;477 begin478 if SpecularCosine <= 0 then479 NnH := 0480 else481 if SpecularCosine >= 1 then482 NnH := 1 else483 begin484 NH := SpecularCosine;485 if FPowerTable = nil then ComputePowerTable;486 {$IFDEF CPUI386} {$asmmode intel}487 i := FPowerTableExp2;488 if i > 0 then489 begin490 PowerTablePos := FPowerTableSize;491 asm492 db $d9,$45,$f0 //flds NH493 mov ecx,i494 @loop:495 db $dc,$c8 //fmul st,st(0)496 dec ecx497 jnz @loop498 db $d8,$4d,$ec //fmuls PowerTablePos499 db $d9,$5d,$ec //fstps PowerTablePos500 end;501 end502 else503 PowerTablePos := NH*FPowerTableSize;504 {$ELSE}505 PowerTablePos := NH;506 for i := FPowerTableExp2-1 downto 0 do507 PowerTablePos := PowerTablePos*PowerTablePos;508 PowerTablePos *= FPowerTableSize;509 {$ENDIF}510 PowerTableIPos := round(PowerTablePos+0.5);511 PowerTableFPos := PowerTablePos-PowerTableIPos;512 NnH := FPowerTable[PowerTableIPos]*(1-PowerTableFPos)+FPowerTable[PowerTableIPos+1]*PowerTableFPos;513 end; //faster than NnH := exp(FSpecularIndex*ln(NH)); !514 515 if FAutoDiffuseColor then516 Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)517 else518 Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);519 520 if FAutoSpecularColor then521 Context^.specularColor += ALightColor*round(SpecularIntensity* NnH*65536)522 else523 Context^.specularColor += ALightColor*FSpecularColorInt*round(SpecularIntensity* NnH*65536);524 end;525 526 procedure TBGRAMaterial3D.ComputeDiffuseColor(Context: PSceneLightingContext;527 const DiffuseIntensity: single; const ALightColor: TColorInt65536);528 begin529 if FAutoDiffuseColor then530 Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)531 else532 Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);533 end;534 535 procedure TBGRAMaterial3D.ComputeDiffuseLightness(536 Context: PSceneLightingContext; DiffuseLightnessTerm32768: integer; ALightLightness: integer);537 begin538 if FAutoDiffuseColor then539 begin540 if ALightLightness <> 32768 then541 Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,ALightLightness)542 else543 Context^.lightness += DiffuseLightnessTerm32768;544 end else545 begin546 if FDiffuseLightness <> 32768 then547 Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,CombineLightness(FDiffuseLightness,ALightLightness))548 else549 Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,ALightLightness);550 end;551 end;552 553 type554 555 { TBGRALight3D }556 557 TBGRALight3D = class(TInterfacedObject,IBGRALight3D)558 protected559 FMinIntensity: single;560 FColorInt: TColorInt65536;561 FViewVector : TPoint3D_128;562 FLightness: integer;563 public564 constructor Create;565 destructor Destroy; override;566 567 procedure ComputeDiffuseLightness(Context: PSceneLightingContext); virtual; abstract;568 procedure ComputeDiffuseColor(Context: PSceneLightingContext); virtual; abstract;569 procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext); virtual; abstract;570 571 function GetLightnessF: single;572 function GetColor: TBGRAPixel;573 function GetColorF: TColorF;574 function GetColorInt: TColorInt65536;575 function GetAsObject: TObject;576 procedure SetColor(const AValue: TBGRAPixel);577 procedure SetColorF(const AValue: TColorF);578 procedure SetColorInt(const AValue: TColorInt65536);579 function GetColoredLight: boolean;580 581 function GetMinIntensity: single;582 procedure SetMinIntensity(const AValue: single);583 function IsDirectional: boolean; virtual; abstract;584 end;585 586 2 { TBGRADirectionalLight3D } 587 3 … … 591 7 public 592 8 constructor Create(ADirection: TPoint3D); 593 function GetDirection: TPoint3D; 9 function GetDirection: TPoint3D; override; 594 10 procedure SetDirection(const AValue: TPoint3D); 595 11 … … 608 24 public 609 25 constructor Create(AVertex: IBGRAVertex3D; AIntensity: single); 610 function GetIntensity: single; 26 function GetIntensity: single; override; 611 27 procedure SetIntensity(const AValue: single); 612 28 613 29 function GetVertex: IBGRAVertex3D; 614 30 procedure SetVertex(const AValue: IBGRAVertex3D); 31 function GetPosition: TPoint3D; override; 615 32 616 33 procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext); override; … … 619 36 function IsDirectional: boolean; override; 620 37 end; 621 622 { TBGRALight3D }623 624 constructor TBGRALight3D.Create;625 begin626 SetColorF(ColorF(1,1,1,1));627 FViewVector := Point3D_128(0,0,-1);628 FMinIntensity:= 0;629 end;630 631 destructor TBGRALight3D.Destroy;632 begin633 inherited Destroy;634 end;635 636 function TBGRALight3D.GetLightnessF: single;637 begin638 result := FLightness/32768;639 end;640 641 function TBGRALight3D.GetColor: TBGRAPixel;642 begin643 result := ColorIntToBGRA(FColorInt);644 end;645 646 function TBGRALight3D.GetColorF: TColorF;647 begin648 result := ColorInt65536ToColorF(FColorInt);649 end;650 651 function TBGRALight3D.GetColorInt: TColorInt65536;652 begin653 result := FColorInt;654 end;655 656 function TBGRALight3D.GetAsObject: TObject;657 begin658 result := self;659 end;660 661 procedure TBGRALight3D.SetColor(const AValue: TBGRAPixel);662 begin663 SetColorInt(BGRAToColorInt(AValue));664 end;665 666 procedure TBGRALight3D.SetColorF(const AValue: TColorF);667 begin668 SetColorInt(ColorFToColorInt65536(AValue));669 end;670 671 procedure TBGRALight3D.SetColorInt(const AValue: TColorInt65536);672 begin673 FColorInt := AValue;674 FLightness:= (AValue.r+AValue.g+AValue.b) div 6;675 end;676 677 function TBGRALight3D.GetColoredLight: boolean;678 begin679 result := (FColorInt.r <> FColorInt.g) or (FColorInt.g <> FColorInt.b);680 end;681 682 function TBGRALight3D.GetMinIntensity: single;683 begin684 result := FMinIntensity;685 end;686 687 procedure TBGRALight3D.SetMinIntensity(const AValue: single);688 begin689 FMinIntensity := AValue;690 end;691 38 692 39 { TBGRAPointLight3D } … … 719 66 end; 720 67 68 function TBGRAPointLight3D.GetPosition: TPoint3D; 69 begin 70 Result:= FVertex.GetViewCoord; 71 end; 72 721 73 procedure TBGRAPointLight3D.ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext); 722 74 {$DEFINE PARAM_POINTLIGHT} … … 735 87 else 736 88 begin 737 intensity := (DotProduct3D_128(vect, Context^.basic.Normal))/(dist2*sqrt(dist2))*FIntensity;89 intensity := DotProduct3D_128(vect, Context^.basic.Normal)/(dist2*sqrt(dist2))*FIntensity; 738 90 if Context^.LightThrough and (intensity < 0) then intensity := -intensity*Context^.LightThroughFactor; 739 91 if intensity > 100 then intensity := 100; … … 743 95 end; 744 96 745 procedure TBGRAPointLight3D.ComputeDiffuseColor(Context: PSceneLightingContext 746 ); 97 procedure TBGRAPointLight3D.ComputeDiffuseColor(Context: PSceneLightingContext); 747 98 var 748 99 vect: TPoint3D_128; … … 755 106 else 756 107 begin 757 intensity := (DotProduct3D_128(vect, Context^.basic.Normal))/(dist2*sqrt(dist2))*FIntensity;108 intensity := DotProduct3D_128(vect, Context^.basic.Normal)/(dist2*sqrt(dist2))*FIntensity; 758 109 if Context^.LightThrough and (intensity < 0) then intensity := -intensity*Context^.LightThroughFactor; 759 110 if intensity > 100 then intensity := 100; -
GraphicTest/Packages/bgrabitmap/object3d.inc
r472 r494 1 2 1 { TBGRAObject3D } 3 2 … … 15 14 FLight := 1; 16 15 FTexture := nil; 17 FMainPart := TBGRAPart3D.Create( nil);16 FMainPart := TBGRAPart3D.Create(self,nil); 18 17 FLightingNormal:= AScene.DefaultLightingNormal; 19 18 FParentLighting:= True; 20 19 FScene := AScene; 20 FFaceColorsInvalidated := true; 21 FMaterialInvalidated := false; 21 22 end; 22 23 23 24 destructor TBGRAObject3D.Destroy; 24 25 begin 26 FMaterial := nil; 25 27 fillchar(FTexture,sizeof(FTexture),0); 26 28 inherited Destroy; … … 34 36 end; 35 37 38 procedure TBGRAObject3D.InvalidateColor; 39 begin 40 FFaceColorsInvalidated := true; 41 end; 42 43 procedure TBGRAObject3D.InvalidateMaterial; 44 begin 45 FMaterialInvalidated := true; 46 end; 47 36 48 function TBGRAObject3D.GetColor: TBGRAPixel; 37 49 begin … … 58 70 FColor := AValue; 59 71 FTexture := nil; 72 InvalidateColor; 60 73 end; 61 74 … … 68 81 begin 69 82 FTexture := AValue; 83 InvalidateMaterial; 70 84 end; 71 85 … … 73 87 begin 74 88 FMaterial := AValue; 89 InvalidateMaterial; 75 90 end; 76 91 … … 124 139 end; 125 140 126 function TBGRAObject3D.GetScene: T BGRAScene3D;141 function TBGRAObject3D.GetScene: TObject; 127 142 begin 128 143 result := FScene; … … 151 166 for i := 0 to GetFaceCount-1 do 152 167 ACallback(GetFace(i)); 168 end; 169 170 procedure TBGRAObject3D.Update; 171 var 172 i: Integer; 173 begin 174 if FParentLighting and (FLightingNormal <> FScene.DefaultLightingNormal) then 175 FLightingNormal := FScene.DefaultLightingNormal; 176 177 if FFaceColorsInvalidated then 178 begin 179 for i := 0 to FFaceCount-1 do 180 FFaces[i].ComputeVertexColors; 181 FFaceColorsInvalidated := false; 182 end; 183 184 if FMaterialInvalidated then 185 begin 186 for i := 0 to FFaceCount-1 do 187 FFaces[i].UpdateMaterial; 188 FMaterialInvalidated := false; 189 end; 153 190 end; 154 191 -
GraphicTest/Packages/bgrabitmap/paletteformats.inc
r472 r494 316 316 function ReadInt16: int16; 317 317 begin 318 AStream.Read({%H-}result, sizeof(result)); 318 {$PUSH}{$HINTS OFF} 319 AStream.Read(result, sizeof(result)); 320 {$POP} 319 321 result := BEtoN(result); 320 322 end; 321 323 function ReadInt32: int32; 322 324 begin 323 AStream.Read({%H-}result, sizeof(result)); 325 {$PUSH}{$HINTS OFF} 326 AStream.Read(result, sizeof(result)); 327 {$POP} 324 328 result := BEtoN(result); 325 329 end; … … 332 336 function ReadSingle: single; 333 337 begin 334 AStream.Read({%H-}Result, sizeof(result)); 338 {$PUSH}{$HINTS OFF} 339 AStream.Read(Result, sizeof(result)); 340 {$POP} 335 341 DWord(Result) := BEtoN(DWord(Result)); 336 342 end; -
GraphicTest/Packages/bgrabitmap/part3d.inc
r472 r494 14 14 FCoordPool: TBGRACoordPool3D; 15 15 FNormalPool: TBGRANormalPool3D; 16 FObject3D: TBGRAObject3D; 16 17 public 17 constructor Create(A Container: IBGRAPart3D);18 constructor Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D); 18 19 destructor Destroy; override; 19 20 procedure Clear(ARecursive: boolean); … … 214 215 end; 215 216 216 constructor TBGRAPart3D.Create(AContainer: IBGRAPart3D); 217 begin 217 constructor TBGRAPart3D.Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D); 218 begin 219 FObject3D := AObject3D; 218 220 FContainer := AContainer; 219 221 FMatrix := MatrixIdentity3D; … … 229 231 FVertexCount := 0; 230 232 if FCoordPool.UsedCapacity > 0 then 231 raise Exception.Create('Coordinate pool still used ');233 raise Exception.Create('Coordinate pool still used. Please set vertex references to nil before destroying the scene.'); 232 234 FreeAndNil(FCoordPool); 233 235 if Assigned(FNormalPool) then … … 258 260 function TBGRAPart3D.Add(x, y, z: single): IBGRAVertex3D; 259 261 begin 260 result := TBGRAVertex3D.Create(F CoordPool,Point3D(x,y,z));262 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,Point3D(x,y,z)); 261 263 Add(result); 262 264 end; … … 264 266 function TBGRAPart3D.Add(pt: TPoint3D): IBGRAVertex3D; 265 267 begin 266 result := TBGRAVertex3D.Create(F CoordPool,pt);268 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt); 267 269 Add(result); 268 270 end; … … 270 272 function TBGRAPart3D.Add(pt: TPoint3D; normal: TPoint3D): IBGRAVertex3D; 271 273 begin 272 result := TBGRAVertex3D.Create(F CoordPool,pt);274 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt); 273 275 result.CustomNormal := normal; 274 276 Add(result); … … 277 279 function TBGRAPart3D.Add(pt: TPoint3D_128): IBGRAVertex3D; 278 280 begin 279 result := TBGRAVertex3D.Create(F CoordPool,pt);281 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt); 280 282 Add(result); 281 283 end; … … 283 285 function TBGRAPart3D.Add(pt: TPoint3D_128; normal: TPoint3D_128): IBGRAVertex3D; 284 286 begin 285 result := TBGRAVertex3D.Create(F CoordPool,pt);287 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt); 286 288 result.CustomNormal := Point3D(normal); 287 289 Add(result); … … 312 314 setlength(result, length(pts)); 313 315 for i := 0 to high(pts) do 314 result[i] := TBGRAVertex3D.Create(F CoordPool,pts[i]);316 result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]); 315 317 Add(result); 316 318 end; … … 323 325 setlength(result, length(pts)); 324 326 for i := 0 to high(pts) do 325 result[i] := TBGRAVertex3D.Create(F CoordPool,pts[i]);327 result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]); 326 328 Add(result); 327 329 end; … … 490 492 end; 491 493 494 {$PUSH}{$OPTIMIZATION OFF} //avoids Internal error 2012090607 492 495 procedure TBGRAPart3D.ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D); 493 496 var … … 501 504 FParts[i].ComputeWithMatrix(Composed,AProjection); 502 505 end; 506 {$POP} 503 507 504 508 function TBGRAPart3D.ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF; … … 550 554 if FPartCount = length(FParts) then 551 555 setlength(FParts, FPartCount*2+1); 552 result := TBGRAPart3D.Create( self);556 result := TBGRAPart3D.Create(FObject3D,self); 553 557 FParts[FPartCount] := result; 554 558 inc(FPartCount); -
GraphicTest/Packages/bgrabitmap/phonglight.inc
r472 r494 2 2 var 3 3 {%H-}dist2,LdotN,NdotH,lightEnergy,diffuse : single; 4 const5 minus_05 = -0.5;6 4 begin 7 5 {$IFDEF BGRASSE_AVAILABLE}If UseSSE then … … 45 43 end; 46 44 47 if LdotN < minus_05 then NdotH := 0 else48 if LdotN < 0 then49 begin50 NdotH := NdotH*(LdotN-minus_05);51 NdotH += NdotH;52 end;53 54 45 {$IFDEF PARAM_POINTLIGHT} 55 46 if dist2 = 0 then … … 62 53 diffuse := LdotN; 63 54 {$ENDIF} 55 if diffuse < FMinIntensity then diffuse:= FMinIntensity; 64 56 65 57 if Context^.LightThrough and (diffuse < 0) then diffuse := -diffuse*Context^.LightThroughFactor; -
GraphicTest/Packages/bgrabitmap/polyaliaspersp.inc
r472 r494 165 165 166 166 begin 167 If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;167 If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit; 168 168 169 169 inter := polyInfo.CreateIntersectionArray; … … 361 361 362 362 begin 363 If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;363 If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit; 364 364 365 365 inter := polyInfo.CreateIntersectionArray; -
GraphicTest/Packages/bgrabitmap/readme.txt
r472 r494 1 BGRABitmap - Drawing routines with alpha blending and antialiasing with Lazarus.1 BGRABitmap - Drawing routines with transparency and antialiasing with Lazarus. Offers also various transforms. 2 2 3 These routines allow to manipulate 32bit images in BGRA format .3 These routines allow to manipulate 32bit images in BGRA format or RGBA format (depending on the platform). 4 4 5 5 This code is under modified LGPL (see COPYING.modifiedLGPL.txt). This means that you can link this library inside your programs for any purpose. Only the included part of the code must remain LGPL. -
GraphicTest/Packages/bgrabitmap/unzipperext.pas
r472 r494 31 31 implementation 32 32 33 uses lazutf8classes;33 uses BGRAUTF8; 34 34 35 35 { TUnzipperStreamUtf8 } -
GraphicTest/Packages/bgrabitmap/vertex3d.inc
r472 r494 1 1 type 2 { TBGRAObject3D } 3 4 TBGRAObject3D = class(TInterfacedObject,IBGRAObject3D) 5 private 6 FColor: TBGRAPixel; 7 FLight: Single; 8 FTexture: IBGRAScanner; 9 FMainPart: IBGRAPart3D; 10 FFaces: array of IBGRAFace3D; 11 FFaceCount: integer; 12 FLightingNormal : TLightingNormal3D; 13 FParentLighting: boolean; 14 FMaterial: IBGRAMaterial3D; 15 FScene: TBGRAScene3D; 16 FFaceColorsInvalidated, 17 FMaterialInvalidated: boolean; 18 procedure AddFace(AFace: IBGRAFace3D); 19 public 20 constructor Create(AScene: TBGRAScene3D); 21 destructor Destroy; override; 22 procedure Clear; 23 procedure InvalidateColor; 24 procedure InvalidateMaterial; 25 function AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D; 26 function AddFace(const AVertices: array of IBGRAVertex3D; ABiface: boolean): IBGRAFace3D; 27 function AddFace(const AVertices: array of IBGRAVertex3D; ATexture: IBGRAScanner): IBGRAFace3D; 28 function AddFace(const AVertices: array of IBGRAVertex3D; AColor: TBGRAPixel): IBGRAFace3D; 29 function AddFace(const AVertices: array of IBGRAVertex3D; AColors: array of TBGRAPixel): IBGRAFace3D; 30 function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D; 31 procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D); 32 function GetColor: TBGRAPixel; 33 function GetLight: Single; 34 function GetTexture: IBGRAScanner; 35 function GetMainPart: IBGRAPart3D; 36 function GetLightingNormal: TLightingNormal3D; 37 function GetParentLighting: boolean; 38 function GetFace(AIndex: integer): IBGRAFace3D; 39 function GetFaceCount: integer; 40 function GetTotalVertexCount: integer; 41 function GetTotalNormalCount: integer; 42 function GetMaterial: IBGRAMaterial3D; 43 procedure SetLightingNormal(const AValue: TLightingNormal3D); 44 procedure SetParentLighting(const AValue: boolean); 45 procedure SetColor(const AValue: TBGRAPixel); 46 procedure SetLight(const AValue: Single); 47 procedure SetTexture(const AValue: IBGRAScanner); 48 procedure SetMaterial(const AValue: IBGRAMaterial3D); 49 procedure RemoveUnusedVertices; 50 procedure SeparatePart(APart: IBGRAPart3D); 51 function GetScene: TObject; 52 function GetRefCount: integer; 53 procedure SetBiface(AValue : boolean); 54 procedure ForEachVertex(ACallback: TVertex3DCallback); 55 procedure ForEachFace(ACallback: TFace3DCallback); 56 procedure Update; 57 end; 58 2 59 { TBGRAVertex3D } 3 60 … … 11 68 FCoordPoolIndex: integer; 12 69 FCustomFlags: DWord; 70 FObject3D: TBGRAObject3D; 13 71 function GetCoordData: PBGRACoordData3D; 14 procedure Init(A CoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);72 procedure Init(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); 15 73 public 16 constructor Create(A CoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); overload;17 constructor Create(A CoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); overload;74 constructor Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); overload; 75 constructor Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); overload; 18 76 destructor Destroy; override; 19 77 function GetColor: TBGRAPixel; … … 166 224 { TBGRAVertex3D } 167 225 168 procedure TBGRAVertex3D.Init(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); 169 begin 226 procedure TBGRAVertex3D.Init(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); 227 begin 228 FObject3D := AObject3D; 170 229 FCoordPool := ACoordPool; 171 230 FCoordPoolIndex := FCoordPool.Add; … … 209 268 end; 210 269 211 constructor TBGRAVertex3D.Create(A CoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D);212 begin 213 Init(A CoordPool, Point3D_128(ASceneCoord));214 end; 215 216 constructor TBGRAVertex3D.Create(A CoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);217 begin 218 Init(A CoordPool, ASceneCoord);270 constructor TBGRAVertex3D.Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); 271 begin 272 Init(AObject3D, ACoordPool, Point3D_128(ASceneCoord)); 273 end; 274 275 constructor TBGRAVertex3D.Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); 276 begin 277 Init(AObject3D, ACoordPool, ASceneCoord); 219 278 end; 220 279 … … 284 343 FColor := AValue; 285 344 FParentColor := false; 345 FObject3D.InvalidateColor; 286 346 end; 287 347 … … 339 399 begin 340 400 FParentColor := AValue; 401 FObject3D.InvalidateColor; 341 402 end; 342 403
Note:
See TracChangeset
for help on using the changeset viewer.