GifImage.pas
上传用户:daoqigc
上传日期:2021-04-20
资源大小:2795k
文件大小:363k
源码类别:

RichEdit

开发平台:

Delphi

  1.   IsLeaf := (Level = ColorBits);
  2.   if (IsLeaf) then
  3.   begin
  4.     Next := nil;
  5.     inc(LeafCount);
  6.   end else
  7.   begin
  8.     Next := ReducibleNodes[Level];
  9.     ReducibleNodes[Level] := self;
  10.   end;
  11. end;
  12. destructor TOctreeNode.Destroy;
  13. var
  14.   i : integer;
  15. begin
  16.   for i := High(Child) downto Low(Child) do
  17.     Child[i].Free;
  18. end;
  19. constructor TColorQuantizer.Create(MaxColors: integer; ColorBits: integer);
  20. var
  21.   i : integer;
  22. begin
  23.   ASSERT(ColorBits <= 8, 'ColorBits must be 8 or less');
  24.   FTree := nil;
  25.   FLeafCount := 0;
  26.   // Initialize all nodes even though only ColorBits+1 of them are needed
  27.   for i := Low(FReducibleNodes) to High(FReducibleNodes) do
  28.     FReducibleNodes[i] := nil;
  29.   FMaxColors := MaxColors;
  30.   FColorBits := ColorBits;
  31. end;
  32. destructor TColorQuantizer.Destroy;
  33. begin
  34.   if (FTree <> nil) then
  35.     DeleteTree(FTree);
  36. end;
  37. procedure TColorQuantizer.GetColorTable(var RGBQuadArray: TRGBQuadArray);
  38. var
  39.   Index : integer;
  40. begin
  41.   Index := 0;
  42.   GetPaletteColors(FTree, RGBQuadArray, Index);
  43. end;
  44. // Handles passed to ProcessImage should refer to DIB sections, not DDBs.
  45. // In certain cases, specifically when it's called upon to process 1, 4, or
  46. // 8-bit per pixel images on systems with palettized display adapters,
  47. // ProcessImage can produce incorrect results if it's passed a handle to a
  48. // DDB.
  49. function TColorQuantizer.ProcessImage(const DIB: TDIBReader): boolean;
  50. var
  51.   i ,
  52.   j : integer;
  53.   ScanLine : pointer;
  54.   Pixel : PRGBTriple;
  55. begin
  56.   Result := True;
  57.   for j := 0 to DIB.Bitmap.Height-1 do
  58.   begin
  59.     Scanline := DIB.Scanline[j];
  60.     Pixel := ScanLine;
  61.     for i := 0 to DIB.Bitmap.Width-1 do
  62.     begin
  63.       with Pixel^ do
  64.         AddColor(FTree, rgbtRed, rgbtGreen, rgbtBlue,
  65.                  FColorBits, 0, FLeafCount, FReducibleNodes);
  66.       while FLeafCount > FMaxColors do
  67.         ReduceTree(FColorbits, FLeafCount, FReducibleNodes);
  68.       inc(Pixel);
  69.     end;
  70.   end;
  71. end;
  72. procedure TColorQuantizer.AddColor(var Node: TOctreeNode; r,g,b: byte;
  73.   ColorBits: integer; Level: integer; var LeafCount: integer;
  74.   var ReducibleNodes: TReducibleNodes);
  75. const
  76.   Mask:  array[0..7] of BYTE = ($80, $40, $20, $10, $08, $04, $02, $01);
  77. var
  78.   Index : integer;
  79.   Shift : integer;
  80. begin
  81.   // If the node doesn't exist, create it.
  82.   if (Node = nil) then
  83.     Node := TOctreeNode.Create(Level, ColorBits, LeafCount, ReducibleNodes);
  84.   if (Node.IsLeaf) then
  85.   begin
  86.     inc(Node.PixelCount);
  87.     inc(Node.RedSum, r);
  88.     inc(Node.GreenSum, g);
  89.     inc(Node.BlueSum, b);
  90.   end else
  91.   begin
  92.     // Recurse a level deeper if the node is not a leaf.
  93.     Shift := 7 - Level;
  94.     Index := (((r and mask[Level]) SHR Shift) SHL 2)  or
  95.              (((g and mask[Level]) SHR Shift) SHL 1)  or
  96.               ((b and mask[Level]) SHR Shift);
  97.     AddColor(Node.Child[Index], r, g, b, ColorBits, Level+1, LeafCount, ReducibleNodes);
  98.   end;
  99. end;
  100. procedure TColorQuantizer.DeleteTree(var Node: TOctreeNode);
  101. var
  102.   i : integer;
  103. begin
  104.   for i := High(TReducibleNodes) downto Low(TReducibleNodes) do
  105.     if (Node.Child[i] <> nil) then
  106.       DeleteTree(Node.Child[i]);
  107.   Node.Free;
  108.   Node := nil;
  109. end;
  110. procedure TColorQuantizer.GetPaletteColors(const Node: TOctreeNode;
  111.   var RGBQuadArray: TRGBQuadArray; var Index: integer);
  112. var
  113.   i : integer;
  114. begin
  115.   if (Node.IsLeaf) then
  116.   begin
  117.     with RGBQuadArray[Index] do
  118.     begin
  119.       if (Node.PixelCount <> 0) then
  120.       begin
  121.         rgbRed   := BYTE(Node.RedSum   DIV Node.PixelCount);
  122.         rgbGreen := BYTE(Node.GreenSum DIV Node.PixelCount);
  123.         rgbBlue  := BYTE(Node.BlueSum  DIV Node.PixelCount);
  124.       end else
  125.       begin
  126.         rgbRed := 0;
  127.         rgbGreen := 0;
  128.         rgbBlue := 0;
  129.       end;
  130.       rgbReserved := 0;
  131.     end;
  132.     inc(Index);
  133.   end else
  134.   begin
  135.     for i := Low(Node.Child) to High(Node.Child) do
  136.       if (Node.Child[i] <> nil) then
  137.         GetPaletteColors(Node.Child[i], RGBQuadArray, Index);
  138.   end;
  139. end;
  140. procedure TColorQuantizer.ReduceTree(ColorBits: integer; var LeafCount: integer;
  141.   var ReducibleNodes: TReducibleNodes);
  142. var
  143.   RedSum ,
  144.   GreenSum ,
  145.   BlueSum  : integer;
  146.   Children : integer;
  147.   i : integer;
  148.   Node : TOctreeNode;
  149. begin
  150.   // Find the deepest level containing at least one reducible node
  151.   i := Colorbits - 1;
  152.   while (i > 0) and (ReducibleNodes[i] = nil) do
  153.     dec(i);
  154.   // Reduce the node most recently added to the list at level i.
  155.   Node := ReducibleNodes[i];
  156.   ReducibleNodes[i] := Node.Next;
  157.   RedSum   := 0;
  158.   GreenSum := 0;
  159.   BlueSum  := 0;
  160.   Children := 0;
  161.   for i := Low(ReducibleNodes) to High(ReducibleNodes) do
  162.     if (Node.Child[i] <> nil) then
  163.     begin
  164.       inc(RedSum, Node.Child[i].RedSum);
  165.       inc(GreenSum, Node.Child[i].GreenSum);
  166.       inc(BlueSum, Node.Child[i].BlueSum);
  167.       inc(Node.PixelCount, Node.Child[i].PixelCount);
  168.       Node.Child[i].Free;
  169.       Node.Child[i] := nil;
  170.       inc(Children);
  171.     end;
  172.   Node.IsLeaf := TRUE;
  173.   Node.RedSum := RedSum;
  174.   Node.GreenSum := GreenSum;
  175.   Node.BlueSum := BlueSum;
  176.   dec(LeafCount, Children-1);
  177. end;
  178. ////////////////////////////////////////////////////////////////////////////////
  179. //
  180. // Octree Color Quantization Wrapper
  181. //
  182. ////////////////////////////////////////////////////////////////////////////////
  183. // Adapted from Earl F. Glynn's PaletteLibrary, March 1998
  184. ////////////////////////////////////////////////////////////////////////////////
  185. // Wrapper for internal use - uses TDIBReader for bitmap access
  186. function doCreateOptimizedPaletteFromSingleBitmap(const DIB: TDIBReader;
  187.   Colors, ColorBits: integer; Windows: boolean): hPalette;
  188. var
  189.   SystemPalette : HPalette;
  190.   ColorQuantizer : TColorQuantizer;
  191.   i : integer;
  192.   LogicalPalette : TMaxLogPalette;
  193.   RGBQuadArray : TRGBQuadArray;
  194.   Offset : integer;
  195. begin
  196.   LogicalPalette.palVersion := $0300;
  197.   LogicalPalette.palNumEntries := Colors;
  198.   if (Windows) then
  199.   begin
  200.     // Get the windows 20 color system palette
  201.     SystemPalette := GetStockObject(DEFAULT_PALETTE);
  202.     GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  203.     GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[245]);
  204.     Colors := 236;
  205.     Offset := 10;
  206.     LogicalPalette.palNumEntries := 256;
  207.   end else
  208.     Offset := 0;
  209.   // Normally for 24-bit images, use ColorBits of 5 or 6.  For 8-bit images
  210.   // use ColorBits = 8.
  211.   ColorQuantizer := TColorQuantizer.Create(Colors, ColorBits);
  212.   try
  213.     ColorQuantizer.ProcessImage(DIB);
  214.     ColorQuantizer.GetColorTable(RGBQuadArray);
  215.   finally
  216.     ColorQuantizer.Free;
  217.   end;
  218.   for i := 0 to Colors-1 do
  219.     with LogicalPalette.palPalEntry[i+Offset] do
  220.     begin
  221.       peRed   := RGBQuadArray[i].rgbRed;
  222.       peGreen := RGBQuadArray[i].rgbGreen;
  223.       peBlue  := RGBQuadArray[i].rgbBlue;
  224.       peFlags := RGBQuadArray[i].rgbReserved;
  225.     end;
  226.   Result := CreatePalette(pLogPalette(@LogicalPalette)^);
  227. end;
  228. function CreateOptimizedPaletteFromSingleBitmap(const Bitmap: TBitmap;
  229.   Colors, ColorBits: integer; Windows: boolean): hPalette;
  230. var
  231.   DIB : TDIBReader;
  232. begin
  233.   DIB := TDIBReader.Create(Bitmap, pf24bit);
  234.   try
  235.     Result := doCreateOptimizedPaletteFromSingleBitmap(DIB, Colors, ColorBits, Windows);
  236.   finally
  237.     DIB.Free;
  238.   end;
  239. end;
  240. function CreateOptimizedPaletteFromManyBitmaps(Bitmaps: TList; Colors, ColorBits: integer;
  241.   Windows: boolean): hPalette;
  242. var
  243.   SystemPalette : HPalette;
  244.   ColorQuantizer : TColorQuantizer;
  245.   i : integer;
  246.   LogicalPalette : TMaxLogPalette;
  247.   RGBQuadArray : TRGBQuadArray;
  248.   Offset : integer;
  249.   DIB : TDIBReader;
  250. begin
  251.   if (Bitmaps = nil) or (Bitmaps.Count = 0) then
  252.     Error(sInvalidBitmapList);
  253.   LogicalPalette.palVersion := $0300;
  254.   LogicalPalette.palNumEntries := Colors;
  255.   if (Windows) then
  256.   begin
  257.     // Get the windows 20 color system palette
  258.     SystemPalette := GetStockObject(DEFAULT_PALETTE);
  259.     GetPaletteEntries(SystemPalette, 0, 10, LogicalPalette.palPalEntry[0]);
  260.     GetPaletteEntries(SystemPalette, 10, 10, LogicalPalette.palPalEntry[245]);
  261.     Colors := 236;
  262.     Offset := 10;
  263.     LogicalPalette.palNumEntries := 256;
  264.   end else
  265.     Offset := 0;
  266.   // Normally for 24-bit images, use ColorBits of 5 or 6.  For 8-bit images
  267.   // use ColorBits = 8.
  268.   ColorQuantizer := TColorQuantizer.Create(Colors, ColorBits);
  269.   try
  270.     for i := 0 to Bitmaps.Count-1 do
  271.     begin
  272.       DIB := TDIBReader.Create(TBitmap(Bitmaps[i]), pf24bit);
  273.       try
  274.         ColorQuantizer.ProcessImage(DIB);
  275.       finally
  276.         DIB.Free;
  277.       end;
  278.     end;
  279.     ColorQuantizer.GetColorTable(RGBQuadArray);
  280.   finally
  281.     ColorQuantizer.Free;
  282.   end;
  283.   for i := 0 to Colors-1 do
  284.     with LogicalPalette.palPalEntry[i+Offset] do
  285.     begin
  286.       peRed   := RGBQuadArray[i].rgbRed;
  287.       peGreen := RGBQuadArray[i].rgbGreen;
  288.       peBlue  := RGBQuadArray[i].rgbBlue;
  289.       peFlags := RGBQuadArray[i].rgbReserved;
  290.     end;
  291.   Result := CreatePalette(pLogPalette(@LogicalPalette)^);
  292. end;
  293. ////////////////////////////////////////////////////////////////////////////////
  294. //
  295. // Color reduction
  296. //
  297. ////////////////////////////////////////////////////////////////////////////////
  298. {$IFOPT R+}
  299.   {$DEFINE R_PLUS}
  300.   {$RANGECHECKS OFF}
  301. {$ENDIF}
  302. //: Reduces the color depth of a bitmap using color quantization and dithering.
  303. function ReduceColors(Bitmap: TBitmap; ColorReduction: TColorReduction;
  304.   DitherMode: TDitherMode; ReductionBits: integer; CustomPalette: hPalette): TBitmap;
  305. var
  306.   Palette : hPalette;
  307.   ColorLookup : TColorLookup;
  308.   Ditherer : TDitherEngine;
  309.   Row : Integer;
  310.   DIBResult : TDIBWriter;
  311.   DIBSource : TDIBReader;
  312.   SrcScanLine ,
  313.   Src : PRGBTriple;
  314.   DstScanLine ,
  315.   Dst : PChar;
  316.   BGR : TRGBTriple;
  317. {$ifdef DEBUG_DITHERPERFORMANCE}
  318.   TimeStart ,
  319.   TimeStop : DWORD;
  320. {$endif}
  321.   function GrayScalePalette: hPalette;
  322.   var
  323.     i : integer;
  324.     Pal : TMaxLogPalette;
  325.   begin
  326.     Pal.palVersion := $0300;
  327.     Pal.palNumEntries := 256;
  328.     for i := 0 to 255 do
  329.     begin
  330.       with (Pal.palPalEntry[i]) do
  331.       begin
  332.         peRed := i;
  333.         peGreen := i;
  334.         peBlue  := i;
  335.         peFlags := PC_NOCOLLAPSE;
  336.       end;
  337.     end;
  338.     Result := CreatePalette(pLogPalette(@Pal)^);
  339.   end;
  340.   function MonochromePalette: hPalette;
  341.   var
  342.     i : integer;
  343.     Pal : TMaxLogPalette;
  344.   const
  345.     Values : array[0..1] of byte
  346.      = (0, 255);
  347.   begin
  348.     Pal.palVersion := $0300;
  349.     Pal.palNumEntries := 2;
  350.     for i := 0 to 1 do
  351.     begin
  352.       with (Pal.palPalEntry[i]) do
  353.       begin
  354.         peRed := Values[i];
  355.         peGreen := Values[i];
  356.         peBlue  := Values[i];
  357.         peFlags := PC_NOCOLLAPSE;
  358.       end;
  359.     end;
  360.     Result := CreatePalette(pLogPalette(@Pal)^);
  361.   end;
  362.   function WindowsGrayScalePalette: hPalette;
  363.   var
  364.     i : integer;
  365.     Pal : TMaxLogPalette;
  366.   const
  367.     Values : array[0..3] of byte
  368.      = (0, 128, 192, 255);
  369.   begin
  370.     Pal.palVersion := $0300;
  371.     Pal.palNumEntries := 4;
  372.     for i := 0 to 3 do
  373.     begin
  374.       with (Pal.palPalEntry[i]) do
  375.       begin
  376.         peRed := Values[i];
  377.         peGreen := Values[i];
  378.         peBlue  := Values[i];
  379.         peFlags := PC_NOCOLLAPSE;
  380.       end;
  381.     end;
  382.     Result := CreatePalette(pLogPalette(@Pal)^);
  383.   end;
  384.   function WindowsHalftonePalette: hPalette;
  385.   var
  386.     DC : HDC;
  387.   begin
  388.     DC := GDICheck(GetDC(0));
  389.     try
  390.       Result := CreateHalfTonePalette(DC);
  391.     finally
  392.       ReleaseDC(0, DC);
  393.     end;
  394.   end;
  395. begin
  396. {$ifdef DEBUG_DITHERPERFORMANCE}
  397.   timeBeginPeriod(5);
  398.   TimeStart := timeGetTime;
  399. {$endif}
  400.   Result := TBitmap.Create;
  401.   try
  402.     if (ColorReduction = rmNone) then
  403.     begin
  404.       Result.Assign(Bitmap);
  405. {$ifndef VER9x}
  406.       SetPixelFormat(Result, pf24bit);
  407. {$endif}
  408.       exit;
  409.     end;
  410. {$IFNDEF VER9x}
  411.     if (Bitmap.Width*Bitmap.Height > BitmapAllocationThreshold) then
  412.       SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  413. {$ENDIF}
  414.     ColorLookup := nil;
  415.     Ditherer := nil;
  416.     DIBResult := nil;
  417.     DIBSource := nil;
  418.     Palette := 0;
  419.     try // Protect above resources
  420.       // Dithering and color mapper only supports 24 bit bitmaps,
  421.       // so we have convert the source bitmap to the appropiate format.
  422.       DIBSource := TDIBReader.Create(Bitmap, pf24bit);
  423.       // Create a palette based on current options
  424.       case (ColorReduction) of
  425.         rmQuantize:
  426.           Palette := doCreateOptimizedPaletteFromSingleBitmap(DIBSource, 1 SHL ReductionBits, 8, False);
  427.         rmQuantizeWindows:
  428.           Palette := CreateOptimizedPaletteFromSingleBitmap(Bitmap, 256, 8, True);
  429.         rmNetscape:
  430.           Palette := WebPalette;
  431.         rmGrayScale:
  432.           Palette := GrayScalePalette;
  433.         rmMonochrome:
  434.           Palette := MonochromePalette;
  435.         rmWindowsGray:
  436.           Palette := WindowsGrayScalePalette;
  437.         rmWindows20:
  438.           Palette := GetStockObject(DEFAULT_PALETTE);
  439.         rmWindows256:
  440.           Palette := WindowsHalftonePalette;
  441.         rmPalette:
  442.           Palette := CopyPalette(CustomPalette);
  443.       else
  444.         exit;
  445.       end;
  446.       { TODO -oanme -cImprovement : Gray scale conversion should be done prior to dithering/mapping. Otherwise corrected values will be converted multiple times. }
  447.       // Create a color mapper based on current options
  448.       case (ColorReduction) of
  449.         // For some strange reason my fast and dirty color lookup
  450.         // is more precise that Windows GetNearestPaletteIndex...
  451.         // rmWindows20:
  452.         //  ColorLookup := TSlowColorLookup.Create(Palette);
  453.         // rmWindowsGray:
  454.         //  ColorLookup := TGrayWindowsLookup.Create(Palette);
  455.         rmQuantize:
  456.           ColorLookup := TFastColorLookup.Create(Palette);
  457.         rmNetscape:
  458.           ColorLookup := TNetscapeColorLookup.Create(Palette);
  459.         rmGrayScale:
  460.           ColorLookup := TGrayScaleLookup.Create(Palette);
  461.         rmMonochrome:
  462.           ColorLookup := TMonochromeLookup.Create(Palette);
  463.       else
  464.         ColorLookup := TFastColorLookup.Create(Palette);
  465.       end;
  466.       // Nothing to do if palette doesn't contain any colors
  467.       if (ColorLookup.Colors = 0) then
  468.         exit;
  469.       // Create a ditherer based on current options
  470.       case (DitherMode) of
  471.         dmNearest:
  472.           Ditherer := TDitherEngine.Create(Bitmap.Width, ColorLookup);
  473.         dmFloydSteinberg:
  474.           Ditherer := TFloydSteinbergDitherer.Create(Bitmap.Width, ColorLookup);
  475.         dmStucki:
  476.           Ditherer := TStuckiDitherer.Create(Bitmap.Width, ColorLookup);
  477.         dmSierra:
  478.           Ditherer := TSierraDitherer.Create(Bitmap.Width, ColorLookup);
  479.         dmJaJuNI:
  480.           Ditherer := TJaJuNIDitherer.Create(Bitmap.Width, ColorLookup);
  481.         dmSteveArche:
  482.           Ditherer := TSteveArcheDitherer.Create(Bitmap.Width, ColorLookup);
  483.         dmBurkes:
  484.           Ditherer := TBurkesDitherer.Create(Bitmap.Width, ColorLookup);
  485.       else
  486.         exit;
  487.       end;
  488.       // The processed bitmap is returned in pf8bit format
  489.       DIBResult := TDIBWriter.Create(Result, pf8bit, Bitmap.Width, Bitmap.Height,
  490.         Palette);
  491.       // Process the image
  492.       Row := 0;
  493.       while (Row < Bitmap.Height) do
  494.       begin
  495.         SrcScanline := DIBSource.ScanLine[Row];
  496.         DstScanline := DIBResult.ScanLine[Row];
  497.         Src := pointer(longInt(SrcScanLine) + Ditherer.Column*sizeof(TRGBTriple));
  498.         Dst := pointer(longInt(DstScanLine) + Ditherer.Column);
  499.         while (Ditherer.Column < Ditherer.Width) and (Ditherer.Column >= 0) do
  500.         begin
  501.           BGR := Src^;
  502.           // Dither and map a single pixel
  503.           Dst^ := Ditherer.Dither(BGR.rgbtRed, BGR.rgbtGreen, BGR.rgbtBlue,
  504.             BGR.rgbtRed, BGR.rgbtGreen, BGR.rgbtBlue);
  505.           inc(Src, Ditherer.Direction);
  506.           inc(Dst, Ditherer.Direction);
  507.         end;
  508.         Inc(Row);
  509.         Ditherer.NextLine;
  510.       end;
  511.     finally
  512.       if (ColorLookup <> nil) then
  513.         ColorLookup.Free;
  514.       if (Ditherer <> nil) then
  515.         Ditherer.Free;
  516.       if (DIBResult <> nil) then
  517.         DIBResult.Free;
  518.       if (DIBSource <> nil) then
  519.         DIBSource.Free;
  520.       // Must delete palette after TDIBWriter since TDIBWriter uses palette 
  521.       if (Palette <> 0) then
  522.         DeleteObject(Palette);
  523.     end;
  524.   except
  525.     Result.Free;
  526.     raise;
  527.   end;
  528. {$ifdef DEBUG_DITHERPERFORMANCE}
  529.   TimeStop := timeGetTime;
  530.   ShowMessage(format('Dithered %d pixels in %d mS, Rate %d pixels/mS (%d pixels/S)',
  531.     [Bitmap.Height*Bitmap.Width, TimeStop-TimeStart,
  532.     MulDiv(Bitmap.Height, Bitmap.Width, TimeStop-TimeStart+1),
  533.     MulDiv(Bitmap.Height, Bitmap.Width * 1000, TimeStop-TimeStart+1)]));
  534.   timeEndPeriod(5);
  535. {$endif}
  536. end;
  537. {$IFDEF R_PLUS}
  538.   {$RANGECHECKS ON}
  539.   {$UNDEF R_PLUS}
  540. {$ENDIF}
  541. ////////////////////////////////////////////////////////////////////////////////
  542. //
  543. // TGIFColorMap
  544. //
  545. ////////////////////////////////////////////////////////////////////////////////
  546. const
  547.   InitColorMapSize = 16;
  548.   DeltaColorMapSize = 32;
  549. //: Creates an instance of a TGIFColorMap object.
  550. constructor TGIFColorMap.Create;
  551. begin
  552.   inherited Create;
  553.   FColorMap := nil;
  554.   FCapacity := 0;
  555.   FCount := 0;
  556.   FOptimized := False;
  557. end;
  558. //: Destroys an instance of a TGIFColorMap object.
  559. destructor TGIFColorMap.Destroy;
  560. begin
  561.   Clear;
  562.   Changed;
  563.   inherited Destroy;
  564. end;
  565. //: Empties the color map.
  566. procedure TGIFColorMap.Clear;
  567. begin
  568.   if (FColorMap <> nil) then
  569.     FreeMem(FColorMap);
  570.   FColorMap := nil;
  571.   FCapacity := 0;
  572.   FCount := 0;
  573.   FOptimized := False;
  574. end;
  575. //: Converts a Windows color value to a RGB value.
  576. class function TGIFColorMap.Color2RGB(Color: TColor): TGIFColor;
  577. begin
  578.   Result.Blue := (Color shr 16) and $FF;
  579.   Result.Green := (Color shr 8) and $FF;
  580.   Result.Red  := Color and $FF;
  581. end;
  582. //: Converts a RGB value to a Windows color value.
  583. class function TGIFColorMap.RGB2Color(Color: TGIFColor): TColor;
  584. begin
  585.   Result := (Color.Blue SHL 16) OR (Color.Green SHL 8) OR Color.Red;
  586. end;
  587. //: Saves the color map to a stream.
  588. procedure TGIFColorMap.SaveToStream(Stream: TStream);
  589. var
  590.   Dummies : integer;
  591.   Dummy : TGIFColor;
  592. begin
  593.   if (FCount = 0) then
  594.     exit;
  595.   Stream.WriteBuffer(FColorMap^, FCount*sizeof(TGIFColor));
  596.   Dummies := (1 SHL BitsPerPixel)-FCount;
  597.   Dummy.Red := 0;
  598.   Dummy.Green := 0;
  599.   Dummy.Blue := 0;
  600.   while (Dummies > 0) do
  601.   begin
  602.     Stream.WriteBuffer(Dummy, sizeof(TGIFColor));
  603.     dec(Dummies);
  604.   end;
  605. end;
  606. //: Loads the color map from a stream.
  607. procedure TGIFColorMap.LoadFromStream(Stream: TStream; Count: integer);
  608. begin
  609.   Clear;
  610.   SetCapacity(Count);
  611.   ReadCheck(Stream, FColorMap^, Count*sizeof(TGIFColor));
  612.   FCount := Count;
  613. end;
  614. //: Returns the position of a color in the color map.
  615. function TGIFColorMap.IndexOf(Color: TColor): integer;
  616. var
  617.   RGB : TGIFColor;
  618. begin
  619.   RGB := Color2RGB(Color);
  620.   if (FOptimized) then
  621.   begin
  622.     // Optimized palette has most frequently occuring entries first
  623.     Result := 0;
  624.     // Reverse search to (hopefully) check latest colors first
  625.     while (Result < FCount) do
  626.       with (FColorMap^[Result]) do
  627.       begin
  628.         if (RGB.Red = Red) and (RGB.Green = Green) and (RGB.Blue = Blue) then
  629.           exit;
  630.         Inc(Result);
  631.       end;
  632.     Result := -1;
  633.   end else
  634.   begin
  635.     Result := FCount-1;
  636.     // Reverse search to (hopefully) check latest colors first
  637.     while (Result >= 0) do
  638.       with (FColorMap^[Result]) do
  639.       begin
  640.         if (RGB.Red = Red) and (RGB.Green = Green) and (RGB.Blue = Blue) then
  641.           exit;
  642.         Dec(Result);
  643.       end;
  644.   end;
  645. end;
  646. procedure TGIFColorMap.SetCapacity(Size: integer);
  647. begin
  648.   if (Size >= FCapacity) then
  649.   begin
  650.     if (Size <= InitColorMapSize) then
  651.       FCapacity := InitColorMapSize
  652.     else
  653.       FCapacity := (Size + DeltaColorMapSize - 1) DIV DeltaColorMapSize * DeltaColorMapSize;
  654.     if (FCapacity > GIFMaxColors) then
  655.       FCapacity := GIFMaxColors;
  656.     ReallocMem(FColorMap, FCapacity * sizeof(TGIFColor));
  657.   end;
  658. end;
  659. //: Imports a Windows palette into the color map.
  660. procedure TGIFColorMap.ImportPalette(Palette: HPalette);
  661. type
  662.   PalArray =  array[byte] of TPaletteEntry;
  663. var
  664.   Pal : PalArray;
  665.   NewCount : integer;
  666.   i : integer;
  667. begin
  668.   Clear;
  669.   NewCount := GetPaletteEntries(Palette, 0, 256, pal);
  670.   if (NewCount = 0) then
  671.     exit;
  672.   SetCapacity(NewCount);
  673.   for i := 0 to NewCount-1 do
  674.     with FColorMap[i], Pal[i] do
  675.     begin
  676.       Red := peRed;
  677.       Green := peGreen;
  678.       Blue := peBlue;
  679.     end;
  680.   FCount := NewCount;
  681.   Changed;
  682. end;
  683. //: Imports a color map structure into the color map.
  684. procedure TGIFColorMap.ImportColorMap(Map: TColorMap; Count: integer);
  685. begin
  686.   Clear;
  687.   if (Count = 0) then
  688.     exit;
  689.   SetCapacity(Count);
  690.   FCount := Count;
  691.   System.Move(Map, FColorMap^, FCount * sizeof(TGIFColor));
  692.   Changed;
  693. end;
  694. //: Imports a Windows palette structure into the color map.
  695. procedure TGIFColorMap.ImportColorTable(Pal: pointer; Count: integer);
  696. var
  697.   i : integer;
  698. begin
  699.   Clear;
  700.   if (Count = 0) then
  701.     exit;
  702.   SetCapacity(Count);
  703.   for i := 0 to Count-1 do
  704.     with FColorMap[i], PRGBQuadArray(Pal)[i] do
  705.     begin
  706.       Red := rgbRed;
  707.       Green := rgbGreen;
  708.       Blue := rgbBlue;
  709.     end;
  710.   FCount := Count;
  711.   Changed;
  712. end;
  713. //: Imports the color table of a DIB into the color map.
  714. procedure TGIFColorMap.ImportDIBColors(Handle: HDC);
  715. var
  716.   Pal : Pointer;
  717.   NewCount : integer;
  718. begin
  719.   Clear;
  720.   GetMem(Pal, sizeof(TRGBQuad) * 256);
  721.   try
  722.     NewCount := GetDIBColorTable(Handle, 0, 256, Pal^);
  723.     ImportColorTable(Pal, NewCount);
  724.   finally
  725.     FreeMem(Pal);
  726.   end;
  727.   Changed;
  728. end;
  729. //: Creates a Windows palette from the color map.
  730. function TGIFColorMap.ExportPalette: HPalette;
  731. var
  732.   Pal : TMaxLogPalette;
  733.   i : Integer;
  734. begin
  735.   if (Count = 0) then
  736.   begin
  737.     Result := 0;
  738.     exit;
  739.   end;
  740.   Pal.palVersion := $300;
  741.   Pal.palNumEntries := Count;
  742.   for i := 0 to Count-1 do
  743.     with FColorMap[i], Pal.palPalEntry[i] do
  744.     begin
  745.       peRed := Red;
  746.       peGreen := Green;
  747.       peBlue := Blue;
  748.       peFlags := PC_NOCOLLAPSE; { TODO -oanme -cImprovement : Verify that PC_NOCOLLAPSE is the correct value to use. }
  749.     end;
  750.   Result := CreatePalette(PLogPalette(@Pal)^);
  751. end;
  752. //: Adds a color to the color map.
  753. function TGIFColorMap.Add(Color: TColor): integer;
  754. begin
  755.   if (FCount >= GIFMaxColors) then
  756.     // Color map full
  757.     Error(sTooManyColors);
  758.   Result := FCount;
  759.   if (Result >= FCapacity) then
  760.     SetCapacity(FCount+1);
  761.   FColorMap^[FCount] := Color2RGB(Color);
  762.   inc(FCount);
  763.   FOptimized := False;
  764.   Changed;
  765. end;
  766. function TGIFColorMap.AddUnique(Color: TColor): integer;
  767. begin
  768.   // Look up color before add (same as IndexOf)
  769.   Result := IndexOf(Color);
  770.   if (Result >= 0) then
  771.     // Color already in map
  772.     exit;
  773.   Result := Add(Color);
  774. end;
  775. //: Removes a color from the color map.
  776. procedure TGIFColorMap.Delete(Index: integer);
  777. begin
  778.   if (Index < 0) or (Index >= FCount) then
  779.     // Color index out of range
  780.     Error(sBadColorIndex);
  781.   dec(FCount);
  782.   if (Index < FCount) then
  783.     System.Move(FColorMap^[Index + 1], FColorMap^[Index], (FCount - Index)* sizeof(TGIFColor));
  784.   FOptimized := False;
  785.   Changed;
  786. end;
  787. function TGIFColorMap.GetColor(Index: integer): TColor;
  788. begin
  789.   if (Index < 0) or (Index >= FCount) then
  790.   begin
  791.     // Color index out of range
  792.     Warning(gsWarning, sBadColorIndex);
  793.     // Raise an exception if the color map is empty
  794.     if (FCount = 0) then
  795.       Error(sEmptyColorMap);
  796.     // Default to color index 0
  797.     Index := 0;
  798.   end;
  799.   Result := RGB2Color(FColorMap^[Index]);
  800. end;
  801. procedure TGIFColorMap.SetColor(Index: integer; Value: TColor);
  802. begin
  803.   if (Index < 0) or (Index >= FCount) then
  804.     // Color index out of range
  805.     Error(sBadColorIndex);
  806.   FColorMap^[Index] := Color2RGB(Value);
  807.   Changed;
  808. end;
  809. function TGIFColorMap.DoOptimize: boolean;
  810. var
  811.   Usage : TColormapHistogram;
  812.   TempMap : array[0..255] of TGIFColor;
  813.   ReverseMap : TColormapReverse;
  814.   i : integer;
  815.   LastFound : boolean;
  816.   NewCount : integer;
  817.   T : TUsageCount;
  818.   Pivot : integer;
  819.   procedure QuickSort(iLo, iHi: Integer);
  820.   var
  821.     Lo, Hi: Integer;
  822.   begin
  823.     repeat
  824.       Lo := iLo;
  825.       Hi := iHi;
  826.       Pivot := Usage[(iLo + iHi) SHR 1].Count;
  827.       repeat
  828.         while (Usage[Lo].Count - Pivot > 0) do inc(Lo);
  829.         while (Usage[Hi].Count - Pivot < 0) do dec(Hi);
  830.         if (Lo <= Hi) then
  831.         begin
  832.           T := Usage[Lo];
  833.           Usage[Lo] := Usage[Hi];
  834.           Usage[Hi] := T;
  835.           inc(Lo);
  836.           dec(Hi);
  837.         end;
  838.       until (Lo > Hi);
  839.       if (iLo < Hi) then
  840.         QuickSort(iLo, Hi);
  841.       iLo := Lo;
  842.     until (Lo >= iHi);
  843.   end;
  844. begin
  845.   if (FCount <= 1) then
  846.   begin
  847.     Result := False;
  848.     exit;
  849.   end;
  850.   FOptimized := True;
  851.   Result := True;
  852.   BuildHistogram(Usage);
  853.   (*
  854.   **  Sort according to usage count
  855.   *)
  856.   QuickSort(0, FCount-1);
  857.   (*
  858.   ** Test for table already sorted
  859.   *)
  860.   for i := 0 to FCount-1 do
  861.     if (Usage[i].Index <> i) then
  862.       break;
  863.   if (i = FCount) then
  864.     exit;
  865.   (*
  866.   ** Build old to new map
  867.   *)
  868.   for i := 0 to FCount-1 do
  869.     ReverseMap[Usage[i].Index] := i;
  870.   MapImages(ReverseMap);
  871.   (*
  872.   **  Reorder colormap
  873.   *)
  874.   LastFound := False;
  875.   NewCount := FCount;
  876.   Move(FColorMap^, TempMap, FCount * sizeof(TGIFColor));
  877.   for i := 0 to FCount-1 do
  878.   begin
  879.     FColorMap^[ReverseMap[i]] := TempMap[i];
  880.     // Find last used color index
  881.     if (Usage[i].Count = 0) and not(LastFound) then
  882.     begin
  883.       LastFound := True;
  884.       NewCount := i;
  885.     end;
  886.   end;
  887.   FCount := NewCount;
  888.   Changed;
  889. end;
  890. function TGIFColorMap.GetBitsPerPixel: integer;
  891. begin
  892.   Result := Colors2bpp(FCount);
  893. end;
  894. //: Copies one color map to another.
  895. procedure TGIFColorMap.Assign(Source: TPersistent);
  896. begin
  897.   if (Source is TGIFColorMap) then
  898.   begin
  899.     Clear;
  900.     FCapacity := TGIFColorMap(Source).FCapacity;
  901.     FCount := TGIFColorMap(Source).FCount;
  902.     FOptimized := TGIFColorMap(Source).FOptimized;
  903.     FColorMap := AllocMem(FCapacity * sizeof(TGIFColor));
  904.     System.Move(TGIFColorMap(Source).FColorMap^, FColorMap^, FCount * sizeof(TGIFColor));
  905.     Changed;
  906.   end else
  907.     inherited Assign(Source);
  908. end;
  909. ////////////////////////////////////////////////////////////////////////////////
  910. //
  911. // TGIFItem
  912. //
  913. ////////////////////////////////////////////////////////////////////////////////
  914. constructor TGIFItem.Create(GIFImage: TGIFImage);
  915. begin
  916.   inherited Create;
  917.   FGIFImage := GIFImage;
  918. end;
  919. procedure TGIFItem.Warning(Severity: TGIFSeverity; Message: string);
  920. begin
  921.   FGIFImage.Warning(self, Severity, Message);
  922. end;
  923. function TGIFItem.GetVersion: TGIFVersion;
  924. begin
  925.   Result := gv87a;
  926. end;
  927. procedure TGIFItem.LoadFromFile(const Filename: string);
  928. var
  929.   Stream: TStream;
  930. begin
  931.   Stream := TFileStream.Create(Filename, fmOpenRead OR fmShareDenyWrite);
  932.   try
  933.     LoadFromStream(Stream);
  934.   finally
  935.     Stream.Free;
  936.   end;
  937. end;
  938. procedure TGIFItem.SaveToFile(const Filename: string);
  939. var
  940.   Stream: TStream;
  941. begin
  942.   Stream := TFileStream.Create(Filename, fmCreate);
  943.   try
  944.     SaveToStream(Stream);
  945.   finally
  946.     Stream.Free;
  947.   end;
  948. end;
  949. ////////////////////////////////////////////////////////////////////////////////
  950. //
  951. // TGIFList
  952. //
  953. ////////////////////////////////////////////////////////////////////////////////
  954. constructor TGIFList.Create(Image: TGIFImage);
  955. begin
  956.   inherited Create;
  957.   FImage := Image;
  958.   FItems := TList.Create;
  959. end;
  960. destructor TGIFList.Destroy;
  961. begin
  962.   Clear;
  963.   FItems.Free;
  964.   inherited Destroy;
  965. end;
  966. function TGIFList.GetItem(Index: Integer): TGIFItem;
  967. begin
  968.   Result := TGIFItem(FItems[Index]);
  969. end;
  970. procedure TGIFList.SetItem(Index: Integer; Item: TGIFItem);
  971. begin
  972.   FItems[Index] := Item;
  973. end;
  974. function TGIFList.GetCount: Integer;
  975. begin
  976.   Result := FItems.Count;
  977. end;
  978. function TGIFList.Add(Item: TGIFItem): Integer;
  979. begin
  980.   Result := FItems.Add(Item);
  981. end;
  982. procedure TGIFList.Clear;
  983. begin
  984.   while (FItems.Count > 0) do
  985.     Delete(0);
  986. end;
  987. procedure TGIFList.Delete(Index: Integer);
  988. var
  989.   Item : TGIFItem;
  990. begin
  991.   Item := TGIFItem(FItems[Index]);
  992.   // Delete before item is destroyed to avoid recursion
  993.   FItems.Delete(Index);
  994.   Item.Free;
  995. end;
  996. procedure TGIFList.Exchange(Index1, Index2: Integer);
  997. begin
  998.   FItems.Exchange(Index1, Index2);
  999. end;
  1000. function TGIFList.First: TGIFItem;
  1001. begin
  1002.   Result := TGIFItem(FItems.First);
  1003. end;
  1004. function TGIFList.IndexOf(Item: TGIFItem): Integer;
  1005. begin
  1006.   Result := FItems.IndexOf(Item);
  1007. end;
  1008. procedure TGIFList.Insert(Index: Integer; Item: TGIFItem);
  1009. begin
  1010.   FItems.Insert(Index, Item);
  1011. end;
  1012. function TGIFList.Last: TGIFItem;
  1013. begin
  1014.   Result := TGIFItem(FItems.Last);
  1015. end;
  1016. procedure TGIFList.Move(CurIndex, NewIndex: Integer);
  1017. begin
  1018.   FItems.Move(CurIndex, NewIndex);
  1019. end;
  1020. function TGIFList.Remove(Item: TGIFItem): Integer;
  1021. begin
  1022.   // Note: TGIFList.Remove must not destroy item
  1023.   Result := FItems.Remove(Item);
  1024. end;
  1025. procedure TGIFList.SaveToStream(Stream: TStream);
  1026. var
  1027.   i : integer;
  1028. begin
  1029.   for i := 0 to FItems.Count-1 do
  1030.     TGIFItem(FItems[i]).SaveToStream(Stream);
  1031. end;
  1032. procedure TGIFList.Warning(Severity: TGIFSeverity; Message: string);
  1033. begin
  1034.   Image.Warning(self, Severity, Message);
  1035. end;
  1036. ////////////////////////////////////////////////////////////////////////////////
  1037. //
  1038. // TGIFGlobalColorMap
  1039. //
  1040. ////////////////////////////////////////////////////////////////////////////////
  1041. type
  1042.   TGIFGlobalColorMap = class(TGIFColorMap)
  1043.   private
  1044.     FHeader : TGIFHeader;
  1045.   protected
  1046.     procedure Warning(Severity: TGIFSeverity; Message: string); override;
  1047.     procedure BuildHistogram(var Histogram: TColormapHistogram); override;
  1048.     procedure MapImages(var Map: TColormapReverse); override;
  1049.   public
  1050.     constructor Create(HeaderItem: TGIFHeader);
  1051.     function Optimize: boolean; override;
  1052.     procedure Changed; override;
  1053.   end;
  1054. constructor TGIFGlobalColorMap.Create(HeaderItem: TGIFHeader);
  1055. begin
  1056.   Inherited Create;
  1057.   FHeader := HeaderItem;
  1058. end;
  1059. procedure TGIFGlobalColorMap.Warning(Severity: TGIFSeverity; Message: string);
  1060. begin
  1061.   FHeader.Image.Warning(self, Severity, Message);
  1062. end;
  1063. procedure TGIFGlobalColorMap.BuildHistogram(var Histogram: TColormapHistogram);
  1064. var
  1065.   Pixel ,
  1066.   LastPixel : PChar;
  1067.   i : integer;
  1068. begin
  1069.   (*
  1070.   ** Init histogram
  1071.   *)
  1072.   for i := 0 to Count-1 do
  1073.   begin
  1074.     Histogram[i].Index := i;
  1075.     Histogram[i].Count := 0;
  1076.   end;
  1077.   for i := 0 to FHeader.Image.Images.Count-1 do
  1078.     if (FHeader.Image.Images[i].ActiveColorMap = self) then
  1079.     begin
  1080.       Pixel := FHeader.Image.Images[i].Data;
  1081.       LastPixel := Pixel + FHeader.Image.Images[i].Width * FHeader.Image.Images[i].Height;
  1082.       (*
  1083.       ** Sum up usage count for each color
  1084.       *)
  1085.       while (Pixel < LastPixel) do
  1086.       begin
  1087.         inc(Histogram[ord(Pixel^)].Count);
  1088.         inc(Pixel);
  1089.       end;
  1090.     end;
  1091. end;
  1092. procedure TGIFGlobalColorMap.MapImages(var Map: TColormapReverse);
  1093. var
  1094.   Pixel ,
  1095.   LastPixel : PChar;
  1096.   i : integer;
  1097. begin
  1098.   for i := 0 to FHeader.Image.Images.Count-1 do
  1099.     if (FHeader.Image.Images[i].ActiveColorMap = self) then
  1100.     begin
  1101.       Pixel := FHeader.Image.Images[i].Data;
  1102.       LastPixel := Pixel + FHeader.Image.Images[i].Width * FHeader.Image.Images[i].Height;
  1103.       (*
  1104.       **  Reorder all pixel to new map
  1105.       *)
  1106.       while (Pixel < LastPixel) do
  1107.       begin
  1108.         Pixel^ := chr(Map[ord(Pixel^)]);
  1109.         inc(Pixel);
  1110.       end;
  1111.       (*
  1112.       **  Reorder transparent colors
  1113.       *)
  1114.       if (FHeader.Image.Images[i].Transparent) then
  1115.         FHeader.Image.Images[i].GraphicControlExtension.TransparentColorIndex :=
  1116.           Map[FHeader.Image.Images[i].GraphicControlExtension.TransparentColorIndex];
  1117.     end;
  1118. end;
  1119. function TGIFGlobalColorMap.Optimize: boolean;
  1120. begin
  1121.   { Optimize with first image, Remove unused colors if only one image }
  1122.   if (FHeader.Image.Images.Count > 0) then
  1123.     Result := DoOptimize
  1124.   else
  1125.     Result := False;
  1126. end;
  1127. procedure TGIFGlobalColorMap.Changed;
  1128. begin
  1129.   FHeader.Image.Palette := 0;
  1130. end;
  1131. ////////////////////////////////////////////////////////////////////////////////
  1132. //
  1133. // TGIFHeader
  1134. //
  1135. ////////////////////////////////////////////////////////////////////////////////
  1136. constructor TGIFHeader.Create(GIFImage: TGIFImage);
  1137. begin
  1138.   inherited Create(GIFImage);
  1139.   FColorMap := TGIFGlobalColorMap.Create(self);
  1140.   Clear;
  1141. end;
  1142. destructor TGIFHeader.Destroy;
  1143. begin
  1144.   FColorMap.Free;
  1145.   inherited Destroy;
  1146. end;
  1147. procedure TGIFHeader.Clear;
  1148. begin
  1149.   FColorMap.Clear;
  1150.   FLogicalScreenDescriptor.ScreenWidth := 0;
  1151.   FLogicalScreenDescriptor.ScreenHeight := 0;
  1152.   FLogicalScreenDescriptor.PackedFields := 0;
  1153.   FLogicalScreenDescriptor.BackgroundColorIndex := 0;
  1154.   FLogicalScreenDescriptor.AspectRatio := 0;
  1155. end;
  1156. procedure TGIFHeader.Assign(Source: TPersistent);
  1157. begin
  1158.   if (Source is TGIFHeader) then
  1159.   begin
  1160.     ColorMap.Assign(TGIFHeader(Source).ColorMap);
  1161.     FLogicalScreenDescriptor := TGIFHeader(Source).FLogicalScreenDescriptor;
  1162.   end else
  1163.   if (Source is TGIFColorMap) then
  1164.   begin
  1165.     Clear;
  1166.     ColorMap.Assign(TGIFColorMap(Source));
  1167.   end else
  1168.     inherited Assign(Source);
  1169. end;
  1170. type
  1171.   TGIFHeaderRec = packed record
  1172.     Signature: array[0..2] of char; { contains 'GIF' }
  1173.     Version: TGIFVersionRec;   { '87a' or '89a' }
  1174.   end;
  1175. const
  1176.   { logical screen descriptor packed field masks }
  1177.   lsdGlobalColorTable = $80; { set if global color table follows L.S.D. }
  1178.   lsdColorResolution = $70; { Color resolution - 3 bits }
  1179.   lsdSort = $08; { set if global color table is sorted - 1 bit }
  1180.   lsdColorTableSize = $07; { size of global color table - 3 bits }
  1181.    { Actual size = 2^value+1    - value is 3 bits }
  1182. procedure TGIFHeader.Prepare;
  1183. var
  1184.   pack : BYTE;
  1185. begin
  1186.   Pack := $00;
  1187.   if (ColorMap.Count > 0) then
  1188.   begin
  1189.     Pack := lsdGlobalColorTable;
  1190.     if (ColorMap.Optimized) then
  1191.       Pack := Pack OR lsdSort;
  1192.   end;
  1193.   // Note: The SHL below was SHL 5 in the original source, but that looks wrong
  1194.   Pack := Pack OR ((Image.ColorResolution SHL 4) AND lsdColorResolution);
  1195.   Pack := Pack OR ((Image.BitsPerPixel-1) AND lsdColorTableSize);
  1196.   FLogicalScreenDescriptor.PackedFields := Pack;
  1197. end;
  1198. procedure TGIFHeader.SaveToStream(Stream: TStream);
  1199. var
  1200.   GifHeader : TGIFHeaderRec;
  1201.   v : TGIFVersion;
  1202. begin
  1203.   v := Image.Version;
  1204.   if (v = gvUnknown) then
  1205.     Error(sBadVersion);
  1206.   GifHeader.Signature := 'GIF';
  1207.   GifHeader.Version := GIFVersions[v];
  1208.   Prepare;
  1209.   Stream.Write(GifHeader, sizeof(GifHeader));
  1210.   Stream.Write(FLogicalScreenDescriptor, sizeof(FLogicalScreenDescriptor));
  1211.   if (FLogicalScreenDescriptor.PackedFields AND lsdGlobalColorTable = lsdGlobalColorTable) then
  1212.     ColorMap.SaveToStream(Stream);
  1213. end;
  1214. procedure TGIFHeader.LoadFromStream(Stream: TStream);
  1215. var
  1216.   GifHeader : TGIFHeaderRec;
  1217.   ColorCount : integer;
  1218.   Position : integer;
  1219. begin
  1220.   Position := Stream.Position;
  1221.   ReadCheck(Stream, GifHeader, sizeof(GifHeader));
  1222.   if (uppercase(GifHeader.Signature) <> 'GIF') then
  1223.   begin
  1224.     // Attempt recovery in case we are reading a GIF stored in a form by rxLib
  1225.     Stream.Position := Position;
  1226.     // Seek past size stored in stream
  1227.     Stream.Seek(sizeof(longInt), soFromCurrent);
  1228.     // Attempt to read signature again
  1229.     ReadCheck(Stream, GifHeader, sizeof(GifHeader));
  1230.     if (uppercase(GifHeader.Signature) <> 'GIF') then
  1231.       Error(sBadSignature);
  1232.   end;
  1233.   ReadCheck(Stream, FLogicalScreenDescriptor, sizeof(FLogicalScreenDescriptor));
  1234.   if (FLogicalScreenDescriptor.PackedFields AND lsdGlobalColorTable = lsdGlobalColorTable) then
  1235.   begin
  1236.     ColorCount := 2 SHL (FLogicalScreenDescriptor.PackedFields AND lsdColorTableSize);
  1237.     if (ColorCount < 2) or (ColorCount > 256) then
  1238.       Error(sScreenBadColorSize);
  1239.     ColorMap.LoadFromStream(Stream, ColorCount)
  1240.   end else
  1241.     ColorMap.Clear;
  1242. end;
  1243. function TGIFHeader.GetVersion: TGIFVersion;
  1244. begin
  1245.   if (FColorMap.Optimized) or (AspectRatio <> 0) then
  1246.     Result := gv89a
  1247.   else
  1248.     Result := inherited GetVersion;
  1249. end;
  1250. function TGIFHeader.GetBackgroundColor: TColor;
  1251. begin
  1252.   Result := FColorMap[BackgroundColorIndex];
  1253. end;
  1254. procedure TGIFHeader.SetBackgroundColor(Color: TColor);
  1255. begin
  1256.   BackgroundColorIndex := FColorMap.AddUnique(Color);
  1257. end;
  1258. procedure TGIFHeader.SetBackgroundColorIndex(Index: BYTE);
  1259. begin
  1260.   if ((Index >= FColorMap.Count) and (FColorMap.Count > 0)) then
  1261.   begin
  1262.     Warning(gsWarning, sBadColorIndex);
  1263.     Index := 0;
  1264.   end;
  1265.   FLogicalScreenDescriptor.BackgroundColorIndex := Index;
  1266. end;
  1267. function TGIFHeader.GetBitsPerPixel: integer;
  1268. begin
  1269.   Result := FColorMap.BitsPerPixel;
  1270. end;
  1271. function TGIFHeader.GetColorResolution: integer;
  1272. begin
  1273.   Result := FColorMap.BitsPerPixel-1;
  1274. end;
  1275. ////////////////////////////////////////////////////////////////////////////////
  1276. //
  1277. // TGIFLocalColorMap
  1278. //
  1279. ////////////////////////////////////////////////////////////////////////////////
  1280. type
  1281.   TGIFLocalColorMap = class(TGIFColorMap)
  1282.   private
  1283.     FSubImage : TGIFSubImage;
  1284.   protected
  1285.     procedure Warning(Severity: TGIFSeverity; Message: string); override;
  1286.     procedure BuildHistogram(var Histogram: TColormapHistogram); override;
  1287.     procedure MapImages(var Map: TColormapReverse); override;
  1288.   public
  1289.     constructor Create(SubImage: TGIFSubImage);
  1290.     function Optimize: boolean; override;
  1291.     procedure Changed; override;
  1292.   end;
  1293. constructor TGIFLocalColorMap.Create(SubImage: TGIFSubImage);
  1294. begin
  1295.   Inherited Create;
  1296.   FSubImage := SubImage;
  1297. end;
  1298. procedure TGIFLocalColorMap.Warning(Severity: TGIFSeverity; Message: string);
  1299. begin
  1300.   FSubImage.Image.Warning(self, Severity, Message);
  1301. end;
  1302. procedure TGIFLocalColorMap.BuildHistogram(var Histogram: TColormapHistogram);
  1303. var
  1304.   Pixel ,
  1305.   LastPixel : PChar;
  1306.   i : integer;
  1307. begin
  1308.   Pixel := FSubImage.Data;
  1309.   LastPixel := Pixel + FSubImage.Width * FSubImage.Height;
  1310.   (*
  1311.   ** Init histogram
  1312.   *)
  1313.   for i := 0 to Count-1 do
  1314.   begin
  1315.     Histogram[i].Index := i;
  1316.     Histogram[i].Count := 0;
  1317.   end;
  1318.   (*
  1319.   ** Sum up usage count for each color
  1320.   *)
  1321.   while (Pixel < LastPixel) do
  1322.   begin
  1323.     inc(Histogram[ord(Pixel^)].Count);
  1324.     inc(Pixel);
  1325.   end;
  1326. end;
  1327. procedure TGIFLocalColorMap.MapImages(var Map: TColormapReverse);
  1328. var
  1329.   Pixel ,
  1330.   LastPixel : PChar;
  1331. begin
  1332.   Pixel := FSubImage.Data;
  1333.   LastPixel := Pixel + FSubImage.Width * FSubImage.Height;
  1334.   (*
  1335.   **  Reorder all pixel to new map
  1336.   *)
  1337.   while (Pixel < LastPixel) do
  1338.   begin
  1339.     Pixel^ := chr(Map[ord(Pixel^)]);
  1340.     inc(Pixel);
  1341.   end;
  1342.   (*
  1343.   **  Reorder transparent colors
  1344.   *)
  1345.   if (FSubImage.Transparent) then
  1346.     FSubImage.GraphicControlExtension.TransparentColorIndex :=
  1347.       Map[FSubImage.GraphicControlExtension.TransparentColorIndex];
  1348. end;
  1349. function TGIFLocalColorMap.Optimize: boolean;
  1350. begin
  1351.   Result := DoOptimize;
  1352. end;
  1353. procedure TGIFLocalColorMap.Changed;
  1354. begin
  1355.   FSubImage.Palette := 0;
  1356. end;
  1357. ////////////////////////////////////////////////////////////////////////////////
  1358. //
  1359. // LZW Decoder
  1360. //
  1361. ////////////////////////////////////////////////////////////////////////////////
  1362. const
  1363.   GIFCodeBits = 12; // Max number of bits per GIF token code
  1364.   GIFCodeMax = (1 SHL GIFCodeBits)-1;// Max GIF token code
  1365.    // 12 bits = 4095
  1366.   StackSize = (2 SHL GIFCodeBits); // Size of decompression stack
  1367.   TableSize = (1 SHL GIFCodeBits); // Size of decompression table
  1368. procedure TGIFSubImage.Decompress(Stream: TStream);
  1369. var
  1370.   table0 : array[0..TableSize-1] of integer;
  1371.   table1 : array[0..TableSize-1] of integer;
  1372.   firstcode, oldcode : integer;
  1373.   buf : array[0..257] of BYTE;
  1374.   Dest : PChar;
  1375.   v ,
  1376.   xpos, ypos, pass : integer;
  1377.   stack : array[0..StackSize-1] of integer;
  1378.   Source : ^integer;
  1379.   BitsPerCode : integer; // number of CodeTableBits/code
  1380.   InitialBitsPerCode : BYTE;
  1381.   MaxCode : integer; // maximum code, given BitsPerCode
  1382.   MaxCodeSize : integer;
  1383.   ClearCode : integer; // Special code to signal "Clear table"
  1384.   EOFCode : integer; // Special code to signal EOF
  1385.   step : integer;
  1386.   i : integer;
  1387.   StartBit , // Index of bit buffer start
  1388.   LastBit , // Index of last bit in buffer
  1389.   LastByte : integer; // Index of last byte in buffer
  1390.   get_done ,
  1391.   return_clear ,
  1392.   ZeroBlock : boolean;
  1393.   ClearValue : BYTE;
  1394. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  1395.   TimeStartDecompress ,
  1396.   TimeStopDecompress : DWORD;
  1397. {$endif}
  1398.   function nextCode(BitsPerCode: integer): integer;
  1399.   const
  1400.     masks: array[0..15] of integer =
  1401.       ($0000, $0001, $0003, $0007,
  1402.        $000f, $001f, $003f, $007f,
  1403.        $00ff, $01ff, $03ff, $07ff,
  1404.        $0fff, $1fff, $3fff, $7fff);
  1405.   var
  1406.     StartIndex, EndIndex : integer;
  1407.     ret : integer;
  1408.     EndBit : integer;
  1409.     count : BYTE;
  1410.   begin
  1411.     if (return_clear) then
  1412.     begin
  1413.       return_clear := False;
  1414.       Result := ClearCode;
  1415.       exit;
  1416.     end;
  1417.     EndBit := StartBit + BitsPerCode;
  1418.     if (EndBit >= LastBit) then
  1419.     begin
  1420.       if (get_done) then
  1421.       begin
  1422.         if (StartBit >= LastBit) then
  1423.           Warning(gsWarning, sDecodeTooFewBits);
  1424.         Result := -1;
  1425.         exit;
  1426.       end;
  1427.       buf[0] := buf[LastByte-2];
  1428.       buf[1] := buf[LastByte-1];
  1429.       if (Stream.Read(count, 1) <> 1) then
  1430.       begin
  1431.         Result := -1;
  1432.         exit;
  1433.       end;
  1434.       if (count = 0) then
  1435.       begin
  1436.         ZeroBlock := True;
  1437.         get_done := TRUE;
  1438.       end else
  1439.       begin
  1440.         // Handle premature end of file
  1441.         if (Stream.Size - Stream.Position < Count) then
  1442.         begin
  1443.           Warning(gsWarning, sOutOfData);
  1444.           // Not enough data left - Just read as much as we can get
  1445.           Count := Stream.Size - Stream.Position;
  1446.         end;
  1447.         if (Count <> 0) then
  1448.           ReadCheck(Stream, Buf[2], Count);
  1449.       end;
  1450.       LastByte := 2 + count;
  1451.       StartBit := (StartBit - LastBit) + 16;
  1452.       LastBit := LastByte * 8;
  1453.       EndBit := StartBit + BitsPerCode;
  1454.     end;
  1455.     EndIndex := EndBit DIV 8;
  1456.     StartIndex := StartBit DIV 8;
  1457.     ASSERT(StartIndex <= high(buf), 'StartIndex too large');
  1458.     if (StartIndex = EndIndex) then
  1459.       ret := buf[StartIndex]
  1460.     else
  1461.       if (StartIndex + 1 = EndIndex) then
  1462.         ret := buf[StartIndex] OR (buf[StartIndex+1] SHL 8)
  1463.       else
  1464.         ret := buf[StartIndex] OR (buf[StartIndex+1] SHL 8) OR (buf[StartIndex+2] SHL 16);
  1465.     ret := (ret SHR (StartBit AND $0007)) AND masks[BitsPerCode];
  1466.     Inc(StartBit, BitsPerCode);
  1467.     Result := ret;
  1468.   end;
  1469.   function NextLZW: integer;
  1470.   var
  1471.     code, incode : integer;
  1472.     i : integer;
  1473.     b : BYTE;
  1474.   begin
  1475.     code := nextCode(BitsPerCode);
  1476.     while (code >= 0) do
  1477.     begin
  1478.       if (code = ClearCode) then
  1479.       begin
  1480.         ASSERT(ClearCode < TableSize, 'ClearCode too large');
  1481.         for i := 0 to ClearCode-1 do
  1482.         begin
  1483.           table0[i] := 0;
  1484.           table1[i] := i;
  1485.         end;
  1486.         for i := ClearCode to TableSize-1 do
  1487.         begin
  1488.           table0[i] := 0;
  1489.           table1[i] := 0;
  1490.         end;
  1491.         BitsPerCode := InitialBitsPerCode+1;
  1492.         MaxCodeSize := 2 * ClearCode;
  1493.         MaxCode := ClearCode + 2;
  1494.         Source := @stack;
  1495.         repeat
  1496.           firstcode := nextCode(BitsPerCode);
  1497.           oldcode := firstcode;
  1498.         until (firstcode <> ClearCode);
  1499.         Result := firstcode;
  1500.         exit;
  1501.       end;
  1502.       if (code = EOFCode) then
  1503.       begin
  1504.         Result := -2;
  1505.         if (ZeroBlock) then
  1506.           exit;
  1507.         // Eat rest of data blocks
  1508.         if (Stream.Read(b, 1) <> 1) then
  1509.           exit;
  1510.         while (b <> 0) do
  1511.         begin
  1512.           Stream.Seek(b, soFromCurrent);
  1513.           if (Stream.Read(b, 1) <> 1) then
  1514.             exit;
  1515.         end;
  1516.         exit;
  1517.       end;
  1518.       incode := code;
  1519.       if (code >= MaxCode) then
  1520.       begin
  1521.         Source^ := firstcode;
  1522.         Inc(Source);
  1523.         code := oldcode;
  1524.       end;
  1525.       ASSERT(Code < TableSize, 'Code too large');
  1526.       while (code >= ClearCode) do
  1527.       begin
  1528.         Source^ := table1[code];
  1529.         Inc(Source);
  1530.         if (code = table0[code]) then
  1531.           Error(sDecodeCircular);
  1532.         code := table0[code];
  1533.         ASSERT(Code < TableSize, 'Code too large');
  1534.       end;
  1535.       firstcode := table1[code];
  1536.       Source^ := firstcode;
  1537.       Inc(Source);
  1538.       code := MaxCode;
  1539.       if (code <= GIFCodeMax) then
  1540.       begin
  1541.         table0[code] := oldcode;
  1542.         table1[code] := firstcode;
  1543.         Inc(MaxCode);
  1544.         if ((MaxCode >= MaxCodeSize) and (MaxCodeSize <= GIFCodeMax)) then
  1545.         begin
  1546.           MaxCodeSize := MaxCodeSize * 2;
  1547.           Inc(BitsPerCode);
  1548.         end;
  1549.       end;
  1550.       oldcode := incode;
  1551.       if (longInt(Source) > longInt(@stack)) then
  1552.       begin
  1553.         Dec(Source);
  1554.         Result := Source^;
  1555.         exit;
  1556.       end
  1557.     end;
  1558.     Result := code;
  1559.   end;
  1560.   function readLZW: integer;
  1561.   begin
  1562.     if (longInt(Source) > longInt(@stack)) then
  1563.     begin
  1564.       Dec(Source);
  1565.       Result := Source^;
  1566.     end else
  1567.       Result := NextLZW;
  1568.   end;
  1569. begin
  1570.   NewImage;
  1571.   // Clear image data in case decompress doesn't complete
  1572.   if (Transparent) then
  1573.     // Clear to transparent color
  1574.     ClearValue := GraphicControlExtension.GetTransparentColorIndex
  1575.   else
  1576.     // Clear to first color
  1577.     ClearValue := 0;
  1578.   FillChar(FData^, FDataSize, ClearValue);
  1579. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  1580.   TimeStartDecompress := timeGetTime;
  1581. {$endif}
  1582.   (*
  1583.   ** Read initial code size in bits from stream
  1584.   *)
  1585.   if (Stream.Read(InitialBitsPerCode, 1) <> 1) then
  1586.     exit;
  1587.   (*
  1588.   **  Initialize the Compression routines
  1589.   *)
  1590.   BitsPerCode := InitialBitsPerCode + 1;
  1591.   ClearCode := 1 SHL InitialBitsPerCode;
  1592.   EOFCode := ClearCode + 1;
  1593.   MaxCodeSize := 2 * ClearCode;
  1594.   MaxCode := ClearCode + 2;
  1595.   StartBit := 0;
  1596.   LastBit := 0;
  1597.   LastByte := 2;
  1598.   ZeroBlock := False;
  1599.   get_done := False;
  1600.   return_clear := TRUE;
  1601.   Source := @stack;
  1602.   try
  1603.     if (Interlaced) then
  1604.     begin
  1605.       ypos := 0;
  1606.       pass := 0;
  1607.       step := 8;
  1608.       for i := 0 to Height-1 do
  1609.       begin
  1610.         Dest := FData + Width * ypos;
  1611.         for xpos := 0 to width-1 do
  1612.         begin
  1613.           v := readLZW;
  1614.           if (v < 0) then
  1615.             exit;
  1616.           Dest^ := char(v);
  1617.           Inc(Dest);
  1618.         end;
  1619.         Inc(ypos, step);
  1620.         if (ypos >= height) then
  1621.           repeat
  1622.             if (pass > 0) then
  1623.               step := step DIV 2;
  1624.             Inc(pass);
  1625.             ypos := step DIV 2;
  1626.           until (ypos < height);
  1627.       end;
  1628.     end else
  1629.     begin
  1630.       Dest := FData;
  1631.       for ypos := 0 to (height * width)-1 do
  1632.       begin
  1633.         v := readLZW;
  1634.         if (v < 0) then
  1635.           exit;
  1636.         Dest^ := char(v);
  1637.         Inc(Dest);
  1638.       end;
  1639.     end;
  1640.   finally
  1641.     if (readLZW >= 0) then
  1642.       ;
  1643. //      raise GIFException.Create('Too much input data, ignoring extra...');
  1644.   end;
  1645. {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  1646.   TimeStopDecompress := timeGetTime;
  1647.   ShowMessage(format('Decompressed %d pixels in %d mS, Rate %d pixels/mS',
  1648.     [Height*Width, TimeStopDecompress-TimeStartDecompress,
  1649.     (Height*Width) DIV (TimeStopDecompress-TimeStartDecompress+1)]));
  1650. {$endif}
  1651. end;
  1652. ////////////////////////////////////////////////////////////////////////////////
  1653. //
  1654. // LZW Encoder stuff
  1655. //
  1656. ////////////////////////////////////////////////////////////////////////////////
  1657. ////////////////////////////////////////////////////////////////////////////////
  1658. // LZW Encoder THashTable
  1659. ////////////////////////////////////////////////////////////////////////////////
  1660. const
  1661.   HashKeyBits = 13; // Max number of bits per Hash Key
  1662.   HashSize = 8009; // Size of hash table
  1663.    // Must be prime
  1664.                                                 // Must be > than HashMaxCode
  1665.                                                 // Must be < than HashMaxKey
  1666.   HashKeyMax = (1 SHL HashKeyBits)-1;// Max hash key value
  1667.    // 13 bits = 8191
  1668.   HashKeyMask = HashKeyMax; // $1FFF
  1669.   GIFCodeMask = GIFCodeMax; // $0FFF
  1670.   HashEmpty = $000FFFFF; // 20 bits
  1671. type
  1672.   // A Hash Key is 20 bits wide.
  1673.   // - The lower 8 bits are the postfix character (the new pixel).
  1674.   // - The upper 12 bits are the prefix code (the GIF token).
  1675.   // A KeyInt must be able to represent the integer values -1..(2^20)-1
  1676.   KeyInt = longInt; // 32 bits
  1677.   CodeInt = SmallInt; // 16 bits
  1678.   THashArray = array[0..HashSize-1] of KeyInt;
  1679.   PHashArray = ^THashArray;
  1680.   THashTable = class
  1681. {$ifdef DEBUG_HASHPERFORMANCE}
  1682.     CountLookupFound : longInt;
  1683.     CountMissFound : longInt;
  1684.     CountLookupNotFound : longInt;
  1685.     CountMissNotFound : longInt;
  1686. {$endif}
  1687.     HashTable: PHashArray;
  1688.   public
  1689.     constructor Create;
  1690.     destructor Destroy; override;
  1691.     procedure Clear;
  1692.     procedure Insert(Key: KeyInt; Code: CodeInt);
  1693.     function Lookup(Key: KeyInt): CodeInt;
  1694.   end;
  1695. function HashKey(Key: KeyInt): CodeInt;
  1696. begin
  1697.   Result := ((Key SHR (GIFCodeBits-8)) XOR Key) MOD HashSize;
  1698. end;
  1699. function NextHashKey(HKey: CodeInt): CodeInt;
  1700. var
  1701.   disp : CodeInt;
  1702. begin
  1703.   (*
  1704.   ** secondary hash (after G. Knott)
  1705.   *)
  1706.   disp := HashSize - HKey;
  1707.   if (HKey = 0) then
  1708.     disp := 1;
  1709. //  disp := 13; // disp should be prime relative to HashSize, but
  1710. // it doesn't seem to matter here...
  1711.   dec(HKey, disp);
  1712.   if (HKey < 0) then
  1713.     inc(HKey, HashSize);
  1714.   Result := HKey;
  1715. end;
  1716. constructor THashTable.Create;
  1717. begin
  1718.   ASSERT(longInt($FFFFFFFF) = -1, 'TGIFImage implementation assumes $FFFFFFFF = -1');
  1719.   inherited Create;
  1720.   GetMem(HashTable, sizeof(THashArray));
  1721.   Clear;
  1722. {$ifdef DEBUG_HASHPERFORMANCE}
  1723.   CountLookupFound := 0;
  1724.   CountMissFound := 0;
  1725.   CountLookupNotFound := 0;
  1726.   CountMissNotFound := 0;
  1727. {$endif}
  1728. end;
  1729. destructor THashTable.Destroy;
  1730. begin
  1731. {$ifdef DEBUG_HASHPERFORMANCE}
  1732.   ShowMessage(
  1733.     Format('Found: %d  HitRate: %.2f',
  1734.       [CountLookupFound, (CountLookupFound+1)/(CountMissFound+1)])+#13+
  1735.     Format('Not found: %d  HitRate: %.2f',
  1736.       [CountLookupNotFound, (CountLookupNotFound+1)/(CountMissNotFound+1)]));
  1737. {$endif}
  1738.   FreeMem(HashTable);
  1739.   inherited Destroy;
  1740. end;
  1741. // Clear hash table and fill with empty slots (doh!)
  1742. procedure THashTable.Clear;
  1743. {$ifdef DEBUG_HASHFILLFACTOR}
  1744. var
  1745.   i ,
  1746.   Count : longInt;
  1747. {$endif}
  1748. begin
  1749. {$ifdef DEBUG_HASHFILLFACTOR}
  1750.   Count := 0;
  1751.   for i := 0 to HashSize-1 do
  1752.     if (HashTable[i] SHR GIFCodeBits <> HashEmpty) then
  1753.       inc(Count);
  1754.   ShowMessage(format('Size: %d, Filled: %d, Rate %.4f',
  1755.     [HashSize, Count, Count/HashSize]));
  1756. {$endif}
  1757.   FillChar(HashTable^, sizeof(THashArray), $FF);
  1758. end;
  1759. // Insert new key/value pair into hash table
  1760. procedure THashTable.Insert(Key: KeyInt; Code: CodeInt);
  1761. var
  1762.   HKey : CodeInt;
  1763. begin
  1764.   // Create hash key from prefix string
  1765.   HKey := HashKey(Key);
  1766.   // Scan for empty slot
  1767.   // while (HashTable[HKey] SHR GIFCodeBits <> HashEmpty) do { Unoptimized }
  1768.   while (HashTable[HKey] AND (HashEmpty SHL GIFCodeBits) <> (HashEmpty SHL GIFCodeBits)) do { Optimized }
  1769.     HKey := NextHashKey(HKey);
  1770.   // Fill slot with key/value pair
  1771.   HashTable[HKey] := (Key SHL GIFCodeBits) OR (Code AND GIFCodeMask);
  1772. end;
  1773. // Search for key in hash table.
  1774. // Returns value if found or -1 if not
  1775. function THashTable.Lookup(Key: KeyInt): CodeInt;
  1776. var
  1777.   HKey : CodeInt;
  1778.   HTKey : KeyInt;
  1779. {$ifdef DEBUG_HASHPERFORMANCE}
  1780.   n : LongInt;
  1781. {$endif}
  1782. begin
  1783.   // Create hash key from prefix string
  1784.   HKey := HashKey(Key);
  1785. {$ifdef DEBUG_HASHPERFORMANCE}
  1786.   n := 0;
  1787. {$endif}
  1788.   // Scan table for key
  1789.   // HTKey := HashTable[HKey] SHR GIFCodeBits; { Unoptimized }
  1790.   Key := Key SHL GIFCodeBits; { Optimized }
  1791.   HTKey := HashTable[HKey] AND (HashEmpty SHL GIFCodeBits); { Optimized }
  1792.   // while (HTKey <> HashEmpty) do { Unoptimized }
  1793.   while (HTKey <> HashEmpty SHL GIFCodeBits) do { Optimized }
  1794.   begin
  1795.     if (Key = HTKey) then
  1796.     begin
  1797.       // Extract and return value
  1798.       Result := HashTable[HKey] AND GIFCodeMask;
  1799. {$ifdef DEBUG_HASHPERFORMANCE}
  1800.       inc(CountLookupFound);
  1801.       inc(CountMissFound, n);
  1802. {$endif}
  1803.       exit;
  1804.     end;
  1805. {$ifdef DEBUG_HASHPERFORMANCE}
  1806.     inc(n);
  1807. {$endif}
  1808.     // Try next slot
  1809.     HKey := NextHashKey(HKey);
  1810.     // HTKey := HashTable[HKey] SHR GIFCodeBits; { Unoptimized }
  1811.     HTKey := HashTable[HKey] AND (HashEmpty SHL GIFCodeBits); { Optimized }
  1812.   end;
  1813.   // Found empty slot - key doesn't exist
  1814.   Result := -1;
  1815. {$ifdef DEBUG_HASHPERFORMANCE}
  1816.   inc(CountLookupNotFound);
  1817.   inc(CountMissNotFound, n);
  1818. {$endif}
  1819. end;
  1820. ////////////////////////////////////////////////////////////////////////////////
  1821. // TGIFStream - Abstract GIF block stream
  1822. //
  1823. // Descendants from TGIFStream either reads or writes data in blocks
  1824. // of up to 255 bytes. These blocks are organized as a leading byte
  1825. // containing the number of bytes in the block (exclusing the count
  1826. // byte itself), followed by the data (up to 254 bytes of data).
  1827. ////////////////////////////////////////////////////////////////////////////////
  1828. type
  1829.   TGIFStream = class(TStream)
  1830.   private
  1831.     FOnWarning : TGIFWarning;
  1832.     FStream : TStream;
  1833.     FOnProgress : TNotifyEvent;
  1834.     FBuffer : array [BYTE] of Char;
  1835.     FBufferCount : integer;
  1836.   protected
  1837.     constructor Create(Stream: TStream);
  1838.     function Read(var Buffer; Count: Longint): Longint; override;
  1839.     function Write(const Buffer; Count: Longint): Longint; override;
  1840.     function Seek(Offset: Longint; Origin: Word): Longint; override;
  1841.     procedure Progress(Sender: TObject); dynamic;
  1842.     property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
  1843.   public
  1844.     property Warning: TGIFWarning read FOnWarning write FOnWarning;
  1845.   end;
  1846. constructor TGIFStream.Create(Stream: TStream);
  1847. begin
  1848.   inherited Create;
  1849.   FStream := Stream;
  1850.   FBufferCount := 1; // Reserve first byte of buffer for length
  1851. end;
  1852. procedure TGIFStream.Progress(Sender: TObject);
  1853. begin
  1854.   if Assigned(FOnProgress) then
  1855.     FOnProgress(Sender);
  1856. end;
  1857. function TGIFStream.Write(const Buffer; Count: Longint): Longint;
  1858. begin
  1859.   raise Exception.Create(sInvalidStream);
  1860. end;
  1861. function TGIFStream.Read(var Buffer; Count: Longint): Longint;
  1862. begin
  1863.   raise Exception.Create(sInvalidStream);
  1864. end;
  1865. function TGIFStream.Seek(Offset: Longint; Origin: Word): Longint;
  1866. begin
  1867.   raise Exception.Create(sInvalidStream);
  1868. end;
  1869. ////////////////////////////////////////////////////////////////////////////////
  1870. // TGIFReader - GIF block reader
  1871. ////////////////////////////////////////////////////////////////////////////////
  1872. type
  1873.   TGIFReader = class(TGIFStream)
  1874.   public
  1875.     constructor Create(Stream: TStream);
  1876.     function Read(var Buffer; Count: Longint): Longint; override;
  1877.   end;
  1878. constructor TGIFReader.Create(Stream: TStream);
  1879. begin
  1880.   inherited Create(Stream);
  1881.   FBufferCount := 0;
  1882. end;
  1883. function TGIFReader.Read(var Buffer; Count: Longint): Longint;
  1884. var
  1885.   n : integer;
  1886.   Dst : PChar;
  1887.   size : BYTE;
  1888. begin
  1889.   Dst := @Buffer;
  1890.   Result := 0;
  1891.   while (Count > 0) do
  1892.   begin
  1893.     // Get data from buffer
  1894.     while (FBufferCount > 0) and (Count > 0) do
  1895.     begin
  1896.       if (FBufferCount > Count) then
  1897.         n := Count
  1898.       else
  1899.         n := FBufferCount;
  1900.       Move(FBuffer, Dst^, n);
  1901.       dec(FBufferCount, n);
  1902.       dec(Count, n);
  1903.       inc(Result, n);
  1904.       inc(Dst, n);
  1905.     end;
  1906.     // Refill buffer when it becomes empty
  1907.     if (FBufferCount <= 0) then
  1908.     begin
  1909.       FStream.Read(size, 1);
  1910.       { TODO -oanme -cImprovement : Should be handled as a warning instead of an error. }
  1911.       if (size >= 255) then
  1912.         Error('GIF block too large');
  1913.       FBufferCount := size;
  1914.       if (FBufferCount > 0) then
  1915.       begin
  1916.         n := FStream.Read(FBuffer, size);
  1917.         if (n = FBufferCount) then
  1918.         begin
  1919.           Warning(self, gsWarning, sOutOfData);
  1920.           break;
  1921.         end;
  1922.       end else
  1923.         break;
  1924.     end;
  1925.   end;
  1926. end;
  1927. ////////////////////////////////////////////////////////////////////////////////
  1928. // TGIFWriter - GIF block writer
  1929. ////////////////////////////////////////////////////////////////////////////////
  1930. type
  1931.   TGIFWriter = class(TGIFStream)
  1932.   private
  1933.     FOutputDirty : boolean;
  1934.   protected
  1935.     procedure FlushBuffer;
  1936.   public
  1937.     constructor Create(Stream: TStream);
  1938.     destructor Destroy; override;
  1939.     function Write(const Buffer; Count: Longint): Longint; override;
  1940.     function WriteByte(Value: BYTE): Longint;
  1941.   end;
  1942. constructor TGIFWriter.Create(Stream: TStream);
  1943. begin
  1944.   inherited Create(Stream);
  1945.   FBufferCount := 1; // Reserve first byte of buffer for length
  1946.   FOutputDirty := False;
  1947. end;
  1948. destructor TGIFWriter.Destroy;
  1949. begin
  1950.   inherited Destroy;
  1951.   if (FOutputDirty) then
  1952.     FlushBuffer;
  1953. end;
  1954. procedure TGIFWriter.FlushBuffer;
  1955. begin
  1956.   if (FBufferCount <= 0) then
  1957.     exit;
  1958.   FBuffer[0] := char(FBufferCount-1); // Block size excluding the count
  1959.   FStream.WriteBuffer(FBuffer, FBufferCount);
  1960.   FBufferCount := 1; // Reserve first byte of buffer for length
  1961.   FOutputDirty := False;
  1962. end;
  1963. function TGIFWriter.Write(const Buffer; Count: Longint): Longint;
  1964. var
  1965.   n : integer;
  1966.   Src : PChar;
  1967. begin
  1968.   Result := Count;
  1969.   FOutputDirty := True;
  1970.   Src := @Buffer;
  1971.   while (Count > 0) do
  1972.   begin
  1973.     // Move data to the internal buffer in 255 byte chunks
  1974.     while (FBufferCount < sizeof(FBuffer)) and (Count > 0) do
  1975.     begin
  1976.       n := sizeof(FBuffer) - FBufferCount;
  1977.       if (n > Count) then
  1978.         n := Count;
  1979.       Move(Src^, FBuffer[FBufferCount], n);
  1980.       inc(Src, n);
  1981.       inc(FBufferCount, n);
  1982.       dec(Count, n);
  1983.     end;
  1984.     // Flush the buffer when it is full
  1985.     if (FBufferCount >= sizeof(FBuffer)) then
  1986.       FlushBuffer;
  1987.   end;
  1988. end;
  1989. function TGIFWriter.WriteByte(Value: BYTE): Longint;
  1990. begin
  1991.   Result := Write(Value, 1);
  1992. end;
  1993. ////////////////////////////////////////////////////////////////////////////////
  1994. // TGIFEncoder - Abstract encoder
  1995. ////////////////////////////////////////////////////////////////////////////////
  1996. type
  1997.   TGIFEncoder = class(TObject)
  1998.   protected
  1999.     FOnWarning : TGIFWarning;
  2000.     MaxColor : integer;
  2001.     BitsPerPixel : BYTE; // Bits per pixel of image
  2002.     Stream : TStream; // Output stream
  2003.     Width , // Width of image in pixels
  2004.     Height : integer; // height of image in pixels
  2005.     Interlace : boolean; // Interlace flag (True = interlaced image)
  2006.     Data : PChar; // Pointer to pixel data
  2007.     GIFStream : TGIFWriter; // Output buffer
  2008.     OutputBucket : longInt; // Output bit bucket
  2009.     OutputBits : integer; // Current # of bits in bucket
  2010.     ClearFlag : Boolean; // True if dictionary has just been cleared
  2011.     BitsPerCode , // Current # of bits per code
  2012.     InitialBitsPerCode : integer; // Initial # of bits per code after
  2013.    // dictionary has been cleared
  2014.     MaxCode : CodeInt; // maximum code, given BitsPerCode
  2015.     ClearCode : CodeInt; // Special output code to signal "Clear table"
  2016.     EOFCode : CodeInt; // Special output code to signal EOF
  2017.     BaseCode : CodeInt; // ...
  2018.     Pixel : PChar; // Pointer to current pixel
  2019.     cX , // Current X counter (Width - X)
  2020.     Y : integer; // Current Y
  2021.     Pass : integer; // Interlace pass
  2022.     function MaxCodesFromBits(Bits: integer): CodeInt;
  2023.     procedure Output(Value: integer); virtual;
  2024.     procedure Clear; virtual;
  2025.     function BumpPixel: boolean;
  2026.     procedure DoCompress; virtual; abstract;
  2027.   public
  2028.     procedure Compress(AStream: TStream; ABitsPerPixel: integer;
  2029.       AWidth, AHeight: integer; AInterlace: boolean; AData: PChar; AMaxColor: integer);
  2030.     property Warning: TGIFWarning read FOnWarning write FOnWarning;
  2031.   end;
  2032. // Calculate the maximum number of codes that a given number of bits can represent
  2033. // MaxCodes := (1^bits)-1
  2034. function TGIFEncoder.MaxCodesFromBits(Bits: integer): CodeInt;
  2035. begin
  2036.   Result := (CodeInt(1) SHL Bits) - 1;
  2037. end;
  2038. // Stuff bits (variable sized codes) into a buffer and output them
  2039. // a byte at a time
  2040. procedure TGIFEncoder.Output(Value: integer);
  2041. const
  2042.   BitBucketMask: array[0..16] of longInt =
  2043.     ($0000,
  2044.      $0001, $0003, $0007, $000F,
  2045.      $001F, $003F, $007F, $00FF,
  2046.      $01FF, $03FF, $07FF, $0FFF,
  2047.      $1FFF, $3FFF, $7FFF, $FFFF);
  2048. begin
  2049.   if (OutputBits > 0) then
  2050.     OutputBucket :=
  2051.       (OutputBucket AND BitBucketMask[OutputBits]) OR (longInt(Value) SHL OutputBits)
  2052.   else
  2053.     OutputBucket := Value;
  2054.   inc(OutputBits, BitsPerCode);
  2055.   while (OutputBits >= 8) do
  2056.   begin
  2057.     GIFStream.WriteByte(OutputBucket AND $FF);
  2058.     OutputBucket := OutputBucket SHR 8;
  2059.     dec(OutputBits, 8);
  2060.   end;
  2061.   if (Value = EOFCode) then
  2062.   begin
  2063.     // At EOF, write the rest of the buffer.
  2064.     while (OutputBits > 0) do
  2065.     begin
  2066.       GIFStream.WriteByte(OutputBucket AND $FF);
  2067.       OutputBucket := OutputBucket SHR 8;
  2068.       dec(OutputBits, 8);
  2069.     end;
  2070.   end;
  2071. end;
  2072. procedure TGIFEncoder.Clear;
  2073. begin
  2074.   // just_cleared = 1;
  2075.   ClearFlag := TRUE;
  2076.   Output(ClearCode);
  2077. end;
  2078. // Bump (X,Y) and data pointer to point to the next pixel
  2079. function TGIFEncoder.BumpPixel: boolean;
  2080. begin
  2081.   // Bump the current X position
  2082.   dec(cX);
  2083.   // If we are at the end of a scan line, set cX back to the beginning
  2084.   // If we are interlaced, bump Y to the appropriate spot, otherwise,
  2085.   // just increment it.
  2086.   if (cX <= 0) then
  2087.   begin
  2088.     if not(Interlace) then
  2089.     begin
  2090.       // Done - no more data
  2091.       Result := False;
  2092.       exit;
  2093.     end;
  2094.     cX := Width;
  2095.     case (Pass) of
  2096.       0:
  2097.         begin
  2098.           inc(Y, 8);
  2099.           if (Y >= Height) then
  2100.           begin
  2101.             inc(Pass);
  2102.             Y := 4;
  2103.           end;
  2104.         end;
  2105.       1:
  2106.         begin
  2107.           inc(Y, 8);
  2108.           if (Y >= Height) then
  2109.           begin
  2110.             inc(Pass);
  2111.             Y := 2;
  2112.           end;
  2113.         end;
  2114.       2:
  2115.         begin
  2116.           inc(Y, 4);
  2117.           if (Y >= Height) then
  2118.           begin
  2119.             inc(Pass);
  2120.             Y := 1;
  2121.           end;
  2122.         end;
  2123.       3:
  2124.         inc(Y, 2);
  2125.     end;
  2126.     if (Y >= height) then
  2127.     begin
  2128.       // Done - No more data
  2129.       Result := False;
  2130.       exit;
  2131.     end;
  2132.     Pixel := Data + (Y * Width);
  2133.   end;
  2134.   Result := True;
  2135. end;
  2136. procedure TGIFEncoder.Compress(AStream: TStream; ABitsPerPixel: integer;
  2137.   AWidth, AHeight: integer; AInterlace: boolean; AData: PChar; AMaxColor: integer);
  2138. const
  2139.   EndBlockByte = $00; // End of block marker
  2140. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  2141. var
  2142.   TimeStartCompress ,
  2143.   TimeStopCompress : DWORD;
  2144. {$endif}
  2145. begin
  2146.   MaxColor := AMaxColor;
  2147.   Stream := AStream;
  2148.   BitsPerPixel := ABitsPerPixel;
  2149.   Width := AWidth;
  2150.   Height := AHeight;
  2151.   Interlace := AInterlace;
  2152.   Data := AData;
  2153.   if (BitsPerPixel <= 1) then
  2154.     BitsPerPixel := 2;
  2155.   InitialBitsPerCode := BitsPerPixel + 1;
  2156.   Stream.Write(BitsPerPixel, 1);
  2157.   // out_bits_init = init_bits;
  2158.   BitsPerCode := InitialBitsPerCode;
  2159.   MaxCode := MaxCodesFromBits(BitsPerCode);
  2160.   ClearCode := (1 SHL (InitialBitsPerCode - 1));
  2161.   EOFCode := ClearCode + 1;
  2162.   BaseCode := EOFCode + 1;
  2163.   // Clear bit bucket
  2164.   OutputBucket := 0;
  2165.   OutputBits  := 0;
  2166.   // Reset pixel counter
  2167.   if (Interlace) then
  2168.     cX := Width
  2169.   else
  2170.     cX := Width*Height;
  2171.   // Reset row counter
  2172.   Y := 0;
  2173.   Pass := 0;
  2174.   GIFStream := TGIFWriter.Create(AStream);
  2175.   try
  2176.     GIFStream.Warning := Warning;
  2177.     if (Data <> nil) and (Height > 0) and (Width > 0) then
  2178.     begin
  2179. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  2180.       TimeStartCompress := timeGetTime;
  2181. {$endif}
  2182.       // Call compress implementation
  2183.       DoCompress;
  2184. {$ifdef DEBUG_COMPRESSPERFORMANCE}
  2185.       TimeStopCompress := timeGetTime;
  2186.       ShowMessage(format('Compressed %d pixels in %d mS, Rate %d pixels/mS',
  2187.         [Height*Width, TimeStopCompress-TimeStartCompress,
  2188.         DWORD(Height*Width) DIV (TimeStopCompress-TimeStartCompress+1)]));
  2189. {$endif}
  2190.       // Output the final code.
  2191.       Output(EOFCode);
  2192.     end else
  2193.       // Output the final code (and nothing else).
  2194.       TGIFEncoder(self).Output(EOFCode);
  2195.   finally
  2196.     GIFStream.Free;
  2197.   end;
  2198.   WriteByte(Stream, EndBlockByte);
  2199. end;
  2200. ////////////////////////////////////////////////////////////////////////////////
  2201. // TRLEEncoder - RLE encoder
  2202. ////////////////////////////////////////////////////////////////////////////////
  2203. type
  2204.   TRLEEncoder = class(TGIFEncoder)
  2205.   private
  2206.     MaxCodes : integer;
  2207.     OutBumpInit ,
  2208.     OutClearInit : integer;
  2209.     Prefix : integer; // Current run color
  2210.     RunLengthTableMax ,
  2211.     RunLengthTablePixel ,
  2212.     OutCount ,
  2213.     OutClear ,
  2214.     OutBump : integer;
  2215.   protected
  2216.     function ComputeTriangleCount(count: integer; nrepcodes: integer): integer;
  2217.     procedure MaxOutClear;
  2218.     procedure ResetOutClear;
  2219.     procedure FlushFromClear(Count: integer);
  2220.     procedure FlushClearOrRepeat(Count: integer);
  2221.     procedure FlushWithTable(Count: integer);
  2222.     procedure Flush(RunLengthCount: integer);
  2223.     procedure OutputPlain(Value: integer);
  2224.     procedure Clear; override;
  2225.     procedure DoCompress; override;
  2226.   end;
  2227. procedure TRLEEncoder.Clear;
  2228. begin
  2229.   OutBump := OutBumpInit;
  2230.   OutClear := OutClearInit;
  2231.   OutCount := 0;
  2232.   RunLengthTableMax := 0;
  2233.   inherited Clear;
  2234.   BitsPerCode := InitialBitsPerCode;
  2235. end;
  2236. procedure TRLEEncoder.OutputPlain(Value: integer);
  2237. begin
  2238.   ClearFlag := False;
  2239.   Output(Value);
  2240.   inc(OutCount);
  2241.   if (OutCount >= OutBump) then
  2242.   begin
  2243.     inc(BitsPerCode);
  2244.     inc(OutBump, 1 SHL (BitsPerCode - 1));
  2245.   end;
  2246.   if (OutCount >= OutClear) then
  2247.     Clear;
  2248. end;
  2249. function TRLEEncoder.ComputeTriangleCount(count: integer; nrepcodes: integer): integer;
  2250. var
  2251.   PerRepeat : integer;
  2252.   n : integer;
  2253.   function iSqrt(x: integer): integer;
  2254.   var
  2255.     r, v : integer;
  2256.   begin
  2257.     if (x < 2) then
  2258.     begin
  2259.       Result := x;
  2260.       exit;
  2261.     end else
  2262.     begin
  2263.       v := x;
  2264.       r := 1;
  2265.       while (v > 0) do
  2266.       begin
  2267.         v := v DIV 4;
  2268.         r := r * 2;
  2269.       end;
  2270.     end;
  2271.     while (True) do
  2272.     begin
  2273.       v := ((x DIV r) + r) DIV 2;
  2274.       if ((v = r) or (v = r+1)) then
  2275.       begin
  2276.         Result := r;
  2277.         exit;
  2278.       end;
  2279.       r := v;
  2280.     end;
  2281.   end;
  2282. begin
  2283.   Result := 0;
  2284.   PerRepeat := (nrepcodes * (nrepcodes+1)) DIV 2;
  2285.   while (Count >= PerRepeat) do
  2286.   begin
  2287.     inc(Result, nrepcodes);
  2288.     dec(Count, PerRepeat);
  2289.   end;
  2290.   if (Count > 0) then
  2291.   begin
  2292.     n := iSqrt(Count);
  2293.     while ((n * (n+1)) >= 2*Count) do
  2294.       dec(n);
  2295.     while ((n * (n+1)) < 2*Count) do
  2296.       inc(n);
  2297.     inc(Result, n);
  2298.   end;
  2299. end;
  2300. procedure TRLEEncoder.MaxOutClear;
  2301. begin
  2302.   OutClear := MaxCodes;
  2303. end;
  2304. procedure TRLEEncoder.ResetOutClear;
  2305. begin
  2306.   OutClear := OutClearInit;
  2307.   if (OutCount >= OutClear) then
  2308.     Clear;
  2309. end;
  2310. procedure TRLEEncoder.FlushFromClear(Count: integer);
  2311. var
  2312.   n : integer;
  2313. begin
  2314.   MaxOutClear;
  2315.   RunLengthTablePixel := Prefix;
  2316.   n := 1;
  2317.   while (Count > 0) do
  2318.   begin
  2319.     if (n = 1) then
  2320.     begin
  2321.       RunLengthTableMax := 1;
  2322.       OutputPlain(Prefix);
  2323.       dec(Count);
  2324.     end else
  2325.     if (Count >= n) then
  2326.     begin
  2327.       RunLengthTableMax := n;
  2328.       OutputPlain(BaseCode + n - 2);
  2329.       dec(Count, n);
  2330.     end else
  2331.     if (Count = 1) then
  2332.     begin
  2333.       inc(RunLengthTableMax);
  2334.       OutputPlain(Prefix);
  2335.       break;
  2336.     end else
  2337.     begin
  2338.       inc(RunLengthTableMax);
  2339.       OutputPlain(BaseCode + Count - 2);
  2340.       break;
  2341.     end;
  2342.     if (OutCount = 0) then
  2343.       n := 1
  2344.     else
  2345.       inc(n);
  2346.   end;
  2347.   ResetOutClear;
  2348. end;
  2349. procedure TRLEEncoder.FlushClearOrRepeat(Count: integer);
  2350. var
  2351.   WithClear : integer;
  2352. begin
  2353.   WithClear := 1 + ComputeTriangleCount(Count, MaxCodes);
  2354.   if (WithClear < Count) then
  2355.   begin
  2356.     Clear;
  2357.     FlushFromClear(Count);
  2358.   end else
  2359.     while (Count > 0) do
  2360.     begin
  2361.       OutputPlain(Prefix);
  2362.       dec(Count);
  2363.     end;
  2364. end;
  2365. procedure TRLEEncoder.FlushWithTable(Count: integer);
  2366. var
  2367.   RepeatMax ,
  2368.   RepeatLeft ,
  2369.   LeftOver : integer;
  2370. begin
  2371.   RepeatMax := Count DIV RunLengthTableMax;
  2372.   LeftOver := Count MOD RunLengthTableMax;
  2373.   if (LeftOver <> 0) then
  2374.     RepeatLeft := 1
  2375.   else
  2376.     RepeatLeft := 0;
  2377.   if (OutCount + RepeatMax + RepeatLeft > MaxCodes) then
  2378.   begin
  2379.     RepeatMax := MaxCodes - OutCount;
  2380.     LeftOver := Count - (RepeatMax * RunLengthTableMax);
  2381.     RepeatLeft := 1 + ComputeTriangleCount(LeftOver, MaxCodes);
  2382.   end;
  2383.   if (1 + ComputeTriangleCount(Count, MaxCodes) < RepeatMax + RepeatLeft) then
  2384.   begin
  2385.     Clear;
  2386.     FlushFromClear(Count);
  2387.     exit;
  2388.   end;
  2389.   MaxOutClear;
  2390.   while (RepeatMax > 0) do
  2391.   begin
  2392.     OutputPlain(BaseCode + RunLengthTableMax-2);
  2393.     dec(RepeatMax);
  2394.   end;
  2395.   if (LeftOver > 0) then
  2396.   begin
  2397.     if (ClearFlag) then
  2398.       FlushFromClear(LeftOver)
  2399.     else if (LeftOver = 1) then
  2400.       OutputPlain(Prefix)
  2401.     else
  2402.       OutputPlain(BaseCode +  LeftOver - 2);
  2403.   end;
  2404.   ResetOutClear;
  2405. end;
  2406. procedure TRLEEncoder.Flush(RunLengthCount: integer);
  2407. begin
  2408.   if (RunLengthCount = 1) then
  2409.   begin
  2410.     OutputPlain(Prefix);
  2411.     exit;
  2412.   end;
  2413.   if (ClearFlag) then
  2414.     FlushFromClear(RunLengthCount)
  2415.   else if ((RunLengthTableMax < 2) or (RunLengthTablePixel <> Prefix)) then
  2416.     FlushClearOrRepeat(RunLengthCount)
  2417.   else
  2418.     FlushWithTable(RunLengthCount);
  2419. end;
  2420. procedure TRLEEncoder.DoCompress;
  2421. var
  2422.   Color : CodeInt;
  2423.   RunLengthCount : integer;
  2424. begin
  2425.   OutBumpInit := ClearCode - 1;
  2426.   // For images with a lot of runs, making OutClearInit larger will
  2427.   // give better compression.
  2428.   if (BitsPerPixel <= 3) then
  2429.     OutClearInit := 9
  2430.   else
  2431.     OutClearInit := OutBumpInit - 1;
  2432.   // max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);
  2433.   // <=> MaxCodes := (1 SHL GIFCodeBits) - ((1 SHL (BitsPerCode - 1)) + 3);
  2434.   // <=> MaxCodes := (1 SHL GIFCodeBits) - ((1 SHL (InitialBitsPerCode - 1)) + 3);
  2435.   // <=> MaxCodes := (1 SHL GIFCodeBits) - (ClearCode + 3);
  2436.   // <=> MaxCodes := (1 SHL GIFCodeBits) - (EOFCode + 2);
  2437.   // <=> MaxCodes := (1 SHL GIFCodeBits) - (BaseCode + 1);
  2438.   // <=> MaxCodes := MaxCodesFromBits(GIFCodeBits) - BaseCode;
  2439.   MaxCodes := MaxCodesFromBits(GIFCodeBits) - BaseCode;
  2440.   Clear;
  2441.   RunLengthCount := 0;
  2442.   Pixel := Data;
  2443.   Prefix := -1; // Dummy value to make Color <> Prefix
  2444.   repeat
  2445.     // Fetch the next pixel
  2446.     Color := CodeInt(Pixel^);
  2447.     inc(Pixel);
  2448.     if (Color >= MaxColor) then
  2449.       Error(sInvalidColor);
  2450.     if (RunLengthCount > 0) and (Color <> Prefix) then
  2451.     begin
  2452.       // End of current run
  2453.       Flush(RunLengthCount);
  2454.       RunLengthCount := 0;
  2455.     end;
  2456.     if (Color = Prefix) then
  2457.       // Increment run length
  2458.       inc(RunLengthCount)
  2459.     else
  2460.     begin
  2461.       // Start new run
  2462.       Prefix := Color;
  2463.       RunLengthCount := 1;
  2464.     end;
  2465.   until not(BumpPixel);
  2466.   Flush(RunLengthCount);
  2467. end;
  2468. ////////////////////////////////////////////////////////////////////////////////
  2469. // TLZWEncoder - LZW encoder
  2470. ////////////////////////////////////////////////////////////////////////////////
  2471. const
  2472.   TableMaxMaxCode = (1 SHL GIFCodeBits); //
  2473.   TableMaxFill = TableMaxMaxCode-1; // Clear table when it fills to
  2474.    // this point.
  2475.    // Note: Must be <= GIFCodeMax
  2476. type
  2477.   TLZWEncoder = class(TGIFEncoder)
  2478.   private
  2479.     Prefix : CodeInt; // Current run color
  2480.     FreeEntry : CodeInt; // next unused code in table
  2481.     HashTable : THashTable;
  2482.   protected
  2483.     procedure Output(Value: integer); override;
  2484.     procedure Clear; override;
  2485.     procedure DoCompress; override;
  2486.   end;
  2487. procedure TLZWEncoder.Output(Value: integer);
  2488. begin
  2489.   inherited Output(Value);
  2490.   // If the next entry is going to be too big for the code size,
  2491.   // then increase it, if possible.
  2492.   if (FreeEntry > MaxCode) or (ClearFlag) then
  2493.   begin
  2494.     if (ClearFlag) then
  2495.     begin
  2496.       BitsPerCode := InitialBitsPerCode;
  2497.       MaxCode := MaxCodesFromBits(BitsPerCode);
  2498.       ClearFlag := False;
  2499.     end else
  2500.     begin
  2501.       inc(BitsPerCode);
  2502.       if (BitsPerCode = GIFCodeBits) then
  2503.         MaxCode := TableMaxMaxCode
  2504.       else
  2505.         MaxCode := MaxCodesFromBits(BitsPerCode);
  2506.     end;
  2507.   end;
  2508. end;
  2509. procedure TLZWEncoder.Clear;
  2510. begin
  2511.   inherited Clear;
  2512.   HashTable.Clear;
  2513.   FreeEntry := ClearCode + 2;
  2514. end;
  2515. procedure TLZWEncoder.DoCompress;
  2516. var
  2517.   Color : char;
  2518.   NewKey : KeyInt;
  2519.   NewCode : CodeInt;
  2520. begin
  2521.   HashTable := THashTable.Create;
  2522.   try
  2523.     // clear hash table and sync decoder
  2524.     Clear;
  2525.     Pixel := Data;
  2526.     Prefix := CodeInt(Pixel^);
  2527.     inc(Pixel);
  2528.     if (Prefix >= MaxColor) then
  2529.       Error(sInvalidColor);
  2530.     while (BumpPixel) do
  2531.     begin
  2532.       // Fetch the next pixel
  2533.       Color := Pixel^;
  2534.       inc(Pixel);
  2535.       if (ord(Color) >= MaxColor) then
  2536.         Error(sInvalidColor);
  2537.       // Append Postfix to Prefix and lookup in table...
  2538.       NewKey := (KeyInt(Prefix) SHL 8) OR ord(Color);
  2539.       NewCode := HashTable.Lookup(NewKey);
  2540.       if (NewCode >= 0) then
  2541.       begin
  2542.         // ...if found, get next pixel
  2543.         Prefix := NewCode;
  2544.         continue;
  2545.       end;
  2546.       // ...if not found, output and start over
  2547.       Output(Prefix);
  2548.       Prefix := CodeInt(Color);
  2549.       if (FreeEntry < TableMaxFill) then
  2550.       begin
  2551.         HashTable.Insert(NewKey, FreeEntry);
  2552.         inc(FreeEntry);
  2553.       end else
  2554.         Clear;
  2555.     end;
  2556.     Output(Prefix);
  2557.   finally
  2558.     HashTable.Free;
  2559.   end;
  2560. end;
  2561. ////////////////////////////////////////////////////////////////////////////////
  2562. //
  2563. // TGIFSubImage
  2564. //
  2565. ////////////////////////////////////////////////////////////////////////////////
  2566. /////////////////////////////////////////////////////////////////////////
  2567. // TGIFSubImage.Compress
  2568. /////////////////////////////////////////////////////////////////////////
  2569. procedure TGIFSubImage.Compress(Stream: TStream);
  2570. var
  2571.   Encoder : TGIFEncoder;
  2572.   BitsPerPixel : BYTE;
  2573.   MaxColors : integer;
  2574. begin
  2575.   if (ColorMap.Count > 0) then
  2576.   begin
  2577.     MaxColors := ColorMap.Count;
  2578.     BitsPerPixel := ColorMap.BitsPerPixel
  2579.   end else
  2580.   begin
  2581.     BitsPerPixel := Image.BitsPerPixel;
  2582.     MaxColors := 1 SHL BitsPerPixel;
  2583.   end;
  2584.   // Create a RLE or LZW GIF encoder
  2585.   if (Image.Compression = gcRLE) then
  2586.     Encoder := TRLEEncoder.Create
  2587.   else
  2588.     Encoder := TLZWEncoder.Create;
  2589.   try
  2590.     Encoder.Warning := Image.Warning;
  2591.     Encoder.Compress(Stream, BitsPerPixel, Width, Height, Interlaced, FData, MaxColors);
  2592.   finally
  2593.     Encoder.Free;
  2594.   end;
  2595. end;
  2596. function TGIFExtensionList.GetExtension(Index: Integer): TGIFExtension;
  2597. begin
  2598.   Result := TGIFExtension(Items[Index]);
  2599. end;
  2600. procedure TGIFExtensionList.SetExtension(Index: Integer; Extension: TGIFExtension);
  2601. begin
  2602.   Items[Index] := Extension;
  2603. end;
  2604. procedure TGIFExtensionList.LoadFromStream(Stream: TStream; Parent: TObject);
  2605. var
  2606.   b : BYTE;
  2607.   Extension : TGIFExtension;
  2608.   ExtensionClass : TGIFExtensionClass;
  2609. begin
  2610.   // Peek ahead to determine block type
  2611.   if (Stream.Read(b, 1) <> 1) then
  2612.     exit;
  2613.   while not(b in [bsTrailer, bsImageDescriptor]) do
  2614.   begin
  2615.     if (b = bsExtensionIntroducer) then
  2616.     begin
  2617.       ExtensionClass := TGIFExtension.FindExtension(Stream);
  2618.       if (ExtensionClass = nil) then
  2619.         Error(sUnknownExtension);
  2620.       Stream.Seek(-1, soFromCurrent);
  2621.       Extension := ExtensionClass.Create(Parent as TGIFSubImage);
  2622.       try
  2623.         Extension.LoadFromStream(Stream);
  2624.         Add(Extension);
  2625.       except
  2626.         Extension.Free;
  2627.         raise;
  2628.       end;
  2629.     end else
  2630.     begin
  2631.       Warning(gsWarning, sBadExtensionLabel);
  2632.       break;
  2633.     end;
  2634.     if (Stream.Read(b, 1) <> 1) then
  2635.       exit;
  2636.   end;
  2637.   Stream.Seek(-1, soFromCurrent);
  2638. end;
  2639. const
  2640.   { image descriptor bit masks }
  2641.   idLocalColorTable = $80;    { set if a local color table follows }
  2642.   idInterlaced = $40;    { set if image is interlaced }
  2643.   idSort = $20;    { set if color table is sorted }
  2644.   idReserved = $0C;    { reserved - must be set to $00 }
  2645.   idColorTableSize = $07;    { size of color table as above }
  2646. constructor TGIFSubImage.Create(GIFImage: TGIFImage);
  2647. begin
  2648.   inherited Create(GIFImage);
  2649.   FExtensions := TGIFExtensionList.Create(GIFImage);
  2650.   FColorMap := TGIFLocalColorMap.Create(self);
  2651.   FImageDescriptor.Separator := bsImageDescriptor;
  2652.   FImageDescriptor.Left := 0;
  2653.   FImageDescriptor.Top := 0;
  2654.   FImageDescriptor.Width := 0;
  2655.   FImageDescriptor.Height := 0;
  2656.   FImageDescriptor.PackedFields := 0;
  2657.   FBitmap := nil;
  2658.   FMask := 0;
  2659.   FNeedMask := True;
  2660.   FData := nil;
  2661.   FDataSize := 0;
  2662.   FTransparent := False;
  2663.   FGCE := nil;
  2664.   // Remember to synchronize with TGIFSubImage.Clear
  2665. end;
  2666. destructor TGIFSubImage.Destroy;
  2667. begin
  2668.   if (FGIFImage <> nil) then
  2669.     FGIFImage.Images.Remove(self);
  2670.   Clear;
  2671.   FExtensions.Free;
  2672.   FColorMap.Free;
  2673.   if (FLocalPalette <> 0) then
  2674.     DeleteObject(FLocalPalette);
  2675.   inherited Destroy;
  2676. end;
  2677. procedure TGIFSubImage.Clear;
  2678. begin
  2679.   FExtensions.Clear;
  2680.   FColorMap.Clear;
  2681.   FreeImage;
  2682.   Height := 0;
  2683.   Width := 0;
  2684.   FTransparent := False;
  2685.   FGCE := nil;
  2686.   FreeBitmap;
  2687.   FreeMask;
  2688.   // Remember to synchronize with TGIFSubImage.Create
  2689. end;
  2690. function TGIFSubImage.GetEmpty: Boolean;
  2691. begin
  2692.   Result := ((FData = nil) or (FDataSize = 0) or (Height = 0) or (Width = 0));
  2693. end;
  2694. function TGIFSubImage.GetPalette: HPALETTE;
  2695. begin
  2696.   if (FBitmap <> nil) and (FBitmap.Palette <> 0) then
  2697.     // Use bitmaps own palette if possible
  2698.     Result := FBitmap.Palette
  2699.   else if (FLocalPalette <> 0) then
  2700.     // Or a previously exported local palette
  2701.     Result := FLocalPalette
  2702.   else if (Image.DoDither) then
  2703.   begin
  2704.     // or create a new dither palette
  2705.     FLocalPalette := WebPalette;
  2706.     Result := FLocalPalette;
  2707.   end
  2708.   else if (ColorMap.Count > 0) then
  2709.   begin
  2710.     // or create a new if first time
  2711.     FLocalPalette := ColorMap.ExportPalette;
  2712.     Result := FLocalPalette;
  2713.   end else
  2714.     // Use global palette if everything else fails
  2715.     Result := Image.Palette;
  2716. end;
  2717. procedure TGIFSubImage.SetPalette(Value: HPalette);
  2718. var
  2719.   NeedNewBitmap : boolean;
  2720. begin
  2721.   if (Value <> FLocalPalette) then
  2722.   begin
  2723.     // Zap old palette
  2724.     if (FLocalPalette <> 0) then
  2725.       DeleteObject(FLocalPalette);
  2726.     // Zap bitmap unless new palette is same as bitmaps own
  2727.     NeedNewBitmap := (FBitmap <> nil) and (Value <> FBitmap.Palette);
  2728.     // Use new palette
  2729.     FLocalPalette := Value;
  2730.     if (NeedNewBitmap) then
  2731.     begin
  2732.       // Need to create new bitmap and repaint
  2733.       FreeBitmap;
  2734.       Image.PaletteModified := True;
  2735.       Image.Changed(Self);
  2736.     end;
  2737.   end;
  2738. end;
  2739. procedure TGIFSubImage.NeedImage;
  2740. begin
  2741.   if (FData = nil) then
  2742.     NewImage;
  2743.   if (FDataSize = 0) then
  2744.     Error(sEmptyImage);
  2745. end;
  2746. procedure TGIFSubImage.NewImage;
  2747. var
  2748.   NewSize : longInt;
  2749. begin
  2750.   FreeImage;
  2751.   NewSize := Height * Width;
  2752.   if (NewSize <> 0) then
  2753.   begin
  2754.     GetMem(FData, NewSize);
  2755.     FillChar(FData^, NewSize, 0);
  2756.   end else
  2757.     FData := nil;
  2758.   FDataSize := NewSize;
  2759. end;
  2760. procedure TGIFSubImage.FreeImage;
  2761. begin
  2762.   if (FData <> nil) then
  2763.     FreeMem(FData);
  2764.   FDataSize := 0;
  2765.   FData := nil;
  2766. end;
  2767. function TGIFSubImage.GetHasBitmap: boolean;
  2768. begin
  2769.   Result := (FBitmap <> nil);
  2770. end;
  2771. procedure TGIFSubImage.SetHasBitmap(Value: boolean);
  2772. begin
  2773.   if (Value <> (FBitmap <> nil)) then
  2774.   begin
  2775.     if (Value) then
  2776.       Bitmap // Referencing Bitmap will automatically create it
  2777.     else
  2778.       FreeBitmap;
  2779.   end;
  2780. end;
  2781. procedure TGIFSubImage.NewBitmap;
  2782. begin
  2783.   FreeBitmap;
  2784.   FBitmap := TBitmap.Create;
  2785. end;
  2786. procedure TGIFSubImage.FreeBitmap;
  2787. begin
  2788.   if (FBitmap <> nil) then
  2789.   begin
  2790.     FBitmap.Free;
  2791.     FBitmap := nil;
  2792.   end;
  2793. end;
  2794. procedure TGIFSubImage.FreeMask;
  2795. begin
  2796.   if (FMask <> 0) then
  2797.   begin
  2798.     DeleteObject(FMask);
  2799.     FMask := 0;
  2800.   end;
  2801.   FNeedMask := True;
  2802. end;
  2803. function TGIFSubImage.HasMask: boolean;
  2804. begin
  2805.   if (FNeedMask) and (Transparent) then
  2806.   begin
  2807.     // Zap old bitmap
  2808.     FreeBitmap;
  2809.     // Create new bitmap and mask
  2810.     GetBitmap;
  2811.   end;
  2812.   Result := (FMask <> 0);
  2813. end;
  2814. function TGIFSubImage.GetBounds(Index: integer): WORD;
  2815. begin
  2816.   case (Index) of
  2817.     1: Result := FImageDescriptor.Left;
  2818.     2: Result := FImageDescriptor.Top;
  2819.     3: Result := FImageDescriptor.Width;
  2820.     4: Result := FImageDescriptor.Height;
  2821.   else
  2822.     Result := 0; // To avoid compiler warnings
  2823.   end;
  2824. end;
  2825. procedure TGIFSubImage.SetBounds(Index: integer; Value: WORD);
  2826. begin
  2827.   case (Index) of
  2828.     1: DoSetBounds(Value, FImageDescriptor.Top, FImageDescriptor.Width, FImageDescriptor.Height);
  2829.     2: DoSetBounds(FImageDescriptor.Left, Value, FImageDescriptor.Width, FImageDescriptor.Height);
  2830.     3: DoSetBounds(FImageDescriptor.Left, FImageDescriptor.Top, Value, FImageDescriptor.Height);
  2831.     4: DoSetBounds(FImageDescriptor.Left, FImageDescriptor.Top, FImageDescriptor.Width, Value);
  2832.   end;
  2833. end;
  2834. {$IFOPT R+}
  2835.   {$DEFINE R_PLUS}
  2836.   {$RANGECHECKS OFF}
  2837. {$ENDIF}
  2838. function TGIFSubImage.DoGetDitherBitmap: TBitmap;
  2839. var
  2840.   ColorLookup : TColorLookup;
  2841.   Ditherer : TDitherEngine;
  2842.   DIBResult : TDIB;
  2843.   Src : PChar;
  2844.   Dst : PChar;
  2845.   Row : integer;
  2846.   Color : TGIFColor;
  2847.   ColMap : PColorMap;
  2848.   Index : byte;
  2849.   TransparentIndex : byte;
  2850.   IsTransparent : boolean;
  2851.   WasTransparent : boolean;
  2852.   MappedTransparentIndex: char;
  2853.   MaskBits : PChar;
  2854.   MaskDest : PChar;
  2855.   MaskRow : PChar;
  2856.   MaskRowWidth ,
  2857.   MaskRowBitWidth : integer;
  2858.   Bit ,
  2859.   RightBit : BYTE;
  2860. begin
  2861.   Result := TBitmap.Create;
  2862.   try
  2863. {$IFNDEF VER9x}
  2864.     if (Width*Height > BitmapAllocationThreshold) then
  2865.       SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  2866. {$ENDIF}
  2867.     if (Empty) then
  2868.     begin
  2869.       // Set bitmap width and height
  2870.       Result.Width := Width;
  2871.       Result.Height := Height;
  2872.       // Build and copy palette to bitmap
  2873.       Result.Palette := CopyPalette(Palette);
  2874.       exit;
  2875.     end;
  2876.     ColorLookup := nil;
  2877.     Ditherer := nil;
  2878.     DIBResult := nil;
  2879.     try // Protect above resources
  2880.       ColorLookup := TNetscapeColorLookup.Create(Palette);
  2881.       Ditherer := TFloydSteinbergDitherer.Create(Width, ColorLookup);
  2882.       // Get DIB buffer for scanline operations
  2883.       // It is assumed that the source palette is the 216 color Netscape palette
  2884.       DIBResult := TDIBWriter.Create(Result, pf8bit, Width, Height, Palette);
  2885.       // Determine if this image is transparent
  2886.       ColMap := ActiveColorMap.Data;
  2887.       IsTransparent := FNeedMask and Transparent;
  2888.       WasTransparent := False;
  2889.       FNeedMask := False;
  2890.       TransparentIndex := 0;
  2891.       MappedTransparentIndex := #0;
  2892.       if (FMask = 0) and (IsTransparent) then
  2893.       begin
  2894.         IsTransparent := True;
  2895.         TransparentIndex := GraphicControlExtension.TransparentColorIndex;
  2896.         Color := ColMap[ord(TransparentIndex)];
  2897.         MappedTransparentIndex := char(Color.Blue DIV 51 +
  2898.           MulDiv(6, Color.Green, 51) + MulDiv(36, Color.Red, 51)+1);
  2899.       end;
  2900.       // Allocate bit buffer for transparency mask
  2901.       MaskDest := nil;
  2902.       Bit := $00;
  2903.       if (IsTransparent) then
  2904.       begin
  2905.         MaskRowWidth := ((Width+15) DIV 16) * 2;
  2906.         MaskRowBitWidth := (Width+7) DIV 8;
  2907.         RightBit := $01 SHL ((8 - (Width AND $0007)) AND $0007);
  2908.         GetMem(MaskBits, MaskRowWidth * Height);
  2909.         FillChar(MaskBits^, MaskRowWidth * Height, 0);
  2910.       end else
  2911.       begin
  2912.         MaskBits := nil;
  2913.         MaskRowWidth := 0;
  2914.         MaskRowBitWidth := 0;
  2915.         RightBit := $00;
  2916.       end;
  2917.       try
  2918.         // Process the image
  2919.         Row := 0;
  2920.         MaskRow := MaskBits;
  2921.         Src := FData;
  2922.         while (Row < Height) do
  2923.         begin
  2924.           if ((Row AND $1F) = 0) then
  2925.             Image.Progress(Self, psRunning, MulDiv(Row, 100, Height),
  2926.               False, Rect(0,0,0,0), sProgressRendering);
  2927.           Dst := DIBResult.ScanLine[Row];
  2928.           if (IsTransparent) then
  2929.           begin
  2930.             // Preset all pixels to transparent
  2931.             FillChar(Dst^, Width, ord(MappedTransparentIndex));
  2932.             if (Ditherer.Direction = 1) then
  2933.             begin
  2934.               MaskDest := MaskRow;
  2935.               Bit := $80;
  2936.             end else
  2937.             begin
  2938.               MaskDest := MaskRow + MaskRowBitWidth-1;
  2939.               Bit := RightBit;
  2940.             end;
  2941.           end;
  2942.           inc(Dst, Ditherer.Column);
  2943.           while (Ditherer.Column < Ditherer.Width) and (Ditherer.Column >= 0) do
  2944.           begin
  2945.             Index := ord(Src^);
  2946.             Color := ColMap[ord(Index)];
  2947.             if (IsTransparent) and (Index = TransparentIndex) then
  2948.             begin
  2949.               MaskDest^ := char(byte(MaskDest^) OR Bit);
  2950.               WasTransparent := True;
  2951.               Ditherer.NextColumn;
  2952.             end else
  2953.             begin
  2954.               // Dither and map a single pixel
  2955.               Dst^ := Ditherer.Dither(Color.Red, Color.Green, Color.Blue,
  2956.                 Color.Red, Color.Green, Color.Blue);
  2957.             end;
  2958.             if (IsTransparent) then
  2959.             begin
  2960.               if (Ditherer.Direction = 1) then
  2961.               begin
  2962.                 Bit := Bit SHR 1;
  2963.                 if (Bit = $00) then
  2964.                 begin
  2965.                   Bit := $80;
  2966.                   inc(MaskDest, 1);
  2967.                 end;
  2968.               end else
  2969.               begin
  2970.                 Bit := Bit SHL 1;
  2971.                 if (Bit = $00) then
  2972.                 begin
  2973.                   Bit := $01;
  2974.                   dec(MaskDest, 1);
  2975.                 end;
  2976.               end;
  2977.             end;
  2978.             inc(Src, Ditherer.Direction);
  2979.             inc(Dst, Ditherer.Direction);
  2980.           end;
  2981.           if (IsTransparent) then
  2982.             Inc(MaskRow, MaskRowWidth);
  2983.           Inc(Row);
  2984.           inc(Src, Width-Ditherer.Direction);
  2985.           Ditherer.NextLine;
  2986.         end;
  2987.         // Transparent paint needs a mask bitmap
  2988.         if (IsTransparent) and (WasTransparent) then
  2989.           FMask := CreateBitmap(Width, Height, 1, 1, MaskBits);
  2990.       finally
  2991.         if (MaskBits <> nil) then
  2992.           FreeMem(MaskBits);
  2993.       end;
  2994.     finally
  2995.       if (ColorLookup <> nil) then
  2996.         ColorLookup.Free;
  2997.       if (Ditherer <> nil) then
  2998.         Ditherer.Free;
  2999.       if (DIBResult <> nil) then
  3000.         DIBResult.Free;
  3001.     end;
  3002.   except
  3003.     Result.Free;
  3004.     raise;
  3005.   end;
  3006. end;
  3007. {$IFDEF R_PLUS}
  3008.   {$RANGECHECKS ON}
  3009.   {$UNDEF R_PLUS}
  3010. {$ENDIF}
  3011. function TGIFSubImage.DoGetBitmap: TBitmap;
  3012. var
  3013.   ScanLineRow : Integer;
  3014.   DIBResult : TDIB;
  3015.   DestScanLine ,
  3016.   Src : PChar;
  3017.   TransparentIndex : byte;
  3018.   IsTransparent : boolean;
  3019.   WasTransparent : boolean;
  3020.   MaskBits : PChar;
  3021.   MaskDest : PChar;
  3022.   MaskRow : PChar;
  3023.   MaskRowWidth : integer;
  3024.   Col : integer;
  3025.   MaskByte : byte;
  3026.   Bit : byte;
  3027. begin
  3028.   Result := TBitmap.Create;
  3029.   try
  3030. {$IFNDEF VER9x}
  3031.     if (Width*Height > BitmapAllocationThreshold) then
  3032.       SetPixelFormat(Result, pf1bit); // To reduce resource consumption of resize
  3033. {$ENDIF}
  3034.     if (Empty) then
  3035.     begin
  3036.       // Set bitmap width and height
  3037.       Result.Width := Width;
  3038.       Result.Height := Height;
  3039.       // Build and copy palette to bitmap
  3040.       Result.Palette := CopyPalette(Palette);
  3041.       exit;
  3042.     end;
  3043.     // Get DIB buffer for scanline operations
  3044.     DIBResult := TDIBWriter.Create(Result, pf8bit, Width, Height, Palette);
  3045.     try
  3046.       // Determine if this image is transparent
  3047.       IsTransparent := FNeedMask and Transparent;
  3048.       WasTransparent := False;
  3049.       FNeedMask := False;
  3050.       TransparentIndex := 0;
  3051.       if (FMask = 0) and (IsTransparent) then
  3052.       begin
  3053.         IsTransparent := True;
  3054.         TransparentIndex := GraphicControlExtension.TransparentColorIndex;
  3055.       end;
  3056.       // Allocate bit buffer for transparency mask
  3057.       if (IsTransparent) then
  3058.       begin
  3059.         MaskRowWidth := ((Width+15) DIV 16) * 2;
  3060.         GetMem(MaskBits, MaskRowWidth * Height);
  3061.         FillChar(MaskBits^, MaskRowWidth * Height, 0);
  3062.         IsTransparent := (MaskBits <> nil);
  3063.       end else
  3064.       begin
  3065.         MaskBits := nil;
  3066.         MaskRowWidth := 0;
  3067.       end;
  3068.       try
  3069.         ScanLineRow := 0;
  3070.         Src := FData;
  3071.         MaskRow := MaskBits;
  3072.         while (ScanLineRow < Height) do
  3073.         begin
  3074.           DestScanline := DIBResult.ScanLine[ScanLineRow];
  3075.           if ((ScanLineRow AND $1F) = 0) then
  3076.             Image.Progress(Self, psRunning, MulDiv(ScanLineRow, 100, Height),
  3077.               False, Rect(0,0,0,0), sProgressRendering);
  3078.           Move(Src^, DestScanline^, Width);
  3079.           Inc(ScanLineRow);
  3080.           if (IsTransparent) then
  3081.           begin
  3082.             Bit := $80;
  3083.             MaskDest := MaskRow;
  3084.             MaskByte := 0;
  3085.             for Col := 0 to Width-1 do
  3086.             begin
  3087.               // Set a bit in the mask if the pixel is transparent
  3088.               if (Src^ = char(TransparentIndex)) then
  3089.                 MaskByte := MaskByte OR Bit;
  3090.               Bit := Bit SHR 1;
  3091.               if (Bit = $00) then
  3092.               begin
  3093.                 // Store a mask byte for each 8 pixels
  3094.                 Bit := $80;
  3095.                 WasTransparent := WasTransparent or (MaskByte <> 0);
  3096.                 MaskDest^ := char(MaskByte);
  3097.                 inc(MaskDest);
  3098.                 MaskByte := 0;
  3099.               end;
  3100.               Inc(Src);
  3101.             end;
  3102.             // Save the last mask byte in case the width isn't divisable by 8
  3103.             if (MaskByte <> 0) then
  3104.             begin
  3105.               WasTransparent := True;
  3106.               MaskDest^ := char(MaskByte);
  3107.             end;
  3108.             Inc(MaskRow, MaskRowWidth);
  3109.           end else
  3110.             Inc(Src, Width);
  3111.         end;
  3112.         // Transparent paint needs a mask bitmap
  3113.         if (IsTransparent) and (WasTransparent) then
  3114.           FMask := CreateBitmap(Width, Height, 1, 1, MaskBits);
  3115.       finally
  3116.         if (MaskBits <> nil) then
  3117.           FreeMem(MaskBits);
  3118.       end;
  3119.     finally
  3120.       // Free DIB buffer used for scanline operations
  3121.       DIBResult.Free;
  3122.     end;
  3123.   except
  3124.     Result.Free;
  3125.     raise;
  3126.   end;
  3127. end;
  3128. {$ifdef DEBUG_RENDERPERFORMANCE}
  3129. var
  3130.   ImageCount : DWORD = 0;
  3131.   RenderTime : DWORD = 0;
  3132. {$endif}
  3133. function TGIFSubImage.GetBitmap: TBitmap;
  3134. var
  3135.   n : integer;
  3136. {$ifdef DEBUG_RENDERPERFORMANCE}
  3137.   RenderStartTime : DWORD;
  3138. {$endif}
  3139. begin
  3140. {$ifdef DEBUG_RENDERPERFORMANCE}
  3141.   if (GetAsyncKeyState(VK_CONTROL) <> 0) then
  3142.   begin
  3143.     ShowMessage(format('Render %d images in %d mS, Rate %d mS/image (%d images/S)',
  3144.       [ImageCount, RenderTime,
  3145.        RenderTime DIV (ImageCount+1),
  3146.        MulDiv(ImageCount, 1000, RenderTime+1)]));
  3147.   end;
  3148. {$endif}
  3149.   Result := FBitmap;
  3150.   if (Result <> nil) or (Empty) then
  3151.     Exit;
  3152. {$ifdef DEBUG_RENDERPERFORMANCE}
  3153.   inc(ImageCount);
  3154.   RenderStartTime := timeGetTime;
  3155. {$endif}
  3156.   try
  3157.     Image.Progress(Self, psStarting, 0, False, Rect(0,0,0,0), sProgressRendering);
  3158.     try
  3159.       if (Image.DoDither) then
  3160.         // Create dithered bitmap
  3161.         FBitmap := DoGetDitherBitmap
  3162.       else
  3163.         // Create "regular" bitmap
  3164.         FBitmap := DoGetBitmap;
  3165.       Result := FBitmap;
  3166.     finally
  3167.       if ExceptObject = nil then
  3168.         n := 100
  3169.       else
  3170.         n := 0;
  3171.       Image.Progress(Self, psEnding, n, Image.PaletteModified, Rect(0,0,0,0),
  3172.         sProgressRendering);
  3173.       // Make sure new palette gets realized, in case OnProgress event didn't.
  3174.       if Image.PaletteModified then
  3175.         Image.Changed(Self);
  3176.     end;
  3177.   except
  3178.     on EAbort do ;   // OnProgress can raise EAbort to cancel image load
  3179.   end;
  3180. {$ifdef DEBUG_RENDERPERFORMANCE}
  3181.   inc(RenderTime, timeGetTime-RenderStartTime);
  3182. {$endif}
  3183. end;
  3184. procedure TGIFSubImage.SetBitmap(Value: TBitmap);
  3185. begin
  3186.   FreeBitmap;
  3187.   if (Value <> nil) then
  3188.     Assign(Value);
  3189. end;
  3190. function TGIFSubImage.GetActiveColorMap: TGIFColorMap;
  3191. begin
  3192.   if (ColorMap.Count > 0) or (Image.GlobalColorMap.Count = 0) then
  3193.     Result := ColorMap
  3194.   else
  3195.     Result := Image.GlobalColorMap;
  3196. end;
  3197. function TGIFSubImage.GetInterlaced: boolean;
  3198. begin
  3199.   Result := (FImageDescriptor.PackedFields AND idInterlaced) <> 0;
  3200. end;
  3201. procedure TGIFSubImage.SetInterlaced(Value: boolean);
  3202. begin
  3203.   if (Value) then
  3204.     FImageDescriptor.PackedFields := FImageDescriptor.PackedFields OR idInterlaced
  3205.   else
  3206.     FImageDescriptor.PackedFields := FImageDescriptor.PackedFields AND NOT(idInterlaced);
  3207. end;
  3208. function TGIFSubImage.GetVersion: TGIFVersion;
  3209. var
  3210.   v : TGIFVersion;
  3211.   i : integer;
  3212. begin
  3213.   if (ColorMap.Optimized) then
  3214.     Result := gv89a
  3215.   else
  3216.     Result := inherited GetVersion;
  3217.   i := 0;
  3218.   while (Result < high(TGIFVersion)) and (i < FExtensions.Count) do
  3219.   begin
  3220.     v := FExtensions[i].Version;
  3221.     if (v > Result) then
  3222.       Result := v;
  3223.   end;
  3224. end;
  3225. function TGIFSubImage.GetColorResolution: integer;
  3226. begin
  3227.   Result := ColorMap.BitsPerPixel-1;
  3228. end;
  3229. function TGIFSubImage.GetBitsPerPixel: integer;
  3230. begin
  3231.   Result := ColorMap.BitsPerPixel;
  3232. end;
  3233. function TGIFSubImage.GetBoundsRect: TRect;
  3234. begin
  3235.   Result := Rect(FImageDescriptor.Left,
  3236.     FImageDescriptor.Top,
  3237.     FImageDescriptor.Left+FImageDescriptor.Width,
  3238.     FImageDescriptor.Top+FImageDescriptor.Height);
  3239. end;
  3240. procedure TGIFSubImage.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
  3241. var
  3242.   TooLarge : boolean;
  3243.   Zap : boolean;
  3244. begin
  3245.   Zap := (FImageDescriptor.Width <> Width) or (FImageDescriptor.Height <> AHeight);
  3246.   FImageDescriptor.Left := ALeft;
  3247.   FImageDescriptor.Top := ATop;
  3248.   FImageDescriptor.Width := AWidth;
  3249.   FImageDescriptor.Height := AHeight;
  3250.   // Delete existing image and bitmaps if size has changed
  3251.   if (Zap) then
  3252.   begin
  3253.     FreeBitmap;
  3254.     FreeMask;
  3255.     FreeImage;
  3256.     // ...and allocate a new image
  3257.     NewImage;
  3258.   end;
  3259.   TooLarge := False;
  3260.   // Set width & height if added image is larger than existing images
  3261. {$IFDEF STRICT_MOZILLA}
  3262.   // From Mozilla source:
  3263.   // Work around broken GIF files where the logical screen
  3264.   // size has weird width or height. [...]
  3265.   if (Image.Width < AWidth) or (Image.Height < AHeight) then
  3266.   begin
  3267.     TooLarge := True;
  3268.     Image.Width := AWidth;
  3269.     Image.Height := AHeight;
  3270.     Left := 0;
  3271.     Top := 0;
  3272.   end;
  3273. {$ELSE}
  3274.   if (Image.Width < ALeft+AWidth) then
  3275.   begin
  3276.     if (Image.Width > 0) then
  3277.     begin
  3278.       TooLarge := True;
  3279.       Warning(gsWarning, sBadWidth)
  3280.     end;
  3281.     Image.Width := ALeft+AWidth;
  3282.   end;
  3283.   if (Image.Height < ATop+AHeight) then
  3284.   begin
  3285.     if (Image.Height > 0) then
  3286.     begin
  3287.       TooLarge := True;
  3288.       Warning(gsWarning, sBadHeight)
  3289.     end;
  3290.     Image.Height := ATop+AHeight;
  3291.   end;
  3292. {$ENDIF}
  3293.   if (TooLarge) then
  3294.     Warning(gsWarning, sScreenSizeExceeded);
  3295. end;
  3296. procedure TGIFSubImage.SetBoundsRect(const Value: TRect);
  3297. begin
  3298.   DoSetBounds(Value.Left, Value.Top, Value.Right-Value.Left+1, Value.Bottom-Value.Top+1);
  3299. end;
  3300. function TGIFSubImage.GetClientRect: TRect;
  3301. begin
  3302.   Result := Rect(0, 0, FImageDescriptor.Width, FImageDescriptor.Height);
  3303. end;
  3304. function TGIFSubImage.GetPixel(x, y: integer): BYTE;
  3305. begin
  3306.   if (x < 0) or (x > Width-1) then
  3307.     Error(sBadPixelCoordinates);
  3308.   Result := BYTE(PChar(longInt(Scanline[y]) + x)^);
  3309. end;
  3310. function TGIFSubImage.GetScanline(y: integer): pointer;
  3311. begin
  3312.   if (y < 0) or (y > Height-1) then
  3313.     Error(sBadPixelCoordinates);
  3314.   NeedImage;
  3315.   Result := pointer(longInt(FData) + y * Width);
  3316. end;
  3317. procedure TGIFSubImage.Prepare;
  3318. var
  3319.   Pack : BYTE;
  3320. begin
  3321.   Pack := FImageDescriptor.PackedFields;
  3322.   if (ColorMap.Count > 0) then
  3323.   begin
  3324.     Pack := idLocalColorTable;
  3325.     if (ColorMap.Optimized) then
  3326.       Pack := Pack OR idSort;
  3327.     Pack := (Pack AND NOT(idColorTableSize)) OR (ColorResolution AND idColorTableSize);
  3328.   end else
  3329.     Pack := Pack AND NOT(idLocalColorTable OR idSort OR idColorTableSize);
  3330.   FImageDescriptor.PackedFields := Pack;
  3331. end;
  3332. procedure TGIFSubImage.SaveToStream(Stream: TStream);
  3333. begin
  3334.   FExtensions.SaveToStream(Stream);
  3335.   if (Empty) then
  3336.     exit;
  3337.   Prepare;
  3338.   Stream.Write(FImageDescriptor, sizeof(TImageDescriptor));
  3339.   ColorMap.SaveToStream(Stream);
  3340.   Compress(Stream);
  3341. end;
  3342. procedure TGIFSubImage.LoadFromStream(Stream: TStream);
  3343. var
  3344.   ColorCount : integer;
  3345.   b : BYTE;
  3346. begin
  3347.   Clear;
  3348.   FExtensions.LoadFromStream(Stream, self);
  3349.   // Check for extension without image
  3350.   if (Stream.Read(b, 1) <> 1) then
  3351.     exit;
  3352.   Stream.Seek(-1, soFromCurrent);
  3353.   if (b = bsTrailer) or (b = 0) then
  3354.     exit;
  3355.   ReadCheck(Stream, FImageDescriptor, sizeof(TImageDescriptor));
  3356.   // From Mozilla source:
  3357.   // Work around more broken GIF files that have zero image
  3358.   // width or height
  3359.   if (FImageDescriptor.Height = 0) or (FImageDescriptor.Width = 0) then
  3360.   begin
  3361.     FImageDescriptor.Height := Image.Height;
  3362.     FImageDescriptor.Width := Image.Width;
  3363.     Warning(gsWarning, sScreenSizeExceeded);
  3364.   end;
  3365.   if (FImageDescriptor.PackedFields AND idLocalColorTable = idLocalColorTable) then
  3366.   begin
  3367.     ColorCount := 2 SHL (FImageDescriptor.PackedFields AND idColorTableSize);
  3368.     if (ColorCount < 2) or (ColorCount > 256) then
  3369.       Error(sImageBadColorSize);
  3370.     ColorMap.LoadFromStream(Stream, ColorCount);
  3371.   end;
  3372.   Decompress(Stream);
  3373.   // On-load rendering
  3374.   if (GIFImageRenderOnLoad) then
  3375.     // Touch bitmap to force frame to be rendered
  3376.     Bitmap;
  3377. end;
  3378. procedure TGIFSubImage.AssignTo(Dest: TPersistent);
  3379. begin
  3380.   if (Dest is TBitmap) then
  3381.     Dest.Assign(Bitmap)
  3382.   else
  3383.     inherited AssignTo(Dest);
  3384. end;
  3385. procedure TGIFSubImage.Assign(Source: TPersistent);
  3386. var
  3387.   MemoryStream : TMemoryStream;
  3388.   i : integer;
  3389.   PixelFormat : TPixelFormat;
  3390.   DIBSource : TDIB;
  3391.   ABitmap : TBitmap;
  3392.   procedure Import8Bit(Dest: PChar);
  3393.   var
  3394.     y : integer;
  3395.   begin
  3396.     // Copy colormap
  3397. {$ifdef VER10_PLUS}
  3398.     if (FBitmap.HandleType = bmDIB) then
  3399.       FColorMap.ImportDIBColors(FBitmap.Canvas.Handle)
  3400.     else
  3401. {$ENDIF}
  3402.       FColorMap.ImportPalette(FBitmap.Palette);
  3403.     // Copy pixels
  3404.     for y := 0 to Height-1 do
  3405.     begin
  3406.       if ((y AND $1F) = 0) then
  3407.         Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  3408.       Move(DIBSource.Scanline[y]^, Dest^, Width);
  3409.       inc(Dest, Width);
  3410.     end;
  3411.   end;
  3412.   procedure Import4Bit(Dest: PChar);
  3413.   var
  3414.     x, y : integer;
  3415.     Scanline : PChar;
  3416.   begin
  3417.     // Copy colormap
  3418.     FColorMap.ImportPalette(FBitmap.Palette);
  3419.     // Copy pixels
  3420.     for y := 0 to Height-1 do
  3421.     begin
  3422.       if ((y AND $1F) = 0) then
  3423.         Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  3424.       ScanLine := DIBSource.Scanline[y];
  3425.       for x := 0 to Width-1 do
  3426.       begin
  3427.         if (x AND $01 = 0) then
  3428.           Dest^ := chr(ord(ScanLine^) SHR 4)
  3429.         else
  3430.         begin
  3431.           Dest^ := chr(ord(ScanLine^) AND $0F);
  3432.           inc(ScanLine);
  3433.         end;
  3434.         inc(Dest);
  3435.       end;
  3436.     end;
  3437.   end;
  3438.   procedure Import1Bit(Dest: PChar);
  3439.   var
  3440.     x, y : integer;
  3441.     Scanline : PChar;
  3442.     Bit : integer;
  3443.     Byte : integer;
  3444.   begin
  3445.     // Copy colormap
  3446.     FColorMap.ImportPalette(FBitmap.Palette);
  3447.     // Copy pixels
  3448.     for y := 0 to Height-1 do
  3449.     begin
  3450.       if ((y AND $1F) = 0) then
  3451.         Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  3452.       ScanLine := DIBSource.Scanline[y];
  3453.       x := Width;
  3454.       Bit := 0;
  3455.       Byte := 0; // To avoid compiler warning
  3456.       while (x > 0) do
  3457.       begin
  3458.         if (Bit = 0) then
  3459.         begin
  3460.           Bit := 8;
  3461.           Byte := ord(ScanLine^);
  3462.           inc(Scanline);
  3463.         end;
  3464.         Dest^ := chr((Byte AND $80) SHR 7);
  3465.         Byte := Byte SHL 1;
  3466.         inc(Dest);
  3467.         dec(Bit);
  3468.         dec(x);
  3469.       end;
  3470.     end;
  3471.   end;
  3472.   procedure Import24Bit(Dest: PChar);
  3473.   type
  3474.     TCacheEntry = record
  3475.       Color : TColor;
  3476.       Index : integer;
  3477.     end;
  3478.   const
  3479.     // Size of palette cache. Must be 2^n.
  3480.     // The cache holds the palette index of the last "CacheSize" colors
  3481.     // processed. Hopefully the cache can speed things up a bit... Initial
  3482.     // testing shows that this is indeed the case at least for non-dithered
  3483.     // bitmaps.
  3484.     // All the same, a small hash table would probably be much better.
  3485.     CacheSize = 8;
  3486.   var
  3487.     i : integer;
  3488.     Cache : array[0..CacheSize-1] of TCacheEntry;
  3489.     LastEntry : integer;
  3490.     Scanline : PRGBTriple;
  3491.     Pixel : TColor;
  3492.     RGBTriple : TRGBTriple absolute Pixel;
  3493.     x, y : integer;
  3494.     ColorMap : PColorMap;
  3495.     t : byte;
  3496.   label
  3497.     NextPixel;
  3498.   begin
  3499.     for i := 0 to CacheSize-1 do
  3500.       Cache[i].Index := -1;
  3501.     LastEntry := 0;
  3502.     // Copy all pixels and build colormap
  3503.     for y := 0 to Height-1 do
  3504.     begin
  3505.       if ((y AND $1F) = 0) then
  3506.         Image.Progress(Self, psRunning, MulDiv(y, 100, Height), False, Rect(0,0,0,0), sProgressConverting);
  3507.       ScanLine := DIBSource.Scanline[y];
  3508.       for x := 0 to Width-1 do
  3509.       begin
  3510.         Pixel := 0;
  3511.         RGBTriple := Scanline^;
  3512.         // Scan cache for color from most recently processed color to last
  3513.         // recently processed. This is done because TColorMap.AddUnique is very slow.
  3514.         i := LastEntry;
  3515.         repeat
  3516.           if (Cache[i].Index = -1) then
  3517.             break;
  3518.           if (Cache[i].Color = Pixel) then
  3519.           begin
  3520.             Dest^ := chr(Cache[i].Index);
  3521.             LastEntry := i;
  3522.             goto NextPixel;
  3523.           end;
  3524.           if (i = 0) then
  3525.             i := CacheSize-1
  3526.           else
  3527.             dec(i);
  3528.         until (i = LastEntry);
  3529.         // Color not found in cache, do it the slow way instead
  3530.         Dest^ := chr(FColorMap.AddUnique(Pixel));
  3531.         // Add color and index to cache
  3532.         LastEntry := (LastEntry + 1) AND (CacheSize-1);
  3533.         Cache[LastEntry].Color := Pixel;
  3534.         Cache[LastEntry].Index := ord(Dest^);
  3535.         NextPixel:
  3536.         Inc(Dest);
  3537.         Inc(Scanline);
  3538.       end;
  3539.     end;
  3540.     // Convert colors in colormap from BGR to RGB
  3541.     ColorMap := FColorMap.Data;
  3542.     i := FColorMap.Count;
  3543.     while (i > 0) do
  3544.     begin
  3545.       t := ColorMap^[0].Red;
  3546.       ColorMap^[0].Red := ColorMap^[0].Blue;
  3547.       ColorMap^[0].Blue := t;
  3548.       inc(integer(ColorMap), sizeof(TGIFColor));
  3549.       dec(i);
  3550.     end;
  3551.   end;
  3552.   procedure ImportViaDraw(ABitmap: TBitmap; Graphic: TGraphic);
  3553.   begin
  3554.     ABitmap.Height := Graphic.Height;
  3555.     ABitmap.Width := Graphic.Width;
  3556.     // Note: Disable the call to SafeSetPixelFormat below to import
  3557.     // in max number of colors with the risk of having to use
  3558.     // TCanvas.Pixels to do it (very slow).
  3559.     // Make things a little easier for TGIFSubImage.Assign by converting
  3560.     // pfDevice to a more import friendly format
  3561. {$ifdef SLOW_BUT_SAFE}
  3562.     SafeSetPixelFormat(ABitmap, pf8bit);
  3563. {$else}
  3564. {$ifndef VER9x}
  3565.     SetPixelFormat(ABitmap, pf24bit);
  3566. {$endif}
  3567. {$endif}
  3568.     ABitmap.Canvas.Draw(0, 0, Graphic);
  3569.   end;
  3570.   procedure AddMask(Mask: TBitmap);
  3571.   var
  3572.     DIBReader : TDIBReader;
  3573.     TransparentIndex : integer;
  3574.     i ,
  3575.     j : integer;
  3576.     GIFPixel ,
  3577.     MaskPixel : PChar;
  3578.     WasTransparent : boolean;
  3579.     GCE : TGIFGraphicControlExtension;
  3580.   begin
  3581.     // Optimize colormap to make room for transparent color
  3582.     ColorMap.Optimize;
  3583.     // Can't make transparent if no color or colormap full
  3584.     if (ColorMap.Count = 0) or (ColorMap.Count = 256) then
  3585.       exit;
  3586.     // Add the transparent color to the color map
  3587.     TransparentIndex := ColorMap.Add(TColor(0));
  3588.     WasTransparent := False;
  3589.     DIBReader := TDIBReader.Create(Mask, pf8bit);
  3590.     try
  3591.       for i := 0 to Height-1 do
  3592.       begin
  3593.         MaskPixel := DIBReader.Scanline[i];
  3594.         GIFPixel := Scanline[i];
  3595.         for j := 0 to Width-1 do
  3596.         begin
  3597.           // Change all unmasked pixels to transparent
  3598.           if (MaskPixel^ <> #0) then
  3599.           begin
  3600.             GIFPixel^ := chr(TransparentIndex);
  3601.             WasTransparent := True;
  3602.           end;
  3603.           inc(MaskPixel);
  3604.           inc(GIFPixel);
  3605.         end;
  3606.       end;
  3607.     finally
  3608.       DIBReader.Free;
  3609.     end;
  3610.     // Add a Graphic Control Extension if any part of the mask was transparent
  3611.     if (WasTransparent) then
  3612.     begin
  3613.       GCE := TGIFGraphicControlExtension.Create(self);
  3614.       GCE.Transparent := True;
  3615.       GCE.TransparentColorIndex := TransparentIndex;
  3616.       Extensions.Add(GCE);
  3617.     end else
  3618.       // Otherwise removed the transparency color since it wasn't used
  3619.       ColorMap.Delete(TransparentIndex);
  3620.   end;
  3621.   procedure AddMaskOnly(hMask: hBitmap);
  3622.   var
  3623.     Mask : TBitmap;
  3624.   begin
  3625.     if (hMask = 0) then
  3626.       exit;
  3627.     // Encapsulate the mask
  3628.     Mask := TBitmap.Create;
  3629.     try
  3630.       Mask.Handle := hMask;
  3631.       AddMask(Mask);
  3632.     finally
  3633.       Mask.ReleaseHandle;
  3634.       Mask.Free;
  3635.     end;
  3636.   end;
  3637.   procedure AddIconMask(Icon: TIcon);
  3638.   var
  3639.     IconInfo : TIconInfo;
  3640.   begin
  3641.     if (not GetIconInfo(Icon.Handle, IconInfo)) then
  3642.       exit;
  3643.     // Extract the icon mask
  3644.     AddMaskOnly(IconInfo.hbmMask);
  3645.   end;
  3646.   procedure AddMetafileMask(Metafile: TMetaFile);
  3647.   var
  3648.     Mask1 ,
  3649.     Mask2 : TBitmap;
  3650.     procedure DrawMetafile(ABitmap: TBitmap; Background: TColor);
  3651.     begin
  3652.       ABitmap.Width := Metafile.Width;
  3653.       ABitmap.Height := Metafile.Height;
  3654. {$ifndef VER9x}
  3655.       SetPixelFormat(ABitmap, pf24bit);
  3656. {$endif}
  3657.       ABitmap.Canvas.Brush.Color := Background;
  3658.       ABitmap.Canvas.Brush.Style := bsSolid;
  3659.       ABitmap.Canvas.FillRect(ABitmap.Canvas.ClipRect);
  3660.       ABitmap.Canvas.Draw(0,0, Metafile);
  3661.     end;
  3662.   begin
  3663.     // Create the metafile mask
  3664.     Mask1 := TBitmap.Create;
  3665.     try
  3666.       Mask2 := TBitmap.Create;
  3667.       try
  3668.         DrawMetafile(Mask1, clWhite);
  3669.         DrawMetafile(Mask2, clBlack);
  3670.         Mask1.Canvas.CopyMode := cmSrcInvert;
  3671.         Mask1.Canvas.Draw(0,0, Mask2);
  3672.         AddMask(Mask1);
  3673.       finally
  3674.         Mask2.Free;
  3675.       end;
  3676.     finally
  3677.       Mask1.Free;
  3678.     end;
  3679.   end;
  3680. begin
  3681.   if (Source = self) then
  3682.     exit;
  3683.   if (Source = nil) then
  3684.   begin
  3685.     Clear;
  3686.   end else
  3687.   //
  3688.   // TGIFSubImage import
  3689.   //
  3690.   if (Source is TGIFSubImage) then
  3691.   begin
  3692.     // Zap existing colormap, extensions and bitmap
  3693.     Clear;
  3694.     if (TGIFSubImage(Source).Empty) then
  3695.       exit;
  3696.     // Copy source data
  3697.     FImageDescriptor := TGIFSubImage(Source).FImageDescriptor;
  3698.     FTransparent := TGIFSubImage(Source).Transparent;
  3699.     // Copy image data
  3700.     NewImage;
  3701.     if (FData <> nil) and (TGIFSubImage(Source).Data <> nil) then
  3702.       Move(TGIFSubImage(Source).Data^, FData^, FDataSize);
  3703.     // Copy palette
  3704.     FColorMap.Assign(TGIFSubImage(Source).ColorMap);
  3705.     // Copy extensions
  3706.     if (TGIFSubImage(Source).Extensions.Count > 0) then
  3707.     begin
  3708.       MemoryStream := TMemoryStream.Create;
  3709.       try
  3710.         TGIFSubImage(Source).Extensions.SaveToStream(MemoryStream);
  3711.         MemoryStream.Seek(0, soFromBeginning);
  3712.         Extensions.LoadFromStream(MemoryStream, Self);
  3713.       finally
  3714.         MemoryStream.Free;
  3715.       end;
  3716.     end;
  3717.     // Copy bitmap representation
  3718.     // (Not really nescessary but improves performance if the bitmap is needed
  3719.     // later on)
  3720.     if (TGIFSubImage(Source).HasBitmap) then
  3721.     begin
  3722.       NewBitmap;
  3723.       FBitmap.Assign(TGIFSubImage(Source).Bitmap);
  3724.     end;
  3725.   end else
  3726.   //
  3727.   // Bitmap import
  3728.   //
  3729.   if (Source is TBitmap) then
  3730.   begin
  3731.     // Zap existing colormap, extensions and bitmap
  3732.     Clear;
  3733.     if (TBitmap(Source).Empty) then
  3734.       exit;
  3735.     Width := TBitmap(Source).Width;
  3736.     Height := TBitmap(Source).Height;
  3737.     PixelFormat := GetPixelFormat(TBitmap(Source));
  3738. {$ifdef VER9x}
  3739.     // Delphi 2 TBitmaps are always DDBs. This means that if a 24 bit
  3740.     // bitmap is loaded in 8 bit device mode, TBitmap.PixelFormat will
  3741.     // be pf8bit, but TBitmap.Palette will be 0!
  3742.     if (TBitmap(Source).Palette = 0) then
  3743.       PixelFormat := pfDevice;
  3744. {$endif}
  3745.     if (PixelFormat > pf8bit) or (PixelFormat = pfDevice) then
  3746.     begin
  3747.       // Convert image to 8 bits/pixel or less
  3748.       FBitmap := ReduceColors(TBitmap(Source), Image.ColorReduction,
  3749.         Image.DitherMode, Image.ReductionBits, 0);
  3750.       PixelFormat := GetPixelFormat(FBitmap);