Changeset 494 for GraphicTest/Packages/bgrabitmap/bgraanimatedgif.pas
- Timestamp:
- Dec 22, 2016, 8:49:19 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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
Note:
See TracChangeset
for help on using the changeset viewer.