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

RichEdit

开发平台:

Delphi

  1. unit GIFImage;
  2. ////////////////////////////////////////////////////////////////////////////////
  3. //                                                                            //
  4. // Project: GIF Graphics Object                                           //
  5. // Module: gifimage                                                      //
  6. // Description: TGraphic implementation of the GIF89a graphics format         //
  7. // Version: 2.2                                                           //
  8. // Release: 5                                                             //
  9. // Date: 23-MAY-1999                                                   //
  10. // Target: Win32, Delphi 2, 3, 4 & 5, C++ Builder 3 & 4                  //
  11. // Author(s): anme: Anders Melander, anders@melander.dk                     //
  12. // fila: Filip Larsen                                            //
  13. // rps: Reinier Sterkenburg                                      //
  14. // Copyright: (c) 1997-99 Anders Melander.                                  //
  15. // All rights reserved.                                          //
  16. // Formatting: 2 space indent, 8 space tabs, 80 columns.                     //
  17. //                                                                            //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // Delphi 6 updates and modifications by Alexey Barkovoy (clootie@reactor.ru) //
  20. // Date: 22-Dec-2001                                                          //
  21. // Date: 13-Jun-2003 - Updated for Delphi7 and (possible) up                  //
  22. // Download from: http://clootie.narod.ru/delphi/download_vcl.html            //
  23. ////////////////////////////////////////////////////////////////////////////////
  24. //                                                                            //
  25. // Please read the "Conditions of use" in the release notes.                  //
  26. //                                                                            //
  27. ////////////////////////////////////////////////////////////////////////////////
  28. // Known problems:
  29. //
  30. // * The combination of buffered, tiled and transparent draw will display the
  31. //   background incorrectly (scaled).
  32. //   If this is a problem for you, use non-buffered (goDirectDraw) drawing
  33. //   instead.
  34. //
  35. // * The combination of non-buffered, transparent and stretched draw is
  36. //   sometimes distorted with a pattern effect when the image is displayed
  37. //   smaller than the real size (shrinked).
  38. //
  39. // * Buffered display flickers when TGIFImage is used by a transparent TImage
  40. //   component.
  41. //   This is a problem with TImage caused by the fact that TImage was designed
  42. //   with static images in mind. Not much I can do about it.
  43. //
  44. ////////////////////////////////////////////////////////////////////////////////
  45. // To do (in rough order of priority):
  46. // { TODO -oanme -cFeature : TImage hook for destroy notification. }
  47. // { TODO -oanme -cFeature : TBitmap pool to limit resource consumption on Win95/98. }
  48. // { TODO -oanme -cImprovement : Make BitsPerPixel property writable. }
  49. // { TODO -oanme -cFeature : Visual GIF component. }
  50. // { TODO -oanme -cImprovement : Easier method to determine DrawPainter status. }
  51. // { TODO -oanme -cFeature : Import to 256+ color GIF. }
  52. // { TODO -oanme -cFeature : Make some of TGIFImage's properties persistent (DrawOptions etc). }
  53. // { TODO -oanme -cFeature : Add TGIFImage.Persistent property. Should save published properties in application extension when this options is set. }
  54. // { TODO -oanme -cBugFix : Solution for background buffering in scrollbox. }
  55. //
  56. //////////////////////////////////////////////////////////////////////////////////
  57. {$ifdef BCB}
  58. {$ObjExportAll On}
  59. {$endif}
  60. interface
  61. ////////////////////////////////////////////////////////////////////////////////
  62. //
  63. // Conditional Compiler Symbols
  64. //
  65. ////////////////////////////////////////////////////////////////////////////////
  66. (*
  67.   DEBUG Must be defined if any of the DEBUG_xxx
  68.    symbols are defined.
  69.                                 If the symbol is defined the source will not be
  70.                                 optimized and overflow- and range checks will be
  71.                                 enabled.
  72.   DEBUG_HASHPERFORMANCE Calculates hash table performance data.
  73.   DEBUG_HASHFILLFACTOR Calculates fill factor of hash table -
  74.    Interferes with DEBUG_HASHPERFORMANCE.
  75.   DEBUG_COMPRESSPERFORMANCE Calculates LZW compressor performance data.
  76.   DEBUG_DECOMPRESSPERFORMANCE Calculates LZW decompressor performance data.
  77.   DEBUG_DITHERPERFORMANCE Calculates color reduction performance data.
  78.   DEBUG_DRAWPERFORMANCE Calculates low level drawing performance data.
  79.    The performance data for DEBUG_DRAWPERFORMANCE
  80.                                 will be displayed when you press the Ctrl key.
  81.   DEBUG_RENDERPERFORMANCE Calculates performance data for the GIF to
  82.    bitmap converter.
  83.    The performance data for DEBUG_DRAWPERFORMANCE
  84.                                 will be displayed when you press the Ctrl key.
  85.   GIF_NOSAFETY Define this symbol to disable overflow- and
  86. range checks.
  87.                                 Ignored if the DEBUG symbol is defined.
  88.   STRICT_MOZILLA Define to mimic Mozilla as closely as possible.
  89.    If not defined, a slightly more "optimal"
  90.                                 implementation is used (IMHO).
  91.   FAST_AS_HELL Define this symbol to use strictly GIF compliant
  92.    (but too fast) animation timing.
  93.                                 Since our paint routines are much faster and
  94.                                 more precise timed than Mozilla's, the standard
  95.                                 GIF and Mozilla values causes animations to loop
  96.                                 faster than they would in Mozilla.
  97.                                 If the symbol is _not_ defined, an alternative
  98.                                 set of tweaked timing values will be used.
  99.                                 The tweaked values are not optimal but are based
  100.                                 on tests performed on my reference system:
  101.                                 - Windows 95
  102.                                 - 133 MHz Pentium
  103.                                 - 64Mb RAM
  104.                                 - Diamond Stealth64/V3000
  105.                                 - 1600*1200 in 256 colors
  106.                                 The alternate values can be modified if you are
  107.                                 not satisfied with my defaults (they can be
  108.                                 found a few pages down).
  109.   REGISTER_TGIFIMAGE            Define this symbol to register TGIFImage with
  110.    the TPicture class and integrate with TImage.
  111.                                 This is required to be able to display GIFs in
  112.                                 the TImage component.
  113.                                 The symbol is defined by default.
  114.                                 Undefine if you use another GIF library to
  115.                                 provide GIF support for TImage.
  116.   PIXELFORMAT_TOO_SLOW When this symbol is defined, the internal
  117.    PixelFormat routines are used in some places
  118.                                 instead of TBitmap.PixelFormat.
  119.                                 The current implementation (Delphi4, Builder 3)
  120.                                 of TBitmap.PixelFormat can in some situation
  121.                                 degrade performance.
  122.                                 The symbol is defined by default.
  123.   CREATEDIBSECTION_SLOW If this symbol is defined, TDIBWriter will
  124.    use global memory as scanline storage, instead
  125.                                 of a DIB section.
  126.                                 Benchmarks have shown that a DIB section is
  127.                                 twice as slow as global memory.
  128.                                 The symbol is defined by default.
  129.                                 The symbol requires that PIXELFORMAT_TOO_SLOW
  130.                                 is defined.
  131.   SERIALIZE_RENDER Define this symbol to serialize threaded
  132.    GIF to bitmap rendering.
  133.                                 When a GIF is displayed with the goAsync option
  134.                                 (the default), the GIF to bitmap rendering is
  135.                                 executed in the context of the draw thread.
  136.                                 If more than one thread is drawing the same GIF
  137.                                 or the GIF is being modified while it is
  138.                                 animating, the GIF to bitmap rendering should be
  139.                                 serialized to guarantee that the bitmap isn't
  140.                                 modified by more than one thread at a time. If
  141.                                 SERIALIZE_RENDER is defined, the draw threads
  142.                                 uses TThread.Synchronize to serialize GIF to
  143.                                 bitmap rendering.
  144. *)
  145. {$DEFINE REGISTER_TGIFIMAGE}
  146. {$DEFINE PIXELFORMAT_TOO_SLOW}
  147. {$DEFINE CREATEDIBSECTION_SLOW}
  148. ////////////////////////////////////////////////////////////////////////////////
  149. //
  150. // Determine Delphi and C++ Builder version
  151. //
  152. ////////////////////////////////////////////////////////////////////////////////
  153. // Delphi 1.x
  154. {$IFDEF VER80}
  155.   'Error: TGIFImage does not support Delphi 1.x'
  156. {$ENDIF}
  157. // Delphi 2.x
  158. {$IFDEF VER90}
  159.   {$DEFINE VER9x}
  160. {$ENDIF}
  161. // C++ Builder 1.x
  162. {$IFDEF VER93}
  163.   // Good luck...
  164.   {$DEFINE VER9x}
  165. {$ENDIF}
  166. // Delphi 3.x
  167. {$IFDEF VER100}
  168.   {$DEFINE VER10_PLUS}
  169.   {$DEFINE D3_BCB3}
  170. {$ENDIF}
  171. // C++ Builder 3.x
  172. {$IFDEF VER110}
  173.   {$DEFINE VER10_PLUS}
  174.   {$DEFINE VER11_PLUS}
  175.   {$DEFINE D3_BCB3}
  176.   {$DEFINE BAD_STACK_ALIGNMENT}
  177. {$ENDIF}
  178. // Delphi 4.x
  179. {$IFDEF VER120}
  180.   {$DEFINE VER10_PLUS}
  181.   {$DEFINE VER11_PLUS}
  182.   {$DEFINE VER12_PLUS}
  183.   {$DEFINE BAD_STACK_ALIGNMENT}
  184. {$ENDIF}
  185. // C++ Builder 4.x
  186. {$IFDEF VER125}
  187.   {$DEFINE VER10_PLUS}
  188.   {$DEFINE VER11_PLUS}
  189.   {$DEFINE VER12_PLUS}
  190.   {$DEFINE VER125_PLUS}
  191.   {$DEFINE BAD_STACK_ALIGNMENT}
  192. {$ENDIF}
  193. // Delphi 5.x
  194. {$IFDEF VER130}
  195.   {$DEFINE VER10_PLUS}
  196.   {$DEFINE VER11_PLUS}
  197.   {$DEFINE VER12_PLUS}
  198.   {$DEFINE VER125_PLUS}
  199.   {$DEFINE VER13_PLUS}
  200.   {$DEFINE BAD_STACK_ALIGNMENT}
  201. {$ENDIF}
  202. // Delphi 6.x
  203. {$IFDEF VER140}
  204.   {$DEFINE VER10_PLUS}
  205.   {$DEFINE VER11_PLUS}
  206.   {$DEFINE VER12_PLUS}
  207.   {$DEFINE VER125_PLUS}
  208.   {$DEFINE VER13_PLUS}
  209.   {$DEFINE VER14_PLUS}
  210.   {$DEFINE BAD_STACK_ALIGNMENT}
  211. {$ENDIF}
  212. // Delphi 7.x
  213. {$IFDEF VER150}
  214.   {$DEFINE VER10_PLUS}
  215.   {$DEFINE VER11_PLUS}
  216.   {$DEFINE VER12_PLUS}
  217.   {$DEFINE VER125_PLUS}
  218.   {$DEFINE VER13_PLUS}
  219.   {$DEFINE VER14_PLUS}
  220.   {$DEFINE VER15_PLUS}
  221.   {$DEFINE BAD_STACK_ALIGNMENT}
  222. {$ENDIF}
  223. {$IFDEF conditionalexpressions}
  224. {$IF RTLVersion >= 14.0} // Should be Delphi6 and up
  225.   {$DEFINE VER10_PLUS}
  226.   {$DEFINE VER11_PLUS}
  227.   {$DEFINE VER12_PLUS}
  228.   {$DEFINE VER125_PLUS}
  229.   {$DEFINE VER13_PLUS}
  230.   {$DEFINE VER14_PLUS}
  231.   {$DEFINE VER15_PLUS}
  232.   {$DEFINE BAD_STACK_ALIGNMENT} // Not sure is this still needed
  233. {$IFEND}
  234. {$ENDIF}
  235. // Unknown compiler version - assume D4 compatible
  236. {$IFNDEF VER9x}
  237.   {$IFNDEF VER10_PLUS}
  238.     {$DEFINE VER10_PLUS}
  239.     {$DEFINE VER11_PLUS}
  240.     {$DEFINE VER12_PLUS}
  241.     {$DEFINE BAD_STACK_ALIGNMENT}
  242.   {$ENDIF}
  243. {$ENDIF}
  244. ////////////////////////////////////////////////////////////////////////////////
  245. //
  246. // Compiler Options required to compile this library
  247. //
  248. ////////////////////////////////////////////////////////////////////////////////
  249. {$A+,B-,H+,J+,K-,M-,T-,X+}
  250. // Debug control - You can safely change these settings
  251. {$IFDEF DEBUG}
  252.   {$C+} // ASSERTIONS
  253.   {$O-} // OPTIMIZATION
  254.   {$Q+} // OVERFLOWCHECKS
  255.   {$R+} // RANGECHECKS
  256. {$ELSE}
  257.   {$C-} // ASSERTIONS
  258.   {$IFDEF GIF_NOSAFETY}
  259.     {$Q-}// OVERFLOWCHECKS
  260.     {$R-}// RANGECHECKS
  261.   {$ENDIF}
  262. {$ENDIF}
  263. // Special options for Time2Help parser
  264. {$ifdef TIME2HELP}
  265. {$UNDEF PIXELFORMAT_TOO_SLOW}
  266. {$endif}
  267. ////////////////////////////////////////////////////////////////////////////////
  268. //
  269. // External dependecies
  270. //
  271. ////////////////////////////////////////////////////////////////////////////////
  272. uses
  273.   sysutils,
  274.   Windows,
  275.   Graphics,
  276.   Classes;
  277. ////////////////////////////////////////////////////////////////////////////////
  278. //
  279. // TGIFImage library version
  280. //
  281. ////////////////////////////////////////////////////////////////////////////////
  282. const
  283.   GIFVersion = $0202;
  284.   GIFVersionMajor = 2;
  285.   GIFVersionMinor = 2;
  286.   GIFVersionRelease = 5;
  287. ////////////////////////////////////////////////////////////////////////////////
  288. //
  289. // Misc constants and support types
  290. //
  291. ////////////////////////////////////////////////////////////////////////////////
  292. const
  293.   GIFMaxColors = 256; // Max number of colors supported by GIF
  294.    // Don't bother changing this value!
  295.   BitmapAllocationThreshold = 500000; // Bitmap pixel count limit at which
  296.    // a newly allocated bitmap will be
  297.                                         // converted to 1 bit format before
  298.                                         // being resized and converted to 8 bit.
  299. var
  300. {$IFDEF FAST_AS_HELL}
  301.   GIFDelayExp: integer = 10; // Delay multiplier in mS.
  302. {$ELSE}
  303.   GIFDelayExp: integer = 12; // Delay multiplier in mS. Tweaked.
  304. {$ENDIF}
  305. // * GIFDelayExp:
  306.    // The following delay values should all
  307.                                         // be multiplied by this value to
  308.                                         // calculate the effective time (in mS).
  309.                                         // According to the GIF specs, this
  310.                                         // value should be 10.
  311.                                         // Since our paint routines are much
  312.                                         // faster than Mozilla's, you might need
  313.                                         // to increase this value if your
  314.                                         // animations loops too fast. The
  315.                                         // optimal value is impossible to
  316.                                         // determine since it depends on the
  317.                                         // speed of the CPU, the viceo card,
  318.                                         // memory and many other factors.
  319.   GIFDefaultDelay: integer = 10; // * GIFDefaultDelay:
  320.    // Default animation delay.
  321.    // This value is used if no GCE is
  322.                                         // defined.
  323.                                         // (10 = 100 mS)
  324. {$IFDEF FAST_AS_HELL}
  325.   GIFMinimumDelay: integer = 1; // Minimum delay (from Mozilla source).
  326.    // (1 = 10 mS)
  327. {$ELSE}
  328.   GIFMinimumDelay: integer = 3; // Minimum delay - Tweaked.
  329. {$ENDIF}
  330. // * GIFMinimumDelay:
  331. // The minumum delay used in the Mozilla
  332.                                         // source is 10mS. This corresponds to a
  333.                                         // value of 1. However, since our paint
  334.                                         // routines are much faster than
  335.                                         // Mozilla's, a value of 3 or 4 gives
  336.                                         // better results.
  337.   GIFMaximumDelay: integer = 1000; // * GIFMaximumDelay:
  338.    // Maximum delay when painter is running
  339.    // in main thread (goAsync is not set).
  340.                                         // This value guarantees that a very
  341.                                         // long and slow GIF does not hang the
  342.                                         // system.
  343.                                         // (1000 = 10000 mS = 10 Seconds)
  344. type
  345.   TGIFVersion = (gvUnknown, gv87a, gv89a);
  346.   TGIFVersionRec = array[0..2] of char;
  347. const
  348.   GIFVersions : array[gv87a..gv89a] of TGIFVersionRec = ('87a', '89a');
  349. type
  350.   // TGIFImage mostly throws exceptions of type GIFException
  351.   GIFException = class(EInvalidGraphic);
  352.   // Severity level as indicated in the Warning methods and the OnWarning event
  353.   TGIFSeverity = (gsInfo, gsWarning, gsError);
  354. ////////////////////////////////////////////////////////////////////////////////
  355. //
  356. // Delphi 2.x support
  357. //
  358. ////////////////////////////////////////////////////////////////////////////////
  359. {$IFDEF VER9x}
  360. // Delphi 2 doesn't support TBitmap.PixelFormat
  361. {$DEFINE PIXELFORMAT_TOO_SLOW}
  362. type
  363.   // TThreadList from Delphi 3 classes.pas
  364.   TThreadList = class
  365.   private
  366.     FList: TList;
  367.     FLock: TRTLCriticalSection;
  368.   public
  369.     constructor Create;
  370.     destructor Destroy; override;
  371.     procedure Add(Item: Pointer);
  372.     procedure Clear;
  373.     function  LockList: TList;
  374.     procedure Remove(Item: Pointer);
  375.     procedure UnlockList;
  376.   end;
  377.   // From Delphi 3 sysutils.pas
  378.   EOutOfMemory = class(Exception);
  379.   // From Delphi 3 classes.pas
  380.   EOutOfResources = class(EOutOfMemory);
  381.   // From Delphi 3 windows.pas
  382.   PMaxLogPalette = ^TMaxLogPalette;
  383.   TMaxLogPalette = packed record
  384.     palVersion: Word;
  385.     palNumEntries: Word;
  386.     palPalEntry: array [Byte] of TPaletteEntry;
  387.   end; { TMaxLogPalette }
  388.   // From Delphi 3 graphics.pas. Used by the D3 TGraphic class.
  389.   TProgressStage = (psStarting, psRunning, psEnding);
  390.   TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
  391.     PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string) of object;
  392.   // From Delphi 3 windows.pas
  393.   PRGBTriple = ^TRGBTriple;
  394. {$ENDIF}
  395. ////////////////////////////////////////////////////////////////////////////////
  396. //
  397. // Forward declarations
  398. //
  399. ////////////////////////////////////////////////////////////////////////////////
  400. type
  401.   TGIFImage = class;
  402.   TGIFSubImage = class;
  403. ////////////////////////////////////////////////////////////////////////////////
  404. //
  405. // TGIFItem
  406. //
  407. ////////////////////////////////////////////////////////////////////////////////
  408.   TGIFItem = class(TPersistent)
  409.   private
  410.     FGIFImage: TGIFImage;
  411.   protected
  412.     function GetVersion: TGIFVersion; virtual;
  413.     procedure Warning(Severity: TGIFSeverity; Message: string); virtual;
  414.   public
  415.     constructor Create(GIFImage: TGIFImage); virtual;
  416.     procedure SaveToStream(Stream: TStream); virtual; abstract;
  417.     procedure LoadFromStream(Stream: TStream); virtual; abstract;
  418.     procedure SaveToFile(const Filename: string); virtual;
  419.     procedure LoadFromFile(const Filename: string); virtual;
  420.     property Version: TGIFVersion read GetVersion;
  421.     property Image: TGIFImage read FGIFImage;
  422.   end;
  423. ////////////////////////////////////////////////////////////////////////////////
  424. //
  425. // TGIFList
  426. //
  427. ////////////////////////////////////////////////////////////////////////////////
  428.   TGIFList = class(TPersistent)
  429.   private
  430.     FItems: TList;
  431.     FImage: TGIFImage;
  432.   protected
  433.     function GetItem(Index: Integer): TGIFItem;
  434.     procedure SetItem(Index: Integer; Item: TGIFItem);
  435.     function GetCount: Integer;
  436.     procedure Warning(Severity: TGIFSeverity; Message: string); virtual;
  437.   public
  438.     constructor Create(Image: TGIFImage);
  439.     destructor Destroy; override;
  440.     function Add(Item: TGIFItem): Integer;
  441.     procedure Clear;
  442.     procedure Delete(Index: Integer);
  443.     procedure Exchange(Index1, Index2: Integer);
  444.     function First: TGIFItem;
  445.     function IndexOf(Item: TGIFItem): Integer;
  446.     procedure Insert(Index: Integer; Item: TGIFItem);
  447.     function Last: TGIFItem;
  448.     procedure Move(CurIndex, NewIndex: Integer);
  449.     function Remove(Item: TGIFItem): Integer;
  450.     procedure SaveToStream(Stream: TStream); virtual;
  451.     procedure LoadFromStream(Stream: TStream; Parent: TObject); virtual; abstract;
  452.     property Items[Index: Integer]: TGIFItem read GetItem write SetItem; default;
  453.     property Count: Integer read GetCount;
  454.     property List: TList read FItems;
  455.     property Image: TGIFImage read FImage;
  456.   end;
  457. ////////////////////////////////////////////////////////////////////////////////
  458. //
  459. // TGIFColorMap
  460. //
  461. ////////////////////////////////////////////////////////////////////////////////
  462.   // One way to do it:
  463.   //  TBaseColor = (bcRed, bcGreen, bcBlue);
  464.   //  TGIFColor = array[bcRed..bcBlue] of BYTE;
  465.   // Another way:
  466.   TGIFColor = packed record
  467.     Red: byte;
  468.     Green: byte;
  469.     Blue: byte;
  470.   end;
  471.   TColorMap = packed array[0..GIFMaxColors-1] of TGIFColor;
  472.   PColorMap = ^TColorMap;
  473.   TUsageCount = record
  474.     Count : integer; // # of pixels using color index
  475.     Index : integer; // Color index
  476.   end;
  477.   TColormapHistogram = array[0..255] of TUsageCount;
  478.   TColormapReverse = array[0..255] of byte;
  479.   TGIFColorMap = class(TPersistent)
  480.   private
  481.     FColorMap : PColorMap;
  482.     FCount : integer;
  483.     FCapacity : integer;
  484.     FOptimized : boolean;
  485.   protected
  486.     function GetColor(Index: integer): TColor;
  487.     procedure SetColor(Index: integer; Value: TColor);
  488.     function GetBitsPerPixel: integer;
  489.     function DoOptimize: boolean;
  490.     procedure SetCapacity(Size: integer);
  491.     procedure Warning(Severity: TGIFSeverity; Message: string); virtual; abstract;
  492.     procedure BuildHistogram(var Histogram: TColormapHistogram); virtual; abstract;
  493.     procedure MapImages(var Map: TColormapReverse); virtual; abstract;
  494.   public
  495.     constructor Create;
  496.     destructor Destroy; override;
  497.     class function Color2RGB(Color: TColor): TGIFColor;
  498.     class function RGB2Color(Color: TGIFColor): TColor;
  499.     procedure SaveToStream(Stream: TStream);
  500.     procedure LoadFromStream(Stream: TStream; Count: integer);
  501.     procedure Assign(Source: TPersistent); override;
  502.     function IndexOf(Color: TColor): integer;
  503.     function Add(Color: TColor): integer;
  504.     function AddUnique(Color: TColor): integer;
  505.     procedure Delete(Index: integer);
  506.     procedure Clear;
  507.     function Optimize: boolean; virtual; abstract;
  508.     procedure Changed; virtual; abstract;
  509.     procedure ImportPalette(Palette: HPalette);
  510.     procedure ImportColorTable(Pal: pointer; Count: integer);
  511.     procedure ImportDIBColors(Handle: HDC);
  512.     procedure ImportColorMap(Map: TColorMap; Count: integer);
  513.     function ExportPalette: HPalette;
  514.     property Colors[Index: integer]: TColor read GetColor write SetColor; default;
  515.     property Data: PColorMap read FColorMap;
  516.     property Count: integer read FCount;
  517.     property Optimized: boolean read FOptimized write FOptimized;
  518.     property BitsPerPixel: integer read GetBitsPerPixel;
  519.   end;
  520. ////////////////////////////////////////////////////////////////////////////////
  521. //
  522. // TGIFHeader
  523. //
  524. ////////////////////////////////////////////////////////////////////////////////
  525.   TLogicalScreenDescriptor = packed record
  526.     ScreenWidth: word;              { logical screen width }
  527.     ScreenHeight: word;             { logical screen height }
  528.     PackedFields: byte;             { packed fields }
  529.     BackgroundColorIndex: byte;     { index to global color table }
  530.     AspectRatio: byte;              { actual ratio = (AspectRatio + 15) / 64 }
  531.   end;
  532.   TGIFHeader = class(TGIFItem)
  533.   private
  534.     FLogicalScreenDescriptor: TLogicalScreenDescriptor;
  535.     FColorMap : TGIFColorMap;
  536.     procedure Prepare;
  537.   protected
  538.     function GetVersion: TGIFVersion; override;
  539.     function GetBackgroundColor: TColor;
  540.     procedure SetBackgroundColor(Color: TColor);
  541.     procedure SetBackgroundColorIndex(Index: BYTE);
  542.     function GetBitsPerPixel: integer;
  543.     function GetColorResolution: integer;
  544.   public
  545.     constructor Create(GIFImage: TGIFImage); override;
  546.     destructor Destroy; override;
  547.     procedure Assign(Source: TPersistent); override;
  548.     procedure SaveToStream(Stream: TStream); override;
  549.     procedure LoadFromStream(Stream: TStream); override;
  550.     procedure Clear;
  551.     property Version: TGIFVersion read GetVersion;
  552.     property Width: WORD read FLogicalScreenDescriptor.ScreenWidth
  553.                          write FLogicalScreenDescriptor.ScreenWidth;
  554.     property Height: WORD read FLogicalScreenDescriptor.ScreenHeight
  555.                           write FLogicalScreenDescriptor.Screenheight;
  556.     property BackgroundColorIndex: BYTE read FLogicalScreenDescriptor.BackgroundColorIndex
  557.                                         write SetBackgroundColorIndex;
  558.     property BackgroundColor: TColor read GetBackgroundColor
  559.                                      write SetBackgroundColor;
  560.     property AspectRatio: BYTE read FLogicalScreenDescriptor.AspectRatio
  561.                                write FLogicalScreenDescriptor.AspectRatio;
  562.     property ColorMap: TGIFColorMap read FColorMap;
  563.     property BitsPerPixel: integer read GetBitsPerPixel;
  564.     property ColorResolution: integer read GetColorResolution;
  565.   end;
  566. ////////////////////////////////////////////////////////////////////////////////
  567. //
  568. //                      TGIFExtension
  569. //
  570. ////////////////////////////////////////////////////////////////////////////////
  571.   TGIFExtensionType = BYTE;
  572.   TGIFExtension = class;
  573.   TGIFExtensionClass = class of TGIFExtension;
  574.   TGIFGraphicControlExtension = class;
  575.   TGIFExtension = class(TGIFItem)
  576.   private
  577.     FSubImage: TGIFSubImage;
  578.   protected
  579.     function GetExtensionType: TGIFExtensionType; virtual; abstract;
  580.     function GetVersion: TGIFVersion; override;
  581.     function DoReadFromStream(Stream: TStream): TGIFExtensionType;
  582.     class procedure RegisterExtension(elabel: BYTE; eClass: TGIFExtensionClass);
  583.     class function FindExtension(Stream: TStream): TGIFExtensionClass;
  584.     class function FindSubExtension(Stream: TStream): TGIFExtensionClass; virtual;
  585.   public
  586.      // Ignore compiler warning about hiding base class constructor
  587.     constructor Create(ASubImage: TGIFSubImage); {$IFDEF VER12_PLUS} reintroduce; {$ENDIF} virtual;
  588.     destructor Destroy; override;
  589.     procedure SaveToStream(Stream: TStream); override;
  590.     procedure LoadFromStream(Stream: TStream); override;
  591.     property ExtensionType: TGIFExtensionType read GetExtensionType;
  592.     property SubImage: TGIFSubImage read FSubImage;
  593.   end;
  594. ////////////////////////////////////////////////////////////////////////////////
  595. //
  596. // TGIFSubImage
  597. //
  598. ////////////////////////////////////////////////////////////////////////////////
  599.   TGIFExtensionList = class(TGIFList)
  600.   protected
  601.     function GetExtension(Index: Integer): TGIFExtension;
  602.     procedure SetExtension(Index: Integer; Extension: TGIFExtension);
  603.   public
  604.     procedure LoadFromStream(Stream: TStream; Parent: TObject); override;
  605.     property Extensions[Index: Integer]: TGIFExtension read GetExtension write SetExtension; default;
  606.   end;
  607.   TImageDescriptor = packed record
  608.     Separator: byte; { fixed value of ImageSeparator }
  609.     Left: word; { Column in pixels in respect to left edge of logical screen }
  610.     Top: word; { row in pixels in respect to top of logical screen }
  611.     Width: word; { width of image in pixels }
  612.     Height: word; { height of image in pixels }
  613.     PackedFields: byte; { Bit fields }
  614.   end;
  615.   TGIFSubImage = class(TGIFItem)
  616.   private
  617.     FBitmap : TBitmap;
  618.     FMask : HBitmap;
  619.     FNeedMask : boolean;
  620.     FLocalPalette : HPalette;
  621.     FData : PChar;
  622.     FDataSize : integer;
  623.     FColorMap : TGIFColorMap;
  624.     FImageDescriptor : TImageDescriptor;
  625.     FExtensions : TGIFExtensionList;
  626.     FTransparent : boolean;
  627.     FGCE : TGIFGraphicControlExtension;
  628.     procedure Prepare;
  629.     procedure Compress(Stream: TStream);
  630.     procedure Decompress(Stream: TStream);
  631.   protected
  632.     function GetVersion: TGIFVersion; override;
  633.     function GetInterlaced: boolean;
  634.     procedure SetInterlaced(Value: boolean);
  635.     function GetColorResolution: integer;
  636.     function GetBitsPerPixel: integer;
  637.     procedure AssignTo(Dest: TPersistent); override;
  638.     function DoGetBitmap: TBitmap;
  639.     function DoGetDitherBitmap: TBitmap;
  640.     function GetBitmap: TBitmap;
  641.     procedure SetBitmap(Value: TBitmap);
  642.     procedure FreeMask;
  643.     function GetEmpty: Boolean;
  644.     function GetPalette: HPALETTE;
  645.     procedure SetPalette(Value: HPalette);
  646.     function GetActiveColorMap: TGIFColorMap;
  647.     function GetBoundsRect: TRect;
  648.     procedure SetBoundsRect(const Value: TRect);
  649.     procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
  650.     function GetClientRect: TRect;
  651.     function GetPixel(x, y: integer): BYTE;
  652.     function GetScanline(y: integer): pointer;
  653.     procedure NewBitmap;
  654.     procedure FreeBitmap;
  655.     procedure NewImage;
  656.     procedure FreeImage;
  657.     procedure NeedImage;
  658.     function ScaleRect(DestRect: TRect): TRect;
  659.     function HasMask: boolean;
  660.     function GetBounds(Index: integer): WORD;
  661.     procedure SetBounds(Index: integer; Value: WORD);
  662.     function GetHasBitmap: boolean;
  663.     procedure SetHasBitmap(Value: boolean);
  664.   public
  665.     constructor Create(GIFImage: TGIFImage); override;
  666.     destructor Destroy; override;
  667.     procedure Clear;
  668.     procedure SaveToStream(Stream: TStream); override;
  669.     procedure LoadFromStream(Stream: TStream); override;
  670.     procedure Assign(Source: TPersistent); override;
  671.     procedure Draw(ACanvas: TCanvas; const Rect: TRect;
  672.       DoTransparent, DoTile: boolean);
  673.     procedure StretchDraw(ACanvas: TCanvas; const Rect: TRect;
  674.       DoTransparent, DoTile: boolean);
  675.     procedure Crop;
  676.     procedure Merge(Previous: TGIFSubImage);
  677.     property HasBitmap: boolean read GetHasBitmap write SetHasBitmap;
  678.     property Left: WORD index 1 read GetBounds write SetBounds;
  679.     property Top: WORD index 2 read GetBounds write SetBounds;
  680.     property Width: WORD index 3 read GetBounds write SetBounds;
  681.     property Height: WORD index 4 read GetBounds write SetBounds;
  682.     property BoundsRect: TRect read GetBoundsRect write SetBoundsRect;
  683.     property ClientRect: TRect read GetClientRect;
  684.     property Interlaced: boolean read GetInterlaced write SetInterlaced;
  685.     property ColorMap: TGIFColorMap read FColorMap;
  686.     property ActiveColorMap: TGIFColorMap read GetActiveColorMap;
  687.     property Data: PChar read FData;
  688.     property DataSize: integer read FDataSize;
  689.     property Extensions: TGIFExtensionList read FExtensions;
  690.     property Version: TGIFVersion read GetVersion;
  691.     property ColorResolution: integer read GetColorResolution;
  692.     property BitsPerPixel: integer read GetBitsPerPixel;
  693.     property Bitmap: TBitmap read GetBitmap write SetBitmap;
  694.     property Mask: HBitmap read FMask;
  695.     property Palette: HPALETTE read GetPalette write SetPalette;
  696.     property Empty: boolean read GetEmpty;
  697.     property Transparent: boolean read FTransparent;
  698.     property GraphicControlExtension: TGIFGraphicControlExtension read FGCE;
  699.     property Pixels[x, y: integer]: BYTE read GetPixel;
  700.     property Scanline[y: integer]: pointer read GetScanline;
  701.   end;
  702. ////////////////////////////////////////////////////////////////////////////////
  703. //
  704. //                      TGIFTrailer
  705. //
  706. ////////////////////////////////////////////////////////////////////////////////
  707.   TGIFTrailer = class(TGIFItem)
  708.     procedure SaveToStream(Stream: TStream); override;
  709.     procedure LoadFromStream(Stream: TStream); override;
  710.   end;
  711. ////////////////////////////////////////////////////////////////////////////////
  712. //
  713. //                      TGIFGraphicControlExtension
  714. //
  715. ////////////////////////////////////////////////////////////////////////////////
  716.   // Graphic Control Extension block a.k.a GCE
  717.   TGIFGCERec = packed record
  718.     BlockSize: byte;         { should be 4 }
  719.     PackedFields: Byte;
  720.     DelayTime: Word;         { in centiseconds }
  721.     TransparentColorIndex: Byte;
  722.     Terminator: Byte;
  723.   end;
  724.   TDisposalMethod = (dmNone, dmNoDisposal, dmBackground, dmPrevious);
  725.   TGIFGraphicControlExtension = class(TGIFExtension)
  726.   private
  727.     FGCExtension: TGIFGCERec;
  728.   protected
  729.     function GetExtensionType: TGIFExtensionType; override;
  730.     function GetTransparent: boolean;
  731.     procedure SetTransparent(Value: boolean);
  732.     function GetTransparentColor: TColor;
  733.     procedure SetTransparentColor(Color: TColor);
  734.     function GetTransparentColorIndex: BYTE;
  735.     procedure SetTransparentColorIndex(Value: BYTE);
  736.     function GetDelay: WORD;
  737.     procedure SetDelay(Value: WORD);
  738.     function GetUserInput: boolean;
  739.     procedure SetUserInput(Value: boolean);
  740.     function GetDisposal: TDisposalMethod;
  741.     procedure SetDisposal(Value: TDisposalMethod);
  742.   public
  743.     constructor Create(ASubImage: TGIFSubImage); override;
  744.     destructor Destroy; override;
  745.     procedure SaveToStream(Stream: TStream); override;
  746.     procedure LoadFromStream(Stream: TStream); override;
  747.     property Delay: WORD read GetDelay write SetDelay;
  748.     property Transparent: boolean read GetTransparent write SetTransparent;
  749.     property TransparentColorIndex: BYTE read GetTransparentColorIndex
  750.                                             write SetTransparentColorIndex;
  751.     property TransparentColor: TColor read GetTransparentColor write SetTransparentColor;
  752.     property UserInput: boolean read GetUserInput write SetUserInput;
  753.     property Disposal: TDisposalMethod read GetDisposal write SetDisposal;
  754.   end;
  755. ////////////////////////////////////////////////////////////////////////////////
  756. //
  757. //                      TGIFTextExtension
  758. //
  759. ////////////////////////////////////////////////////////////////////////////////
  760.   TGIFPlainTextExtensionRec = packed record
  761.     BlockSize: byte;         { should be 12 }
  762.     Left, Top, Width, Height: Word;
  763.     CellWidth, CellHeight: Byte;
  764.     TextFGColorIndex,
  765.     TextBGColorIndex: Byte;
  766.   end;
  767.   TGIFTextExtension = class(TGIFExtension)
  768.   private
  769.     FText : TStrings;
  770.     FPlainTextExtension : TGIFPlainTextExtensionRec;
  771.   protected
  772.     function GetExtensionType: TGIFExtensionType; override;
  773.     function GetForegroundColor: TColor;
  774.     procedure SetForegroundColor(Color: TColor);
  775.     function GetBackgroundColor: TColor;
  776.     procedure SetBackgroundColor(Color: TColor);
  777.     function GetBounds(Index: integer): WORD;
  778.     procedure SetBounds(Index: integer; Value: WORD);
  779.     function GetCharWidthHeight(Index: integer): BYTE;
  780.     procedure SetCharWidthHeight(Index: integer; Value: BYTE);
  781.     function GetColorIndex(Index: integer): BYTE;
  782.     procedure SetColorIndex(Index: integer; Value: BYTE);
  783.   public
  784.     constructor Create(ASubImage: TGIFSubImage); override;
  785.     destructor Destroy; override;
  786.     procedure SaveToStream(Stream: TStream); override;
  787.     procedure LoadFromStream(Stream: TStream); override;
  788.     property Left: WORD index 1 read GetBounds write SetBounds;
  789.     property Top: WORD index 2 read GetBounds write SetBounds;
  790.     property GridWidth: WORD index 3 read GetBounds write SetBounds;
  791.     property GridHeight: WORD index 4 read GetBounds write SetBounds;
  792.     property CharWidth: BYTE index 1 read GetCharWidthHeight write SetCharWidthHeight;
  793.     property CharHeight: BYTE index 2 read GetCharWidthHeight write SetCharWidthHeight;
  794.     property ForegroundColorIndex: BYTE index 1 read GetColorIndex write SetColorIndex;
  795.     property ForegroundColor: TColor read GetForegroundColor;
  796.     property BackgroundColorIndex: BYTE  index 2 read GetColorIndex write SetColorIndex;
  797.     property BackgroundColor: TColor read GetBackgroundColor;
  798.     property Text: TStrings read FText write FText;
  799.   end;
  800. ////////////////////////////////////////////////////////////////////////////////
  801. //
  802. //                      TGIFCommentExtension
  803. //
  804. ////////////////////////////////////////////////////////////////////////////////
  805.   TGIFCommentExtension = class(TGIFExtension)
  806.   private
  807.     FText : TStrings;
  808.   protected
  809.     function GetExtensionType: TGIFExtensionType; override;
  810.   public
  811.     constructor Create(ASubImage: TGIFSubImage); override;
  812.     destructor Destroy; override;
  813.     procedure SaveToStream(Stream: TStream); override;
  814.     procedure LoadFromStream(Stream: TStream); override;
  815.     property Text: TStrings read FText;
  816.   end;
  817. ////////////////////////////////////////////////////////////////////////////////
  818. //
  819. //                      TGIFApplicationExtension
  820. //
  821. ////////////////////////////////////////////////////////////////////////////////
  822.   TGIFIdentifierCode = array[0..7] of char;
  823.   TGIFAuthenticationCode = array[0..2] of char;
  824.   TGIFApplicationRec = packed record
  825.     Identifier : TGIFIdentifierCode;
  826.     Authentication : TGIFAuthenticationCode;
  827.   end;
  828.   TGIFApplicationExtension = class;
  829.   TGIFAppExtensionClass = class of TGIFApplicationExtension;
  830.   TGIFApplicationExtension = class(TGIFExtension)
  831.   private
  832.     FIdent : TGIFApplicationRec;
  833.     function GetAuthentication: string;
  834.     function GetIdentifier: string;
  835.   protected
  836.     function GetExtensionType: TGIFExtensionType; override;
  837.     procedure SetAuthentication(const Value: string);
  838.     procedure SetIdentifier(const Value: string);
  839.     procedure SaveData(Stream: TStream); virtual; abstract;
  840.     procedure LoadData(Stream: TStream); virtual; abstract;
  841.   public
  842.     constructor Create(ASubImage: TGIFSubImage); override;
  843.     destructor Destroy; override;
  844.     procedure SaveToStream(Stream: TStream); override;
  845.     procedure LoadFromStream(Stream: TStream); override;
  846.     class procedure RegisterExtension(eIdent: TGIFApplicationRec; eClass: TGIFAppExtensionClass);
  847.     class function FindSubExtension(Stream: TStream): TGIFExtensionClass; override;
  848.     property Identifier: string read GetIdentifier write SetIdentifier;
  849.     property Authentication: string read GetAuthentication write SetAuthentication;
  850.   end;
  851. ////////////////////////////////////////////////////////////////////////////////
  852. //
  853. //                      TGIFUnknownAppExtension
  854. //
  855. ////////////////////////////////////////////////////////////////////////////////
  856.   TGIFBlock = class(TObject)
  857.   private
  858.     FSize : BYTE;
  859.     FData : pointer;
  860.   public
  861.     constructor Create(ASize: integer);
  862.     destructor Destroy; override;
  863.     procedure SaveToStream(Stream: TStream);
  864.     procedure LoadFromStream(Stream: TStream);
  865.     property Size: BYTE read FSize;
  866.     property Data: pointer read FData;
  867.   end;
  868.   TGIFUnknownAppExtension = class(TGIFApplicationExtension)
  869.   private
  870.     FBlocks : TList;
  871.   protected
  872.     procedure SaveData(Stream: TStream); override;
  873.     procedure LoadData(Stream: TStream); override;
  874.   public
  875.     constructor Create(ASubImage: TGIFSubImage); override;
  876.     destructor Destroy; override;
  877.     property Blocks: TList read FBlocks;
  878.   end;
  879. ////////////////////////////////////////////////////////////////////////////////
  880. //
  881. //                      TGIFAppExtNSLoop
  882. //
  883. ////////////////////////////////////////////////////////////////////////////////
  884.   TGIFAppExtNSLoop = class(TGIFApplicationExtension)
  885.   private
  886.     FLoops : WORD;
  887.     FBufferSize : DWORD;
  888.   protected
  889.     procedure SaveData(Stream: TStream); override;
  890.     procedure LoadData(Stream: TStream); override;
  891.   public
  892.     constructor Create(ASubImage: TGIFSubImage); override;
  893.     property Loops: WORD read FLoops write FLoops;
  894.     property BufferSize: DWORD read FBufferSize write FBufferSize;
  895.   end;
  896. ////////////////////////////////////////////////////////////////////////////////
  897. //
  898. //                      TGIFImage
  899. //
  900. ////////////////////////////////////////////////////////////////////////////////
  901.   TGIFImageList = class(TGIFList)
  902.   protected
  903.     function GetImage(Index: Integer): TGIFSubImage;
  904.     procedure SetImage(Index: Integer; SubImage: TGIFSubImage);
  905.   public
  906.     procedure LoadFromStream(Stream: TStream; Parent: TObject); override;
  907.     procedure SaveToStream(Stream: TStream); override;
  908.     property SubImages[Index: Integer]: TGIFSubImage read GetImage write SetImage; default;
  909.   end;
  910.   // Compression algorithms
  911.   TGIFCompression =
  912.     (gcLZW, // Normal LZW compression
  913.      gcRLE // GIF compatible RLE compression
  914.     );
  915.   // Color reduction methods
  916.   TColorReduction =
  917.     (rmNone, // Do not perform color reduction
  918.      rmWindows20, // Reduce to the Windows 20 color system palette
  919.      rmWindows256, // Reduce to the Windows 256 color halftone palette (Only works in 256 color display mode)
  920.      rmWindowsGray, // Reduce to the Windows 4 grayscale colors
  921.      rmMonochrome, // Reduce to a black/white monochrome palette
  922.      rmGrayScale, // Reduce to a uniform 256 shade grayscale palette
  923.      rmNetscape, // Reduce to the Netscape 216 color palette
  924.      rmQuantize, // Reduce to optimal 2^n color palette
  925.      rmQuantizeWindows, // Reduce to optimal 256 color windows palette
  926.      rmPalette // Reduce to custom palette
  927.     );
  928.   TDitherMode =
  929.     (dmNearest, // Nearest color matching w/o error correction
  930.      dmFloydSteinberg, // Floyd Steinberg Error Diffusion dithering
  931.      dmStucki, // Stucki Error Diffusion dithering
  932.      dmSierra, // Sierra Error Diffusion dithering
  933.      dmJaJuNI, // Jarvis, Judice & Ninke Error Diffusion dithering
  934.      dmSteveArche, // Stevenson & Arche Error Diffusion dithering
  935.      dmBurkes // Burkes Error Diffusion dithering
  936.      // dmOrdered, // Ordered dither
  937.     );
  938.   // Optimization options
  939.   TGIFOptimizeOption =
  940.     (ooCrop, // Crop animated GIF frames
  941.      ooMerge, // Merge pixels of same color
  942.      ooCleanup, // Remove comments and application extensions
  943.      ooColorMap, // Sort color map by usage and remove unused entries
  944.      ooReduceColors // Reduce color depth ***NOT IMPLEMENTED***
  945.     );
  946.   TGIFOptimizeOptions = set of TGIFOptimizeOption;
  947.   TGIFDrawOption =
  948.     (goAsync, // Asyncronous draws (paint in thread)
  949.      goTransparent, // Transparent draws
  950.      goAnimate, // Animate draws
  951.      goLoop, // Loop animations
  952.      goLoopContinously, // Ignore loop count and loop forever
  953.      goValidateCanvas, // Validate canvas in threaded paint ***NOT IMPLEMENTED***
  954.      goDirectDraw, // Draw() directly on canvas
  955.      goClearOnLoop, // Clear animation on loop
  956.      goTile, // Tiled display
  957.      goDither, // Dither to Netscape palette
  958.      goAutoDither // Only dither on 256 color systems
  959.     );
  960.   TGIFDrawOptions = set of TGIFDrawOption;
  961.   // Note: if goAsync is not set then goDirectDraw should be set. Otherwise
  962.   // the image will not be displayed.
  963.   PGIFPainter = ^TGIFPainter;
  964.   TGIFPainter = class(TThread)
  965.   private
  966.     FImage : TGIFImage; // The TGIFImage that owns this painter
  967.     FCanvas : TCanvas; // Destination canvas
  968.     FRect : TRect; // Destination rect
  969.     FDrawOptions : TGIFDrawOptions;// Paint options
  970.     FAnimationSpeed : integer; // Animation speed %
  971.     FActiveImage : integer; // Current frame
  972.     Disposal , // Used by synchronized paint
  973.     OldDisposal : TDisposalMethod;// Used by synchronized paint
  974.     BackupBuffer : TBitmap; // Used by synchronized paint
  975.     FrameBuffer : TBitmap; // Used by synchronized paint
  976.     Background : TBitmap; // Used by synchronized paint
  977.     ValidateDC : HDC;
  978.     DoRestart : boolean; // Flag used to restart animation
  979.     FStarted : boolean; // Flag used to signal start of paint
  980.     PainterRef : PGIFPainter; // Pointer to var referencing painter
  981.     FEventHandle : THandle; // Animation delay event
  982.     ExceptObject : Exception; // Eaten exception
  983.     ExceptAddress : pointer; // Eaten exceptions address
  984.     FEvent : TNotifyEvent; // Used by synchronized events
  985.     FOnStartPaint : TNotifyEvent;
  986.     FOnPaint : TNotifyEvent;
  987.     FOnAfterPaint : TNotifyEvent;
  988.     FOnLoop : TNotifyEvent;
  989.     FOnEndPaint : TNotifyEvent;
  990.     procedure DoOnTerminate(Sender: TObject);// Sync. shutdown procedure
  991.     procedure DoSynchronize(Method: TThreadMethod);// Conditional sync stub
  992. {$ifdef SERIALIZE_RENDER}
  993.     procedure PrefetchBitmap; // Sync. bitmap prefetch
  994. {$endif}
  995.     procedure DoPaintFrame; // Sync. buffered paint procedure
  996.     procedure DoPaint; // Sync. paint procedure
  997.     procedure DoEvent;
  998.     procedure SetActiveImage(const Value: integer);// Sync. event procedure
  999.   protected
  1000.     procedure Execute; override;
  1001.     procedure SetAnimationSpeed(Value: integer);
  1002.   public
  1003.     constructor Create(AImage: TGIFImage; ACanvas: TCanvas; ARect: TRect;
  1004.       Options: TGIFDrawOptions);
  1005.     constructor CreateRef(Painter: PGIFPainter; AImage: TGIFImage; ACanvas: TCanvas; ARect: TRect;
  1006.       Options: TGIFDrawOptions);
  1007.     destructor Destroy; override;
  1008.     procedure Start;
  1009.     procedure Stop;
  1010.     procedure Restart;
  1011.     property Image: TGIFImage read FImage;
  1012.     property Canvas: TCanvas read FCanvas;
  1013.     property Rect: TRect read FRect write FRect;
  1014.     property DrawOptions: TGIFDrawOptions read FDrawOptions write FDrawOptions;
  1015.     property AnimationSpeed: integer read FAnimationSpeed write SetAnimationSpeed;
  1016.     property Started: boolean read FStarted;
  1017.     property ActiveImage: integer read FActiveImage write SetActiveImage;
  1018.     property OnStartPaint: TNotifyEvent read FOnStartPaint write FOnStartPaint;
  1019.     property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  1020.     property OnAfterPaint: TNotifyEvent read FOnAfterPaint write FOnAfterPaint;
  1021.     property OnLoop: TNotifyEvent read FOnLoop write FOnLoop;
  1022.     property OnEndPaint : TNotifyEvent read FOnEndPaint  write FOnEndPaint ;
  1023.     property EventHandle: THandle read FEventHandle;
  1024.   end;
  1025.   TGIFWarning = procedure(Sender: TObject; Severity: TGIFSeverity; Message: string) of object;
  1026.   TGIFImage = class(TGraphic)
  1027.   private
  1028.     IsDrawing : Boolean;
  1029.     IsInsideGetPalette : boolean;
  1030.     FImages : TGIFImageList;
  1031.     FHeader : TGIFHeader;
  1032.     FGlobalPalette : HPalette;
  1033.     FPainters : TThreadList;
  1034.     FDrawOptions : TGIFDrawOptions;
  1035.     FColorReduction : TColorReduction;
  1036.     FReductionBits : integer;
  1037.     FDitherMode : TDitherMode;
  1038.     FCompression : TGIFCompression;
  1039.     FOnWarning : TGIFWarning;
  1040.     FBitmap : TBitmap;
  1041.     FDrawPainter : TGIFPainter;
  1042.     FThreadPriority : TThreadPriority;
  1043.     FAnimationSpeed : integer;
  1044.     FDrawBackgroundColor: TColor;
  1045.     FOnStartPaint : TNotifyEvent;
  1046.     FOnPaint : TNotifyEvent;
  1047.     FOnAfterPaint : TNotifyEvent;
  1048.     FOnLoop : TNotifyEvent;
  1049.     FOnEndPaint : TNotifyEvent;
  1050. {$IFDEF VER9x}
  1051.     FPaletteModified : Boolean;
  1052.     FOnProgress : TProgressEvent;
  1053. {$ENDIF}
  1054.     function GetAnimate: Boolean;
  1055.     procedure SetAnimate(const Value: Boolean);
  1056.   protected
  1057.     // Obsolete: procedure Changed(Sender: TObject); {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1058.     function GetHeight: Integer; override;
  1059.     procedure SetHeight(Value: Integer); override;
  1060.     function GetWidth: Integer; override;
  1061.     procedure SetWidth(Value: Integer); override;
  1062.     procedure AssignTo(Dest: TPersistent); override;
  1063.     function InternalPaint(Painter: PGIFPainter; ACanvas: TCanvas; const Rect: TRect; Options: TGIFDrawOptions): TGIFPainter;
  1064.     procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
  1065.     function Equals(Graphic: TGraphic): Boolean; override;
  1066.     function GetPalette: HPALETTE; {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1067.     procedure SetPalette(Value: HPalette); {$IFDEF VER9x} virtual; {$ELSE} override; {$ENDIF}
  1068.     function GetEmpty: Boolean; override;
  1069.     procedure WriteData(Stream: TStream); override;
  1070.     function GetIsTransparent: Boolean;
  1071.     function GetVersion: TGIFVersion;
  1072.     function GetColorResolution: integer;
  1073.     function GetBitsPerPixel: integer;
  1074.     function GetBackgroundColorIndex: BYTE;
  1075.     procedure SetBackgroundColorIndex(const Value: BYTE);
  1076.     function GetBackgroundColor: TColor;
  1077.     procedure SetBackgroundColor(const Value: TColor);
  1078.     function GetAspectRatio: BYTE;
  1079.     procedure SetAspectRatio(const Value: BYTE);
  1080.     procedure SetDrawOptions(Value: TGIFDrawOptions);
  1081.     procedure SetAnimationSpeed(Value: integer);
  1082.     procedure SetReductionBits(Value: integer);
  1083.     procedure NewImage;
  1084.     function GetBitmap: TBitmap;
  1085.     function NewBitmap: TBitmap;
  1086.     procedure FreeBitmap;
  1087.     function GetColorMap: TGIFColorMap;
  1088.     function GetDoDither: boolean;
  1089.     property DrawPainter: TGIFPainter read FDrawPainter; // Extremely volatile
  1090.     property DoDither: boolean read GetDoDither;
  1091. {$IFDEF VER9x}
  1092.     procedure Progress(Sender: TObject; Stage: TProgressStage;
  1093.       PercentDone: Byte;  RedrawNow: Boolean; const R: TRect; const Msg: string); dynamic;
  1094. {$ENDIF}
  1095.   public
  1096.     constructor Create; override;
  1097.     destructor Destroy; override;
  1098.     procedure SaveToStream(Stream: TStream); override;
  1099.     procedure LoadFromStream(Stream: TStream); override;
  1100.     procedure LoadFromResourceName(Instance: THandle; const ResName: String);
  1101.     function Add(Source: TPersistent): integer;
  1102.     procedure Pack;
  1103.     procedure OptimizeColorMap;
  1104.     procedure Optimize(Options: TGIFOptimizeOptions;
  1105.       ColorReduction: TColorReduction; DitherMode: TDitherMode;
  1106.       ReductionBits: integer);
  1107.     procedure Clear;
  1108.     procedure StopDraw;
  1109.     function Paint(ACanvas: TCanvas; const Rect: TRect; Options: TGIFDrawOptions): TGIFPainter;
  1110.     procedure PaintStart;
  1111.     procedure PaintPause;
  1112.     procedure PaintStop;
  1113.     procedure PaintResume;
  1114.     procedure PaintRestart;
  1115.     procedure Warning(Sender: TObject; Severity: TGIFSeverity; Message: string); virtual;
  1116.     procedure Assign(Source: TPersistent); override;
  1117.     procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  1118.       APalette: HPALETTE); override;
  1119.     procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  1120.       var APalette: HPALETTE); override;
  1121.     property GlobalColorMap: TGIFColorMap read GetColorMap;
  1122.     property Version: TGIFVersion read GetVersion;
  1123.     property Images: TGIFImageList read FImages;
  1124.     property ColorResolution: integer read GetColorResolution;
  1125.     property BitsPerPixel: integer read GetBitsPerPixel;
  1126.     property BackgroundColorIndex: BYTE read GetBackgroundColorIndex write SetBackgroundColorIndex;
  1127.     property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
  1128.     property AspectRatio: BYTE read GetAspectRatio write SetAspectRatio;
  1129.     property Header: TGIFHeader read FHeader; // ***OBSOLETE***
  1130.     property IsTransparent: boolean read GetIsTransparent;
  1131.     property DrawOptions: TGIFDrawOptions read FDrawOptions write SetDrawOptions;
  1132.     property DrawBackgroundColor: TColor read FDrawBackgroundColor write FDrawBackgroundColor;
  1133.     property ColorReduction: TColorReduction read FColorReduction write FColorReduction;
  1134.     property ReductionBits: integer read FReductionBits write SetReductionBits;
  1135.     property DitherMode: TDitherMode read FDitherMode write FDitherMode;
  1136.     property Compression: TGIFCompression read FCompression write FCompression;
  1137.     property AnimationSpeed: integer read FAnimationSpeed write SetAnimationSpeed;
  1138.     property Animate: Boolean read GetAnimate write SetAnimate;
  1139.     property Painters: TThreadList read FPainters;
  1140.     property ThreadPriority: TThreadPriority read FThreadPriority write FThreadPriority;
  1141.     property Bitmap: TBitmap read GetBitmap; // Volatile - beware!
  1142.     property OnWarning: TGIFWarning read FOnWarning write FOnWarning;
  1143.     property OnStartPaint: TNotifyEvent read FOnStartPaint write FOnStartPaint;
  1144.     property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  1145.     property OnAfterPaint: TNotifyEvent read FOnAfterPaint write FOnAfterPaint;
  1146.     property OnLoop: TNotifyEvent read FOnLoop write FOnLoop;
  1147.     property OnEndPaint : TNotifyEvent read FOnEndPaint  write FOnEndPaint ;
  1148. {$IFDEF VER9x}
  1149.     property Palette: HPALETTE read GetPalette write SetPalette;
  1150.     property PaletteModified: Boolean read FPaletteModified write FPaletteModified;
  1151.     property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
  1152. {$ENDIF}
  1153.   end;
  1154. ////////////////////////////////////////////////////////////////////////////////
  1155. //
  1156. //                      Utility routines
  1157. //
  1158. ////////////////////////////////////////////////////////////////////////////////
  1159.   // WebPalette creates a 216 color uniform palette a.k.a. the Netscape Palette
  1160.   function WebPalette: HPalette;
  1161.   // ReduceColors
  1162.   // Map colors in a bitmap to their nearest representation in a palette using
  1163.   // the methods specified by the ColorReduction and DitherMode parameters.
  1164.   // The ReductionBits parameter specifies the desired number of colors (bits
  1165.   // per pixel) when the reduction method is rmQuantize. The CustomPalette
  1166.   // specifies the palette when the rmPalette reduction method is used.
  1167.   function ReduceColors(Bitmap: TBitmap; ColorReduction: TColorReduction;
  1168.     DitherMode: TDitherMode; ReductionBits: integer; CustomPalette: hPalette): TBitmap;
  1169.   // CreateOptimizedPaletteFromManyBitmaps
  1170.   //: Performs Color Quantization on multiple bitmaps.
  1171.   // The Bitmaps parameter is a list of bitmaps. Returns an optimized palette.
  1172.   function CreateOptimizedPaletteFromManyBitmaps(Bitmaps: TList; Colors, ColorBits: integer;
  1173.     Windows: boolean): hPalette;
  1174. {$IFDEF VER9x}
  1175.   // From Delphi 3 graphics.pas
  1176. type
  1177.   TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
  1178. {$ENDIF}
  1179.   procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
  1180.     var ImageSize: longInt; PixelFormat: TPixelFormat);
  1181.   function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
  1182.    var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
  1183. ////////////////////////////////////////////////////////////////////////////////
  1184. //
  1185. //                      Global variables
  1186. //
  1187. ////////////////////////////////////////////////////////////////////////////////
  1188. // GIF Clipboard format identifier for use by LoadFromClipboardFormat and
  1189. // SaveToClipboardFormat.
  1190. // Set in Initialization section.
  1191. var
  1192.   CF_GIF: WORD;
  1193. ////////////////////////////////////////////////////////////////////////////////
  1194. //
  1195. //                      Library defaults
  1196. //
  1197. ////////////////////////////////////////////////////////////////////////////////
  1198. var
  1199.   //: Default options for TGIFImage.DrawOptions.
  1200.   GIFImageDefaultDrawOptions : TGIFDrawOptions =
  1201.     [goAsync, goLoop, goTransparent, goAnimate, goDither, goAutoDither
  1202. {$IFDEF STRICT_MOZILLA}
  1203.      ,goClearOnLoop
  1204. {$ENDIF}
  1205.     ];
  1206.   // WARNING! Do not use goAsync and goDirectDraw unless you have absolute
  1207.   // control of the destination canvas.
  1208.   // TGIFPainter will continue to write on the canvas even after the canvas has
  1209.   // been deleted, unless *you* prevent it.
  1210.   // The goValidateCanvas option will fix this problem if it is ever implemented.
  1211.   //: Default color reduction methods for bitmap import.
  1212.   // These are the fastest settings, but also the ones that gives the
  1213.   // worst result (in most cases).
  1214.   GIFImageDefaultColorReduction: TColorReduction = rmNetscape;
  1215.   GIFImageDefaultColorReductionBits: integer = 8; // Range 3 - 8
  1216.   GIFImageDefaultDitherMode: TDitherMode = dmNearest;
  1217.   //: Default encoder compression method.
  1218.   GIFImageDefaultCompression: TGIFCompression = gcLZW;
  1219.   //: Default painter thread priority
  1220.   GIFImageDefaultThreadPriority: TThreadPriority = tpNormal;
  1221.   //: Default animation speed in % of normal speed (range 0 - 1000)
  1222.   GIFImageDefaultAnimationSpeed: integer = 100;
  1223.   // DoAutoDither is set to True in the initializaion section if the desktop DC
  1224.   // supports 256 colors or less.
  1225.   // It can be modified in your application to disable/enable Auto Dithering
  1226.   DoAutoDither: boolean = False;
  1227.   // Palette is set to True in the initialization section if the desktop DC
  1228.   // supports 256 colors or less.
  1229.   // You should NOT modify it.
  1230.   PaletteDevice: boolean = False;
  1231.   // Set GIFImageRenderOnLoad to True to render (convert to bitmap) the
  1232.   // GIF frames as they are loaded instead of rendering them on-demand.
  1233.   // This might increase resource consumption and will increase load time,
  1234.   // but will cause animated GIFs to display more smoothly.
  1235.   GIFImageRenderOnLoad: boolean = False;
  1236.   // If GIFImageOptimizeOnStream is true, the GIF will be optimized
  1237.   // before it is streamed to the DFM file.
  1238.   // This will not affect TGIFImage.SaveToStream or SaveToFile.
  1239.   GIFImageOptimizeOnStream: boolean = False;
  1240. ////////////////////////////////////////////////////////////////////////////////
  1241. //
  1242. //                      Design Time support
  1243. //
  1244. ////////////////////////////////////////////////////////////////////////////////
  1245. // Dummy component registration for design time support of GIFs in TImage
  1246. procedure Register;
  1247. ////////////////////////////////////////////////////////////////////////////////
  1248. //
  1249. //                      Error messages
  1250. //
  1251. ////////////////////////////////////////////////////////////////////////////////
  1252. {$ifndef VER9x}
  1253. resourcestring
  1254. {$else}
  1255. const
  1256. {$endif}
  1257.   // GIF Error messages
  1258.   sOutOfData = 'Premature end of data';
  1259.   sTooManyColors = 'Color table overflow';
  1260.   sBadColorIndex = 'Invalid color index';
  1261.   sBadVersion = 'Unsupported GIF version';
  1262.   sBadSignature = 'Invalid GIF signature';
  1263.   sScreenBadColorSize = 'Invalid number of colors specified in Screen Descriptor';
  1264.   sImageBadColorSize = 'Invalid number of colors specified in Image Descriptor';
  1265.   sUnknownExtension = 'Unknown extension type';
  1266.   sBadExtensionLabel = 'Invalid extension introducer';
  1267.   sOutOfMemDIB = 'Failed to allocate memory for GIF DIB';
  1268.   sDIBCreate = 'Failed to create DIB from Bitmap';
  1269.   sDecodeTooFewBits = 'Decoder bit buffer under-run';
  1270.   sDecodeCircular = 'Circular decoder table entry';
  1271.   sBadTrailer = 'Invalid Image trailer';
  1272.   sBadExtensionInstance = 'Internal error: Extension Instance does not match Extension Label';
  1273.   sBadBlockSize = 'Unsupported Application Extension block size';
  1274.   sBadBlock = 'Unknown GIF block type';
  1275.   sUnsupportedClass = 'Object type not supported for operation';
  1276.   sInvalidData = 'Invalid GIF data';
  1277.   sBadHeight = 'Image height too small for contained frames';
  1278.   sBadWidth = 'Image width too small for contained frames';
  1279. {$IFNDEF REGISTER_TGIFIMAGE}
  1280.   sGIFToClipboard = 'Clipboard operations not supported for GIF objects';
  1281. {$ELSE}
  1282.   sFailedPaste = 'Failed to store GIF on clipboard';
  1283. {$IFDEF VER9x}
  1284.   sUnknownClipboardFormat= 'Unsupported clipboard format';
  1285. {$ENDIF}
  1286. {$ENDIF}
  1287.   sScreenSizeExceeded = 'Image exceeds Logical Screen size';
  1288.   sNoColorTable = 'No global or local color table defined';
  1289.   sBadPixelCoordinates = 'Invalid pixel coordinates';
  1290.   sUnsupportedBitmap = 'Unsupported bitmap format';
  1291.   sInvalidPixelFormat = 'Unsupported PixelFormat';
  1292.   sBadDimension = 'Invalid image dimensions';
  1293.   sNoDIB = 'Image has no DIB';
  1294.   sInvalidStream = 'Invalid stream operation';
  1295.   sInvalidColor = 'Color not in color table';
  1296.   sInvalidBitSize = 'Invalid Bits Per Pixel value';
  1297.   sEmptyColorMap = 'Color table is empty';
  1298.   sEmptyImage = 'Image is empty';
  1299.   sInvalidBitmapList = 'Invalid bitmap list';
  1300.   sInvalidReduction = 'Invalid reduction method';
  1301. {$IFDEF VER9x}
  1302.   // From Delphi 3 consts.pas
  1303.   SOutOfResources = 'Out of system resources';
  1304.   SInvalidBitmap = 'Bitmap image is not valid';
  1305.   SScanLine = 'Scan line index out of range';
  1306. {$ENDIF}
  1307. ////////////////////////////////////////////////////////////////////////////////
  1308. //
  1309. //                      Misc texts
  1310. //
  1311. ////////////////////////////////////////////////////////////////////////////////
  1312.   // File filter name
  1313.   sGIFImageFile = 'GIF Image';
  1314.   // Progress messages
  1315.   sProgressLoading = 'Loading...';
  1316.   sProgressSaving = 'Saving...';
  1317.   sProgressConverting = 'Converting...';
  1318.   sProgressRendering = 'Rendering...';
  1319.   sProgressCopying = 'Copying...';
  1320.   sProgressOptimizing = 'Optimizing...';
  1321. ////////////////////////////////////////////////////////////////////////////////
  1322. ////////////////////////////////////////////////////////////////////////////////
  1323. //
  1324. // Implementation
  1325. //
  1326. ////////////////////////////////////////////////////////////////////////////////
  1327. ////////////////////////////////////////////////////////////////////////////////
  1328. implementation
  1329. { This makes me long for the C preprocessor... }
  1330. {$ifdef DEBUG}
  1331.   {$ifdef DEBUG_COMPRESSPERFORMANCE}
  1332.     {$define DEBUG_PERFORMANCE}
  1333.   {$else}
  1334.     {$ifdef DEBUG_DECOMPRESSPERFORMANCE}
  1335.       {$define DEBUG_PERFORMANCE}
  1336.     {$else}
  1337.       {$ifdef DEBUG_DITHERPERFORMANCE}
  1338.         {$define DEBUG_PERFORMANCE}
  1339.       {$else}
  1340.         {$ifdef DEBUG_DITHERPERFORMANCE}
  1341.           {$define DEBUG_PERFORMANCE}
  1342.         {$else}
  1343.           {$ifdef DEBUG_DRAWPERFORMANCE}
  1344.             {$define DEBUG_PERFORMANCE}
  1345.           {$else}
  1346.             {$ifdef DEBUG_RENDERPERFORMANCE}
  1347.               {$define DEBUG_PERFORMANCE}
  1348.             {$endif}
  1349.           {$endif}
  1350.         {$endif}
  1351.       {$endif}
  1352.     {$endif}
  1353.   {$endif}
  1354. {$endif}
  1355. uses
  1356. {$ifdef DEBUG}
  1357.   dialogs,
  1358. {$endif}
  1359.   mmsystem, // timeGetTime()
  1360.   messages,
  1361.   Consts;
  1362. ////////////////////////////////////////////////////////////////////////////////
  1363. //
  1364. // Misc consts
  1365. //
  1366. ////////////////////////////////////////////////////////////////////////////////
  1367. const
  1368.   { Extension/block label values }
  1369.   bsPlainTextExtension = $01;
  1370.   bsGraphicControlExtension = $F9;
  1371.   bsCommentExtension = $FE;
  1372.   bsApplicationExtension = $FF;
  1373.   bsImageDescriptor = Ord(',');
  1374.   bsExtensionIntroducer = Ord('!');
  1375.   bsTrailer = ord(';');
  1376.   // Thread messages - Used by TThread.Synchronize()
  1377.   CM_DESTROYWINDOW = $8FFE; // Defined in classes.pas
  1378.   CM_EXECPROC  = $8FFF; // Defined in classes.pas
  1379. ////////////////////////////////////////////////////////////////////////////////
  1380. //
  1381. //                      Design Time support
  1382. //
  1383. ////////////////////////////////////////////////////////////////////////////////
  1384. //: Dummy component registration to add design-time support of GIFs to TImage.
  1385. // Since TGIFImage isn't a component there's nothing to register here, but
  1386. // since Register is only called at design time we can set the design time
  1387. // GIF paint options here (modify as you please):
  1388. procedure Register;
  1389. begin
  1390.   // Don't loop animations at design-time. Animated GIFs will animate once and
  1391.   // then stop thus not using CPU resources and distracting the developer.
  1392.   Exclude(GIFImageDefaultDrawOptions, goLoop);
  1393. end;
  1394. ////////////////////////////////////////////////////////////////////////////////
  1395. //
  1396. // Utilities
  1397. //
  1398. ////////////////////////////////////////////////////////////////////////////////
  1399. //: Creates a 216 color uniform non-dithering Netscape palette.
  1400. function WebPalette: HPalette;
  1401. type
  1402.   TLogWebPalette = packed record
  1403.     palVersion : word;
  1404.     palNumEntries : word;
  1405.     PalEntries : array[0..5,0..5,0..5] of TPaletteEntry;
  1406.   end;
  1407. var
  1408.   r, g, b : byte;
  1409.   LogWebPalette : TLogWebPalette;
  1410.   LogPalette : TLogpalette absolute LogWebPalette; // Stupid typecast
  1411. begin
  1412.   with LogWebPalette do
  1413.   begin
  1414.     palVersion:= $0300;
  1415.     palNumEntries:= 216;
  1416.     for r:=0 to 5 do
  1417.       for g:=0 to 5 do
  1418.         for b:=0 to 5 do
  1419.         begin
  1420.           with PalEntries[r,g,b] do
  1421.           begin
  1422.             peRed := 51 * r;
  1423.             peGreen := 51 * g;
  1424.             peBlue := 51 * b;
  1425.             peFlags := 0;
  1426.           end;
  1427.         end;
  1428.   end;
  1429.   Result := CreatePalette(Logpalette);
  1430. end;
  1431. (*
  1432. **  GDI Error handling
  1433. **  Adapted from graphics.pas
  1434. *)
  1435. {$IFOPT R+}
  1436.   {$DEFINE R_PLUS}
  1437.   {$RANGECHECKS OFF}
  1438. {$ENDIF}
  1439. {$ifdef D3_BCB3}
  1440. function GDICheck(Value: Integer): Integer;
  1441. {$else}
  1442. function GDICheck(Value: Cardinal): Cardinal;
  1443. {$endif}
  1444. var
  1445.   ErrorCode : integer;
  1446.   Buf : array [byte] of char;
  1447.   function ReturnAddr: Pointer;
  1448.   // From classes.pas
  1449.   asm
  1450.     MOV EAX,[EBP+4] // sysutils.pas says [EBP-4], but this works !
  1451.   end;
  1452. begin
  1453.   if (Value = 0) then
  1454.   begin
  1455.     ErrorCode := GetLastError;
  1456.     if (ErrorCode <> 0) and (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
  1457.       ErrorCode, LOCALE_USER_DEFAULT, Buf, sizeof(Buf), nil) <> 0) then
  1458.       raise EOutOfResources.Create(Buf) at ReturnAddr
  1459.     else
  1460.       raise EOutOfResources.Create(SOutOfResources) at ReturnAddr;
  1461.   end;
  1462.   Result := Value;
  1463. end;
  1464. {$IFDEF R_PLUS}
  1465.   {$RANGECHECKS ON}
  1466.   {$UNDEF R_PLUS}
  1467. {$ENDIF}
  1468. (*
  1469. **  Raise error condition
  1470. *)
  1471. procedure Error(msg: string);
  1472.   function ReturnAddr: Pointer;
  1473.   // From classes.pas
  1474.   asm
  1475.     MOV EAX,[EBP+4] // sysutils.pas says [EBP-4] !
  1476.   end;
  1477. begin
  1478.   raise GIFException.Create(msg) at ReturnAddr;
  1479. end;
  1480. (*
  1481. **  Return number bytes required to
  1482. **  hold a given number of bits.
  1483. *)
  1484. function ByteAlignBit(Bits: Cardinal): Cardinal;
  1485. begin
  1486.   Result := (Bits+7) SHR 3;
  1487. end;
  1488. // Rounded up to nearest 2
  1489. function WordAlignBit(Bits: Cardinal): Cardinal;
  1490. begin
  1491.   Result := ((Bits+15) SHR 4) SHL 1;
  1492. end;
  1493. // Rounded up to nearest 4
  1494. function DWordAlignBit(Bits: Cardinal): Cardinal;
  1495. begin
  1496.   Result := ((Bits+31) SHR 5) SHL 2;
  1497. end;
  1498. // Round to arbitrary number of bits
  1499. function AlignBit(Bits, BitsPerPixel, Alignment: Cardinal): Cardinal;
  1500. begin
  1501.   Dec(Alignment);
  1502.   Result := ((Bits * BitsPerPixel) + Alignment) and not Alignment;
  1503.   Result := Result SHR 3;
  1504. end;
  1505. (*
  1506. **  Compute Bits per Pixel from Number of Colors
  1507. **  (Return the ceiling log of n)
  1508. *)
  1509. function Colors2bpp(Colors: integer): integer;
  1510. var
  1511.   MaxColor : integer;
  1512. begin
  1513.   (*
  1514.   ** This might be faster computed by multiple if then else statements
  1515.   *)
  1516.   if (Colors = 0) then
  1517.     Result := 0
  1518.   else
  1519.   begin
  1520.     Result := 1;
  1521.     MaxColor := 2;
  1522.     while (Colors > MaxColor) do
  1523.     begin
  1524.       inc(Result);
  1525.       MaxColor := MaxColor SHL 1;
  1526.     end;
  1527.   end;
  1528. end;
  1529. (*
  1530. **  Write an ordinal byte value to a stream
  1531. *)
  1532. procedure WriteByte(Stream: TStream; b: BYTE);
  1533. begin
  1534.   Stream.Write(b, 1);
  1535. end;
  1536. (*
  1537. **  Read an ordinal byte value from a stream
  1538. *)
  1539. function ReadByte(Stream: TStream): BYTE;
  1540. begin
  1541.   Stream.Read(Result, 1);
  1542. end;
  1543. (*
  1544. **  Read data from stream and raise exception of EOF
  1545. *)
  1546. procedure ReadCheck(Stream: TStream; var Buffer; Size: LongInt);
  1547. var
  1548.   ReadSize : integer;
  1549. begin
  1550.   ReadSize := Stream.Read(Buffer, Size);
  1551.   if (ReadSize <> Size) then
  1552.     Error(sOutOfData);
  1553. end;
  1554. (*
  1555. **  Write a string list to a stream as multiple blocks
  1556. **  of max 255 characters in each.
  1557. *)
  1558. procedure WriteStrings(Stream: TStream; Text: TStrings);
  1559. var
  1560.   i : integer;
  1561.   b : BYTE;
  1562.   size : integer;
  1563.   s : string;
  1564. begin
  1565.   for i := 0 to Text.Count-1 do
  1566.   begin
  1567.     s := Text[i];
  1568.     size := length(s);
  1569.     if (size > 255) then
  1570.       b := 255
  1571.     else
  1572.       b := size;
  1573.     while (size > 0) do
  1574.     begin
  1575.       dec(size, b);
  1576.       WriteByte(Stream, b);
  1577.       Stream.Write(PChar(s)^, b);
  1578.       delete(s, 1, b);
  1579.       if (b > size) then
  1580.         b := size;
  1581.     end;
  1582.   end;
  1583.   // Terminating zero (length = 0)
  1584.   WriteByte(Stream, 0);
  1585. end;
  1586. (*
  1587. **  Read a string list from a stream as multiple blocks
  1588. **  of max 255 characters in each.
  1589. *)
  1590. { TODO -oanme -cImprovement : Replace ReadStrings with TGIFReader. }
  1591. procedure ReadStrings(Stream: TStream; Text: TStrings);
  1592. var
  1593.   size : BYTE;
  1594.   buf : array[0..255] of char;
  1595. begin
  1596.   Text.Clear;
  1597.   if (Stream.Read(size, 1) <> 1) then
  1598.     exit;
  1599.   while (size > 0) do
  1600.   begin
  1601.     ReadCheck(Stream, buf, size);
  1602.     buf[size] := #0;
  1603.     Text.Add(Buf);
  1604.     if (Stream.Read(size, 1) <> 1) then
  1605.       exit;
  1606.   end;
  1607. end;
  1608. ////////////////////////////////////////////////////////////////////////////////
  1609. //
  1610. // Delphi 2.x / C++ Builder 1.x support
  1611. //
  1612. ////////////////////////////////////////////////////////////////////////////////
  1613. {$IFDEF VER9x}
  1614. var
  1615.   // From Delphi 3 graphics.pas
  1616.   SystemPalette16: HPalette; // 16 color palette that maps to the system palette
  1617. type
  1618.   TPixelFormats = set of TPixelFormat;
  1619. const
  1620.   // Only pf1bit, pf4bit and pf8bit is supported since they are the only ones
  1621.   // with palettes
  1622.   SupportedPixelformats: TPixelFormats = [pf1bit, pf4bit, pf8bit];
  1623. {$ENDIF}
  1624. // --------------------------
  1625. // InitializeBitmapInfoHeader
  1626. // --------------------------
  1627. // Fills a TBitmapInfoHeader with the values of a bitmap when converted to a
  1628. // DIB of a specified PixelFormat.
  1629. //
  1630. // Parameters:
  1631. // Bitmap The handle of the source bitmap.
  1632. // Info The TBitmapInfoHeader buffer that will receive the values.
  1633. // PixelFormat The pixel format of the destination DIB.
  1634. //
  1635. {$IFDEF BAD_STACK_ALIGNMENT}
  1636.   // Disable optimization to circumvent optimizer bug...
  1637.   {$IFOPT O+}
  1638.     {$DEFINE O_PLUS}
  1639.     {$O-}
  1640.   {$ENDIF}
  1641. {$ENDIF}
  1642. procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var Info: TBitmapInfoHeader;
  1643.   PixelFormat: TPixelFormat);
  1644. // From graphics.pas, "optimized" for our use
  1645. var
  1646.   DIB : TDIBSection;
  1647.   Bytes : Integer;
  1648. begin
  1649.   DIB.dsbmih.biSize := 0;
  1650.   Bytes := GetObject(Bitmap, SizeOf(DIB), @DIB);
  1651.   if (Bytes = 0) then
  1652.     Error(sInvalidBitmap);
  1653.   if (Bytes >= (sizeof(DIB.dsbm) + sizeof(DIB.dsbmih))) and
  1654.     (DIB.dsbmih.biSize >= sizeof(DIB.dsbmih)) then
  1655.     Info := DIB.dsbmih
  1656.   else
  1657.   begin
  1658.     FillChar(Info, sizeof(Info), 0);
  1659.     with Info, DIB.dsbm do
  1660.     begin
  1661.       biSize := SizeOf(Info);
  1662.       biWidth := bmWidth;
  1663.       biHeight := bmHeight;
  1664.     end;
  1665.   end;
  1666.   case PixelFormat of
  1667.     pf1bit: Info.biBitCount := 1;
  1668.     pf4bit: Info.biBitCount := 4;
  1669.     pf8bit: Info.biBitCount := 8;
  1670.     pf24bit: Info.biBitCount := 24;
  1671.   else
  1672.     Error(sInvalidPixelFormat);
  1673.     // Info.biBitCount := DIB.dsbm.bmBitsPixel * DIB.dsbm.bmPlanes;
  1674.   end;
  1675.   Info.biPlanes := 1;
  1676.   Info.biCompression := BI_RGB; // Always return data in RGB format
  1677.   Info.biSizeImage := AlignBit(Info.biWidth, Info.biBitCount, 32) * Cardinal(abs(Info.biHeight));
  1678. end;
  1679. {$IFDEF O_PLUS}
  1680.   {$O+}
  1681.   {$UNDEF O_PLUS}
  1682. {$ENDIF}
  1683. // -------------------
  1684. // InternalGetDIBSizes
  1685. // -------------------
  1686. // Calculates the buffer sizes nescessary for convertion of a bitmap to a DIB
  1687. // of a specified PixelFormat.
  1688. // See the GetDIBSizes API function for more info.
  1689. //
  1690. // Parameters:
  1691. // Bitmap The handle of the source bitmap.
  1692. // InfoHeaderSize
  1693. // The returned size of a buffer that will receive the DIB's
  1694. // TBitmapInfo structure.
  1695. // ImageSize The returned size of a buffer that will receive the DIB's
  1696. // pixel data.
  1697. // PixelFormat The pixel format of the destination DIB.
  1698. //
  1699. procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
  1700.   var ImageSize: longInt; PixelFormat: TPixelFormat);
  1701. // From graphics.pas, "optimized" for our use
  1702. var
  1703.   Info : TBitmapInfoHeader;
  1704. begin
  1705.   InitializeBitmapInfoHeader(Bitmap, Info, PixelFormat);
  1706.   // Check for palette device format
  1707.   if (Info.biBitCount > 8) then
  1708.   begin
  1709.     // Header but no palette
  1710.     InfoHeaderSize := SizeOf(TBitmapInfoHeader);
  1711.     if ((Info.biCompression and BI_BITFIELDS) <> 0) then
  1712.       Inc(InfoHeaderSize, 12);
  1713.   end else
  1714.     // Header and palette
  1715.     InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl Info.biBitCount);
  1716.   ImageSize := Info.biSizeImage;
  1717. end;
  1718. // --------------
  1719. // InternalGetDIB
  1720. // --------------
  1721. // Converts a bitmap to a DIB of a specified PixelFormat.
  1722. //
  1723. // Parameters:
  1724. // Bitmap The handle of the source bitmap.
  1725. // Pal The handle of the source palette.
  1726. // BitmapInfo The buffer that will receive the DIB's TBitmapInfo structure.
  1727. // A buffer of sufficient size must have been allocated prior to
  1728. // calling this function.
  1729. // Bits The buffer that will receive the DIB's pixel data.
  1730. // A buffer of sufficient size must have been allocated prior to
  1731. // calling this function.
  1732. // PixelFormat The pixel format of the destination DIB.
  1733. //
  1734. // Returns:
  1735. // True on success, False on failure.
  1736. //
  1737. // Note: The InternalGetDIBSizes function can be used to calculate the
  1738. // nescessary sizes of the BitmapInfo and Bits buffers.
  1739. //
  1740. function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
  1741.   var BitmapInfo; var Bits; PixelFormat: TPixelFormat): Boolean;
  1742. // From graphics.pas, "optimized" for our use
  1743. var
  1744.   OldPal : HPALETTE;
  1745.   DC : HDC;
  1746. begin
  1747.   InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), PixelFormat);
  1748.   OldPal := 0;
  1749.   DC := CreateCompatibleDC(0);
  1750.   try
  1751.     if (Palette <> 0) then
  1752.     begin
  1753.       OldPal := SelectPalette(DC, Palette, False);
  1754.       RealizePalette(DC);
  1755.     end;
  1756.     Result := (GetDIBits(DC, Bitmap, 0, abs(TBitmapInfoHeader(BitmapInfo).biHeight),
  1757.       @Bits, TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0);
  1758.   finally
  1759.     if (OldPal <> 0) then
  1760.       SelectPalette(DC, OldPal, False);
  1761.     DeleteDC(DC);
  1762.   end;
  1763. end;
  1764. // ----------
  1765. // DIBFromBit
  1766. // ----------
  1767. // Converts a bitmap to a DIB of a specified PixelFormat.
  1768. // The DIB is returned in a TMemoryStream ready for streaming to a BMP file.
  1769. //
  1770. // Note: As opposed to D2's DIBFromBit function, the returned stream also
  1771. // contains a TBitmapFileHeader at offset 0.
  1772. //
  1773. // Parameters:
  1774. // Stream The TMemoryStream used to store the bitmap data.
  1775. // The stream must be allocated and freed by the caller prior to
  1776. // calling this function.
  1777. // Src The handle of the source bitmap.
  1778. // Pal The handle of the source palette.
  1779. // PixelFormat The pixel format of the destination DIB.
  1780. // DIBHeader A pointer to the DIB's TBitmapInfo (or TBitmapInfoHeader)
  1781. // structure in the memory stream.
  1782. // The size of the structure can either be deduced from the
  1783. // pixel format (i.e. number of colors) or calculated by
  1784. // subtracting the DIBHeader pointer from the DIBBits pointer.
  1785. // DIBBits A pointer to the DIB's pixel data in the memory stream.
  1786. //
  1787. procedure DIBFromBit(Stream: TMemoryStream; Src: HBITMAP;
  1788.   Pal: HPALETTE; PixelFormat: TPixelFormat; var DIBHeader, DIBBits: Pointer);
  1789. // (From D2 graphics.pas, "optimized" for our use)
  1790. var
  1791.   HeaderSize : integer;
  1792.   FileSize : longInt;
  1793.   ImageSize : longInt;
  1794.   BitmapFileHeader : PBitmapFileHeader;
  1795. begin
  1796.   if (Src = 0) then
  1797.     Error(sInvalidBitmap);
  1798.   // Get header- and pixel data size for new pixel format
  1799.   InternalGetDIBSizes(Src, HeaderSize, ImageSize, PixelFormat);
  1800.   // Make room in stream for a TBitmapInfo and pixel data
  1801.   FileSize := sizeof(TBitmapFileHeader) + HeaderSize + ImageSize;
  1802.   Stream.SetSize(FileSize);
  1803.   // Get pointer to TBitmapFileHeader
  1804.   BitmapFileHeader := Stream.Memory;
  1805.   // Get pointer to TBitmapInfo
  1806.   DIBHeader := Pointer(Longint(BitmapFileHeader) + sizeof(TBitmapFileHeader));
  1807.   // Get pointer to pixel data
  1808.   DIBBits := Pointer(Longint(DIBHeader) + HeaderSize);
  1809.   // Initialize file header
  1810.   FillChar(BitmapFileHeader^, sizeof(TBitmapFileHeader), 0);
  1811.   with BitmapFileHeader^ do
  1812.   begin
  1813.     bfType := $4D42; // 'BM' = Windows BMP signature
  1814.     bfSize := FileSize; // File size (not needed)
  1815.     bfOffBits := sizeof(TBitmapFileHeader) + HeaderSize; // Offset of pixel data
  1816.   end;
  1817.   // Get pixel data in new pixel format
  1818.   InternalGetDIB(Src, Pal, DIBHeader^, DIBBits^, PixelFormat);
  1819. end;
  1820. // --------------
  1821. // GetPixelFormat
  1822. // --------------
  1823. // Returns the current pixel format of a bitmap.
  1824. //
  1825. // Replacement for delphi 3 TBitmap.PixelFormat getter.
  1826. //
  1827. // Parameters:
  1828. // Bitmap The bitmap which pixel format is returned.
  1829. //
  1830. // Returns:
  1831. // The PixelFormat of the bitmap
  1832. //
  1833. function GetPixelFormat(Bitmap: TBitmap): TPixelFormat;
  1834. {$IFDEF VER9x}
  1835. // From graphics.pas, "optimized" for our use
  1836. var
  1837.   DIBSection : TDIBSection;
  1838.   Bytes : Integer;
  1839.   Handle : HBitmap;
  1840. begin
  1841.   Result := pfCustom; // This value is never returned
  1842.   // BAD_STACK_ALIGNMENT 
  1843.   // Note: To work around an optimizer bug, we do not use Bitmap.Handle
  1844.   // directly. Instead we store the value and use it indirectly. Unless we do
  1845.   // this, the register containing Bitmap.Handle will be overwritten!
  1846.   Handle := Bitmap.Handle;
  1847.   if (Handle <> 0) then
  1848.   begin
  1849.     Bytes := GetObject(Handle, SizeOf(DIBSection), @DIBSection);
  1850.     if (Bytes = 0) then
  1851.       Error(sInvalidBitmap);
  1852.     with (DIBSection) do
  1853.     begin
  1854.       // Check for NT bitmap
  1855.       if (Bytes < (SizeOf(dsbm) + SizeOf(dsbmih))) or (dsbmih.biSize < SizeOf(dsbmih)) then
  1856.         DIBSection.dsBmih.biBitCount := dsbm.bmBitsPixel * dsbm.bmPlanes;
  1857.       case (dsBmih.biBitCount) of
  1858.         0: Result := pfDevice;
  1859.         1: Result := pf1bit;
  1860.         4: Result := pf4bit;
  1861.         8: Result := pf8bit;
  1862.         16: case (dsBmih.biCompression) of
  1863.               BI_RGB:
  1864.                 Result := pf15Bit;
  1865.               BI_BITFIELDS:
  1866.                 if (dsBitFields[1] = $07E0) then
  1867.                   Result := pf16Bit;
  1868.             end;
  1869.         24: Result := pf24Bit;
  1870.         32: if (dsBmih.biCompression = BI_RGB) then
  1871.               Result := pf32Bit;
  1872.       else
  1873.         Error(sUnsupportedBitmap);
  1874.       end;
  1875.     end;
  1876.   end else
  1877. //    Result := pfDevice;
  1878.     Error(sUnsupportedBitmap);
  1879. end;
  1880. {$ELSE}
  1881. begin
  1882.   Result := Bitmap.PixelFormat;
  1883. end;
  1884. {$ENDIF}
  1885. // --------------
  1886. // SetPixelFormat
  1887. // --------------
  1888. // Changes the pixel format of a TBitmap.
  1889. //
  1890. // Replacement for delphi 3 TBitmap.PixelFormat setter.
  1891. // The returned TBitmap will always be a DIB.
  1892. //
  1893. // Note: Under Delphi 3.x this function will leak a palette handle each time it
  1894. //       converts a TBitmap to pf8bit format!
  1895. //       If possible, use SafeSetPixelFormat instead to avoid this.
  1896. //
  1897. // Parameters:
  1898. // Bitmap The bitmap to modify.
  1899. // PixelFormat The pixel format to convert to.
  1900. //
  1901. procedure SetPixelFormat(Bitmap: TBitmap; PixelFormat: TPixelFormat);
  1902. {$IFDEF VER9x}
  1903. var
  1904.   Stream : TMemoryStream;
  1905.   Header ,
  1906.   Bits : Pointer;
  1907. begin
  1908.   // Can't change anything without a handle
  1909.   if (Bitmap.Handle = 0) then
  1910.     Error(sInvalidBitmap);
  1911.   // Only convert to supported formats
  1912.   if not(PixelFormat in SupportedPixelformats) then
  1913.     Error(sInvalidPixelFormat);
  1914.   // No need to convert to same format
  1915.   if (GetPixelFormat(Bitmap) = PixelFormat) then
  1916.     exit;
  1917.   Stream := TMemoryStream.Create;
  1918.   try
  1919.     // Convert to DIB file in memory stream
  1920.     DIBFromBit(Stream, Bitmap.Handle, Bitmap.Palette, PixelFormat, Header, Bits);
  1921.     // Load DIB from stream
  1922.     Stream.Position := 0;
  1923.     Bitmap.LoadFromStream(Stream);
  1924.   finally
  1925.     Stream.Free;
  1926.   end;
  1927. end;
  1928. {$ELSE}
  1929. begin
  1930.   Bitmap.PixelFormat := PixelFormat;
  1931. end;
  1932. {$ENDIF}
  1933. {$IFDEF VER100}
  1934. var
  1935.   pf8BitBitmap: TBitmap = nil;
  1936. {$ENDIF}
  1937. // ------------------
  1938. // SafeSetPixelFormat
  1939. // ------------------
  1940. // Changes the pixel format of a TBitmap but doesn't preserve the contents.
  1941. //
  1942. // Replacement for Delphi 3 TBitmap.PixelFormat setter.
  1943. // The returned TBitmap will always be an empty DIB of the same size as the
  1944. // original bitmap.
  1945. //
  1946. // This function is used to avoid the palette handle leak that Delphi 3's
  1947. // SetPixelFormat and TBitmap.PixelFormat suffers from.
  1948. //
  1949. // Parameters:
  1950. // Bitmap The bitmap to modify.
  1951. // PixelFormat The pixel format to convert to.
  1952. //
  1953. procedure SafeSetPixelFormat(Bitmap: TBitmap; PixelFormat: TPixelFormat);
  1954. {$IFDEF VER9x}
  1955. begin
  1956.   SetPixelFormat(Bitmap, PixelFormat);
  1957. end;
  1958. {$ELSE}
  1959. {$IFNDEF VER100}
  1960. var
  1961.   Palette : hPalette;
  1962. begin
  1963.   Bitmap.PixelFormat := PixelFormat;
  1964.   // Work around a bug in TBitmap:
  1965.   // When converting to pf8bit format, the palette assigned to TBitmap.Palette
  1966.   // will be a half tone palette (which only contains the 20 system colors).
  1967.   // Unfortunately this is not the palette used to render the bitmap and it
  1968.   // is also not the palette saved with the bitmap.
  1969.   if (PixelFormat = pf8bit) then
  1970.   begin
  1971.     // Disassociate the wrong palette from the bitmap (without affecting
  1972.     // the DIB color table)
  1973.     Palette := Bitmap.ReleasePalette;
  1974.     if (Palette <> 0) then
  1975.       DeleteObject(Palette);
  1976.     // Recreate the palette from the DIB color table
  1977.     Bitmap.Palette;
  1978.   end;
  1979. end;
  1980. {$ELSE}
  1981. var
  1982.   Width ,
  1983.   Height : integer;
  1984. begin
  1985.   if (PixelFormat = pf8bit) then
  1986.   begin
  1987.     // Partial solution to "TBitmap.PixelFormat := pf8bit" leak
  1988.     // by Greg Chapman <glc@well.com>
  1989.     if (pf8BitBitmap = nil) then
  1990.     begin
  1991.       // Create a "template" bitmap
  1992.       // The bitmap is deleted in the finalization section of the unit.
  1993.       pf8BitBitmap:= TBitmap.Create;
  1994.       // Convert template to pf8bit format
  1995.       // This will leak 1 palette handle, but only once
  1996.       pf8BitBitmap.PixelFormat:= pf8Bit;
  1997.     end;
  1998.     // Store the size of the original bitmap
  1999.     Width := Bitmap.Width;
  2000.     Height := Bitmap.Height;
  2001.     // Convert to pf8bit format by copying template
  2002.     Bitmap.Assign(pf8BitBitmap);
  2003.     // Restore the original size
  2004.     Bitmap.Width := Width;
  2005.     Bitmap.Height := Height;
  2006.   end else
  2007.     // This is safe since only pf8bit leaks
  2008.     Bitmap.PixelFormat := PixelFormat;
  2009. end;
  2010. {$ENDIF}
  2011. {$ENDIF}
  2012. {$IFDEF VER9x}
  2013. // -----------
  2014. // CopyPalette
  2015. // -----------
  2016. // Copies a HPALETTE.
  2017. //
  2018. // Copied from D3 graphics.pas.
  2019. // This is declared private in some old versions of Delphi 2 so we have to
  2020. // implement it here to support those old versions.
  2021. //
  2022. // Parameters:
  2023. // Palette The palette to copy.
  2024. //
  2025. // Returns:
  2026. // The handle to a new palette.
  2027. //
  2028. function CopyPalette(Palette: HPALETTE): HPALETTE;
  2029. var
  2030.   PaletteSize: Integer;
  2031.   LogPal: TMaxLogPalette;
  2032. begin
  2033.   Result := 0;
  2034.   if Palette = 0 then Exit;
  2035.   PaletteSize := 0;
  2036.   if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
  2037.   if PaletteSize = 0 then Exit;
  2038.   with LogPal do
  2039.   begin
  2040.     palVersion := $0300;
  2041.     palNumEntries := PaletteSize;
  2042.     GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
  2043.   end;
  2044.   Result := CreatePalette(PLogPalette(@LogPal)^);
  2045. end;
  2046. // TThreadList implementation from Delphi 3 classes.pas
  2047. constructor TThreadList.Create;
  2048. begin
  2049.   inherited Create;
  2050.   InitializeCriticalSection(FLock);
  2051.   FList := TList.Create;
  2052. end;
  2053. destructor TThreadList.Destroy;
  2054. begin
  2055.   LockList;    // Make sure nobody else is inside the list.
  2056.   try
  2057.     FList.Free;
  2058.     inherited Destroy;
  2059.   finally
  2060.     UnlockList;
  2061.     DeleteCriticalSection(FLock);
  2062.   end;
  2063. end;
  2064. procedure TThreadList.Add(Item: Pointer);
  2065. begin
  2066.   LockList;
  2067.   try
  2068.     if FList.IndexOf(Item) = -1 then
  2069.       FList.Add(Item);
  2070.   finally
  2071.     UnlockList;
  2072.   end;
  2073. end;
  2074. procedure TThreadList.Clear;
  2075. begin
  2076.   LockList;
  2077.   try
  2078.     FList.Clear;
  2079.   finally
  2080.     UnlockList;
  2081.   end;
  2082. end;
  2083. function  TThreadList.LockList: TList;
  2084. begin
  2085.   EnterCriticalSection(FLock);
  2086.   Result := FList;
  2087. end;
  2088. procedure TThreadList.Remove(Item: Pointer);
  2089. begin
  2090.   LockList;
  2091.   try
  2092.     FList.Remove(Item);
  2093.   finally
  2094.     UnlockList;
  2095.   end;
  2096. end;
  2097. procedure TThreadList.UnlockList;
  2098. begin
  2099.   LeaveCriticalSection(FLock);
  2100. end;
  2101. // End of TThreadList implementation
  2102. // From Delphi 3 sysutils.pas
  2103. { CompareMem performs a binary compare of Length bytes of memory referenced
  2104.   by P1 to that of P2.  CompareMem returns True if the memory referenced by
  2105.   P1 is identical to that of P2. }
  2106. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  2107. asm
  2108.         PUSH    ESI
  2109.         PUSH    EDI
  2110.         MOV     ESI,P1
  2111.         MOV     EDI,P2
  2112.         MOV     EDX,ECX
  2113.         XOR     EAX,EAX
  2114.         AND     EDX,3
  2115.         SHR     ECX,1
  2116.         SHR     ECX,1
  2117.         REPE    CMPSD
  2118.         JNE     @@2
  2119.         MOV     ECX,EDX
  2120.         REPE    CMPSB
  2121.         JNE     @@2
  2122. @@1:    INC     EAX
  2123. @@2:    POP     EDI
  2124.         POP     ESI
  2125. end;
  2126. // Dummy ASSERT procedure since ASSERT does not exist in Delphi 2.x
  2127. procedure ASSERT(Condition: boolean; Message: string);
  2128. begin
  2129. end;
  2130. {$ENDIF} // Delphi 2.x stuff
  2131. ////////////////////////////////////////////////////////////////////////////////
  2132. //
  2133. // TDIB Classes
  2134. //
  2135. //  These classes gives read and write access to TBitmap's pixel data
  2136. //  independently of the Delphi version used.
  2137. //
  2138. ////////////////////////////////////////////////////////////////////////////////
  2139. type
  2140.   TDIB = class(TObject)
  2141.   private
  2142.     FBitmap : TBitmap;
  2143.     FPixelFormat : TPixelFormat;
  2144.   protected
  2145.     function GetScanline(Row: integer): pointer; virtual; abstract;
  2146.     constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2147.   public
  2148.     property Scanline[Row: integer]: pointer read GetScanline;
  2149.     property Bitmap: TBitmap read FBitmap;
  2150.     property PixelFormat: TPixelFormat read FPixelFormat;
  2151.   end;
  2152.   TDIBReader = class(TDIB)
  2153.   private
  2154. {$ifdef VER9x}
  2155.     FDIB : TDIBSection;
  2156.     FDC : HDC;
  2157.     FScanLine : pointer;
  2158.     FLastRow : integer;
  2159.     FInfo : PBitmapInfo;
  2160.     FBytes : integer;
  2161. {$endif}
  2162.   protected
  2163.     function GetScanline(Row: integer): pointer; override;
  2164.   public
  2165.     constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2166.     destructor Destroy; override;
  2167.   end;
  2168.   TDIBWriter = class(TDIB)
  2169.   private
  2170. {$ifdef PIXELFORMAT_TOO_SLOW}
  2171.     FDIBInfo : PBitmapInfo;
  2172.     FDIBBits : pointer;
  2173.     FDIBInfoSize : integer;
  2174.     FDIBBitsSize : longInt;
  2175. {$ifndef CREATEDIBSECTION_SLOW}
  2176.     FDIB : HBITMAP;
  2177. {$endif}
  2178. {$endif}
  2179.     FPalette : HPalette;
  2180.     FHeight : integer;
  2181.     FWidth : integer;
  2182.   protected
  2183.     procedure CreateDIB;
  2184.     procedure FreeDIB;
  2185.     procedure NeedDIB;
  2186.     function GetScanline(Row: integer): pointer; override;
  2187.   public
  2188.     constructor Create(ABitmap: TBitmap; APixelFormat: TPixelFormat;
  2189.       AWidth, AHeight: integer; APalette: HPalette);
  2190.     destructor Destroy; override;
  2191.     procedure UpdateBitmap;
  2192.     property Width: integer read FWidth;
  2193.     property Height: integer read FHeight;
  2194.     property Palette: HPalette read FPalette;
  2195.   end;
  2196. ////////////////////////////////////////////////////////////////////////////////
  2197. constructor TDIB.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2198. begin
  2199.   inherited Create;
  2200.   FBitmap := ABitmap;
  2201.   FPixelFormat := APixelFormat;
  2202. end;
  2203. ////////////////////////////////////////////////////////////////////////////////
  2204. constructor TDIBReader.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat);
  2205. {$ifdef VER9x}
  2206. var
  2207.   InfoHeaderSize : integer;
  2208.   ImageSize : longInt;
  2209. {$endif}
  2210. begin
  2211.   inherited Create(ABitmap, APixelFormat);
  2212. {$ifndef VER9x}
  2213.   SetPixelFormat(FBitmap, FPixelFormat);
  2214. {$else}
  2215.   FDC := CreateCompatibleDC(0);
  2216.   SelectPalette(FDC, FBitmap.Palette, False);
  2217.   // Allocate DIB info structure
  2218.   InternalGetDIBSizes(ABitmap.Handle, InfoHeaderSize, ImageSize, APixelFormat);
  2219.   GetMem(FInfo, InfoHeaderSize);
  2220.   // Get DIB info
  2221.   InitializeBitmapInfoHeader(ABitmap.Handle, FInfo^.bmiHeader, APixelFormat);
  2222.   // Allocate scan line buffer
  2223.   GetMem(FScanLine, ImageSize DIV abs(FInfo^.bmiHeader.biHeight));
  2224.   FLastRow := -1;
  2225. {$endif}
  2226. end;
  2227. destructor TDIBReader.Destroy;
  2228. begin
  2229. {$ifdef VER9x}
  2230.   DeleteDC(FDC);
  2231.   FreeMem(FScanLine);
  2232.   FreeMem(FInfo);
  2233. {$endif}
  2234.   inherited Destroy;
  2235. end;
  2236. function TDIBReader.GetScanline(Row: integer): pointer;
  2237. begin
  2238. {$ifdef VER9x}
  2239.   if (Row < 0) or (Row >= FBitmap.Height) then
  2240.     raise EInvalidGraphicOperation.Create(SScanLine);
  2241.   GDIFlush;
  2242.   Result := FScanLine;
  2243.   if (Row = FLastRow) then
  2244.     exit;
  2245.   FLastRow := Row;
  2246.   if (FInfo^.bmiHeader.biHeight > 0) then  // bottom-up DIB
  2247.     Row := FInfo^.bmiHeader.biHeight - Row - 1;
  2248.   GetDIBits(FDC, FBitmap.Handle, Row, 1, FScanLine, FInfo^, DIB_RGB_COLORS);
  2249. {$else}
  2250.   Result := FBitmap.ScanLine[Row];
  2251. {$endif}
  2252. end;
  2253. ////////////////////////////////////////////////////////////////////////////////
  2254. constructor TDIBWriter.Create(ABitmap: TBitmap; APixelFormat: TPixelFormat;
  2255.   AWidth, AHeight: integer; APalette: HPalette);
  2256. begin
  2257.   inherited Create(ABitmap, APixelFormat);
  2258.   // DIB writer only supports 8 or 24 bit bitmaps
  2259.   if not(APixelFormat in [pf8bit, pf24bit]) then
  2260.     Error(sInvalidPixelFormat);
  2261.   if (AWidth = 0) or (AHeight = 0) then
  2262.     Error(sBadDimension);
  2263.   FHeight := AHeight;
  2264.   FWidth := AWidth;
  2265. {$ifndef PIXELFORMAT_TOO_SLOW}
  2266.   FBitmap.Palette := 0;
  2267.   FBitmap.Height := FHeight;
  2268.   FBitmap.Width := FWidth;
  2269.   SafeSetPixelFormat(FBitmap, FPixelFormat);
  2270.   FPalette := CopyPalette(APalette);
  2271.   FBitmap.Palette := FPalette;
  2272. {$else}
  2273.   FPalette := APalette;
  2274.   FDIBInfo := nil;
  2275.   FDIBBits := nil;
  2276. {$ifndef CREATEDIBSECTION_SLOW}
  2277.   FDIB := 0;
  2278. {$endif}
  2279. {$endif}
  2280. end;
  2281. destructor TDIBWriter.Destroy;
  2282. begin
  2283.   UpdateBitmap;
  2284.   FreeDIB;
  2285.   inherited Destroy;
  2286. end;
  2287. function TDIBWriter.GetScanline(Row: integer): pointer;
  2288. begin
  2289. {$ifdef PIXELFORMAT_TOO_SLOW}
  2290.   NeedDIB;
  2291.   if (FDIBBits = nil) then
  2292.     Error(sNoDIB);
  2293.   with FDIBInfo^.bmiHeader do
  2294.   begin
  2295.     if (Row < 0) or (Row >= Height) then
  2296.       raise EInvalidGraphicOperation.Create(SScanLine);
  2297.     GDIFlush;
  2298.     if biHeight > 0 then  // bottom-up DIB
  2299.       Row := biHeight - Row - 1;
  2300.     Result := PChar(Cardinal(FDIBBits) + Cardinal(Row) * AlignBit(biWidth, biBitCount, 32));
  2301.   end;
  2302. {$else}
  2303.   Result := FBitmap.ScanLine[Row];
  2304. {$endif}
  2305. end;
  2306. procedure TDIBWriter.CreateDIB;
  2307. {$IFDEF PIXELFORMAT_TOO_SLOW}
  2308. var
  2309.   SrcColors : WORD;
  2310. //  ScreenDC : HDC;
  2311.   // From Delphi 3.02 graphics.pas
  2312.   // There is a bug in the ByteSwapColors from Delphi 3.0!
  2313.   procedure ByteSwapColors(var Colors; Count: Integer);
  2314.   var   // convert RGB to BGR and vice-versa.  TRGBQuad <-> TPaletteEntry
  2315.     SysInfo: TSystemInfo;
  2316.   begin
  2317.     GetSystemInfo(SysInfo);
  2318.     asm
  2319.           MOV   EDX, Colors
  2320.           MOV   ECX, Count
  2321.           DEC   ECX
  2322.           JS    @@END
  2323.           LEA   EAX, SysInfo
  2324.           CMP   [EAX].TSystemInfo.wProcessorLevel, 3
  2325.           JE    @@386
  2326.     @@1:  MOV   EAX, [EDX+ECX*4]
  2327.           BSWAP EAX
  2328.           SHR   EAX,8
  2329.           MOV   [EDX+ECX*4],EAX
  2330.           DEC   ECX
  2331.           JNS   @@1
  2332.           JMP   @@END
  2333.     @@386:
  2334.           PUSH  EBX
  2335.     @@2:  XOR   EBX,EBX
  2336.           MOV   EAX, [EDX+ECX*4]
  2337.           MOV   BH, AL
  2338.           MOV   BL, AH
  2339.           SHR   EAX,16
  2340.           SHL   EBX,8
  2341.           MOV   BL, AL
  2342.           MOV   [EDX+ECX*4],EBX
  2343.           DEC   ECX
  2344.           JNS   @@2
  2345.           POP   EBX
  2346.       @@END:
  2347.     end;
  2348.   end;
  2349. {$ENDIF}
  2350. begin
  2351. {$ifdef PIXELFORMAT_TOO_SLOW}
  2352.   FreeDIB;
  2353.   if (PixelFormat = pf8bit) then
  2354.     // 8 bit: Header and palette
  2355.     FDIBInfoSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl 8)
  2356.   else
  2357.     // 24 bit: Header but no palette
  2358.     FDIBInfoSize := SizeOf(TBitmapInfoHeader);
  2359.   // Allocate TBitmapInfo structure
  2360.   GetMem(FDIBInfo, FDIBInfoSize);
  2361.   try
  2362.     FDIBInfo^.bmiHeader.biSize := SizeOf(FDIBInfo^.bmiHeader);
  2363.     FDIBInfo^.bmiHeader.biWidth := Width;
  2364.     FDIBInfo^.bmiHeader.biHeight := Height;
  2365.     FDIBInfo^.bmiHeader.biPlanes := 1;
  2366.     FDIBInfo^.bmiHeader.biSizeImage := 0;
  2367.     FDIBInfo^.bmiHeader.biCompression := BI_RGB;
  2368.     if (PixelFormat = pf8bit) then
  2369.     begin
  2370.       FDIBInfo^.bmiHeader.biBitCount := 8;
  2371.       // Find number of colors defined by palette
  2372.       if (Palette <> 0) and
  2373.         (GetObject(Palette, sizeof(SrcColors), @SrcColors) <> 0) and
  2374.         (SrcColors <> 0) then
  2375.       begin
  2376.         // Copy all colors...
  2377.         GetPaletteEntries(Palette, 0, SrcColors, FDIBInfo^.bmiColors[0]);
  2378.         // ...and convert BGR to RGB
  2379.         ByteSwapColors(FDIBInfo^.bmiColors[0], SrcColors);
  2380.       end else
  2381.         SrcColors := 0;
  2382.       // Finally zero any unused entried
  2383.       if (SrcColors < 256) then
  2384.         FillChar(pointer(LongInt(@FDIBInfo^.bmiColors)+SizeOf(TRGBQuad)*SrcColors)^,
  2385.           256 - SrcColors, 0);
  2386.       FDIBInfo^.bmiHeader.biClrUsed := 256;
  2387.       FDIBInfo^.bmiHeader.biClrImportant := SrcColors;
  2388.     end else
  2389.     begin
  2390.       FDIBInfo^.bmiHeader.biBitCount := 24;
  2391.       FDIBInfo^.bmiHeader.biClrUsed := 0;
  2392.       FDIBInfo^.bmiHeader.biClrImportant := 0;
  2393.     end;
  2394.     FDIBBitsSize := AlignBit(Width, FDIBInfo^.bmiHeader.biBitCount, 32) * Cardinal(abs(Height));
  2395. {$ifdef CREATEDIBSECTION_SLOW}
  2396.     FDIBBits := GlobalAllocPtr(GMEM_MOVEABLE, FDIBBitsSize);
  2397.     if (FDIBBits = nil) then
  2398.       raise EOutOfMemory.Create(sOutOfMemDIB);
  2399. {$else}
  2400. //    ScreenDC := GDICheck(GetDC(0));
  2401.     try
  2402.       // Allocate DIB section
  2403.       // Note: You can ignore warnings about the HDC parameter being 0. The
  2404.       // parameter is not used for 24 bit bitmaps
  2405.       FDIB := GDICheck(CreateDIBSection(0 {ScreenDC}, FDIBInfo^, DIB_RGB_COLORS,
  2406.         FDIBBits,
  2407.         {$IFDEF VER9x} nil, {$ELSE} 0, {$ENDIF}
  2408.         0));
  2409.     finally
  2410. //      ReleaseDC(0, ScreenDC);
  2411.     end;
  2412. {$endif}
  2413.   except
  2414.     FreeDIB;
  2415.     raise;
  2416.   end;
  2417. {$endif}
  2418. end;
  2419. procedure TDIBWriter.FreeDIB;
  2420. begin
  2421. {$ifdef PIXELFORMAT_TOO_SLOW}
  2422.   if (FDIBInfo <> nil) then
  2423.     FreeMem(FDIBInfo);
  2424. {$ifdef CREATEDIBSECTION_SLOW}
  2425.   if (FDIBBits <> nil) then
  2426.     GlobalFreePtr(FDIBBits);
  2427. {$else}
  2428.   if (FDIB <> 0) then
  2429.     DeleteObject(FDIB);
  2430.   FDIB := 0;
  2431. {$endif}
  2432.   FDIBInfo := nil;
  2433.   FDIBBits := nil;
  2434. {$endif}
  2435. end;
  2436. procedure TDIBWriter.NeedDIB;
  2437. begin
  2438. {$ifdef PIXELFORMAT_TOO_SLOW}
  2439. {$ifdef CREATEDIBSECTION_SLOW}
  2440.   if (FDIBBits = nil) then
  2441. {$else}
  2442.   if (FDIB = 0) then
  2443. {$endif}
  2444.     CreateDIB;
  2445. {$endif}
  2446. end;
  2447. // Convert the DIB created by CreateDIB back to a TBitmap
  2448. procedure TDIBWriter.UpdateBitmap;
  2449. {$ifdef PIXELFORMAT_TOO_SLOW}
  2450. var
  2451.   Stream : TMemoryStream;
  2452.   FileSize : longInt;
  2453.   BitmapFileHeader : TBitmapFileHeader;
  2454. {$endif}
  2455. begin
  2456. {$ifdef PIXELFORMAT_TOO_SLOW}
  2457. {$ifdef CREATEDIBSECTION_SLOW}
  2458.   if (FDIBBits = nil) then
  2459. {$else}
  2460.   if (FDIB = 0) then
  2461. {$endif}
  2462.     exit;
  2463.   // Win95 and NT differs in what solution performs best
  2464. {$ifndef CREATEDIBSECTION_SLOW}
  2465. {$ifdef VER10_PLUS}
  2466.   if (Win32Platform = VER_PLATFORM_WIN32_NT) then
  2467.   begin
  2468.     // Assign DIB to bitmap
  2469.     FBitmap.Handle := FDIB;
  2470.     FDIB := 0;
  2471.     FBitmap.Palette := CopyPalette(Palette);
  2472.   end else
  2473. {$endif}
  2474. {$endif}
  2475.   begin
  2476.     // Write DIB to a stream in the BMP file format
  2477.     Stream := TMemoryStream.Create;
  2478.     try
  2479.       // Make room in stream for a TBitmapInfo and pixel data
  2480.       FileSize := sizeof(TBitmapFileHeader) + FDIBInfoSize + FDIBBitsSize;
  2481.       Stream.SetSize(FileSize);
  2482.       // Initialize file header
  2483.       FillChar(BitmapFileHeader, sizeof(TBitmapFileHeader), 0);
  2484.       with BitmapFileHeader do
  2485.       begin
  2486.         bfType := $4D42; // 'BM' = Windows BMP signature
  2487.         bfSize := FileSize; // File size (not needed)
  2488.         bfOffBits := sizeof(TBitmapFileHeader) + FDIBInfoSize; // Offset of pixel data
  2489.       end;
  2490.       // Save file header
  2491.       Stream.Write(BitmapFileHeader, sizeof(TBitmapFileHeader));
  2492.       // Save TBitmapInfo structure
  2493.       Stream.Write(FDIBInfo^, FDIBInfoSize);
  2494.       // Save pixel data
  2495.       Stream.Write(FDIBBits^, FDIBBitsSize);
  2496.       // Rewind and load bitmap from stream
  2497.       Stream.Position := 0;
  2498.       FBitmap.LoadFromStream(Stream);
  2499.     finally
  2500.       Stream.Free;
  2501.     end;
  2502.   end;
  2503. {$endif}
  2504. end;
  2505. ////////////////////////////////////////////////////////////////////////////////
  2506. //
  2507. // Color Mapping
  2508. //
  2509. ////////////////////////////////////////////////////////////////////////////////
  2510. type
  2511.   TColorLookup = class(TObject)
  2512.   private
  2513.     FColors : integer;
  2514.   public
  2515.     constructor Create(Palette: hPalette); virtual;
  2516.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; virtual; abstract;
  2517.     property Colors: integer read FColors;
  2518.   end;
  2519.   PRGBQuadArray = ^TRGBQuadArray; // From Delphi 3 graphics.pas
  2520.   TRGBQuadArray = array[Byte] of TRGBQuad; // From Delphi 3 graphics.pas
  2521.   BGRArray = array[0..0] of TRGBTriple;
  2522.   PBGRArray = ^BGRArray;
  2523.   PalArray =  array[byte] of TPaletteEntry;
  2524.   PPalArray = ^PalArray;
  2525.   // TFastColorLookup implements a simple but reasonably fast generic color
  2526.   // mapper. It trades precision for speed by reducing the size of the color
  2527.   // space.
  2528.   // Using a class instead of inline code results in a speed penalty of
  2529.   // approx. 15% but reduces the complexity of the color reduction routines that
  2530.   // uses it. If bitmap to GIF conversion speed is really important to you, the
  2531.   // implementation can easily be inlined again.
  2532.   TInverseLookup = array[0..1 SHL 15-1] of SmallInt;
  2533.   PInverseLookup = ^TInverseLookup;
  2534.   TFastColorLookup = class(TColorLookup)
  2535.   private
  2536.     FPaletteEntries : PPalArray;
  2537.     FInverseLookup : PInverseLookup;
  2538.   public
  2539.     constructor Create(Palette: hPalette); override;
  2540.     destructor Destroy; override;
  2541.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2542.   end;
  2543.   // TSlowColorLookup implements a precise but very slow generic color mapper.
  2544.   // It uses the GetNearestPaletteIndex GDI function.
  2545.   // Note: Tests has shown TFastColorLookup to be more precise than
  2546.   // TSlowColorLookup in many cases. I can't explain why...
  2547.   TSlowColorLookup = class(TColorLookup)
  2548.   private
  2549.     FPaletteEntries : PPalArray;
  2550.     FPalette : hPalette;
  2551.   public
  2552.     constructor Create(Palette: hPalette); override;
  2553.     destructor Destroy; override;
  2554.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2555.   end;
  2556.   // TNetscapeColorLookup maps colors to the netscape 6*6*6 color cube.
  2557.   TNetscapeColorLookup = class(TColorLookup)
  2558.   public
  2559.     constructor Create(Palette: hPalette); override;
  2560.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2561.   end;
  2562.   // TGrayWindowsLookup maps colors to 4 shade palette.
  2563.   TGrayWindowsLookup = class(TSlowColorLookup)
  2564.   public
  2565.     constructor Create(Palette: hPalette); override;
  2566.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2567.   end;
  2568.   // TGrayScaleLookup maps colors to a uniform 256 shade palette.
  2569.   TGrayScaleLookup = class(TColorLookup)
  2570.   public
  2571.     constructor Create(Palette: hPalette); override;
  2572.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2573.   end;
  2574.   // TMonochromeLookup maps colors to a black/white palette.
  2575.   TMonochromeLookup = class(TColorLookup)
  2576.   public
  2577.     constructor Create(Palette: hPalette); override;
  2578.     function Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2579.   end;
  2580. constructor TColorLookup.Create(Palette: hPalette);
  2581. begin
  2582.   inherited Create;
  2583. end;
  2584. constructor TFastColorLookup.Create(Palette: hPalette);
  2585. var
  2586.   i : integer;
  2587.   InverseIndex : integer;
  2588. begin
  2589.   inherited Create(Palette);
  2590.   GetMem(FPaletteEntries, sizeof(TPaletteEntry) * 256);
  2591.   FColors := GetPaletteEntries(Palette, 0, 256, FPaletteEntries^);
  2592.   New(FInverseLookup);
  2593.   for i := low(TInverseLookup) to high(TInverseLookup) do
  2594.     FInverseLookup^[i] := -1;
  2595.   // Premap palette colors
  2596.   if (FColors > 0) then
  2597.     for i := 0 to FColors-1 do
  2598.       with FPaletteEntries^[i] do
  2599.       begin
  2600.         InverseIndex := (peRed SHR 3) OR ((peGreen AND $F8) SHL 2) OR ((peBlue AND $F8) SHL 7);
  2601.         if (FInverseLookup^[InverseIndex] = -1) then
  2602.           FInverseLookup^[InverseIndex] := i;
  2603.       end;
  2604. end;
  2605. destructor TFastColorLookup.Destroy;
  2606. begin
  2607.   if (FPaletteEntries <> nil) then
  2608.     FreeMem(FPaletteEntries);
  2609.   if (FInverseLookup <> nil) then
  2610.     Dispose(FInverseLookup);
  2611.   inherited Destroy;
  2612. end;
  2613. // Map color to arbitrary palette
  2614. function TFastColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2615. var
  2616.   i : integer;
  2617.   InverseIndex : integer;
  2618.   Delta ,
  2619.   MinDelta ,
  2620.   MinColor : integer;
  2621. begin
  2622.   // Reduce color space with 3 bits in each dimension
  2623.   InverseIndex := (Red SHR 3) OR ((Green AND $F8) SHL 2) OR ((Blue AND $F8) SHL 7);
  2624.   if (FInverseLookup^[InverseIndex] <> -1) then
  2625.     Result := char(FInverseLookup^[InverseIndex])
  2626.   else
  2627.   begin
  2628.     // Sequential scan for nearest color to minimize euclidian distance
  2629.     MinDelta := 3 * (256 * 256);
  2630.     MinColor := 0;
  2631.     for i := 0 to FColors-1 do
  2632.       with FPaletteEntries[i] do
  2633.       begin
  2634.         Delta := ABS(peRed - Red) + ABS(peGreen - Green) + ABS(peBlue - Blue);
  2635.         if (Delta < MinDelta) then
  2636.         begin
  2637.           MinDelta := Delta;
  2638.           MinColor := i;
  2639.         end;
  2640.       end;
  2641.     Result := char(MinColor);
  2642.     FInverseLookup^[InverseIndex] := MinColor;
  2643.   end;
  2644.   with FPaletteEntries^[ord(Result)] do
  2645.   begin
  2646.     R := peRed;
  2647.     G := peGreen;
  2648.     B := peBlue;
  2649.   end;
  2650. end;
  2651. constructor TSlowColorLookup.Create(Palette: hPalette);
  2652. begin
  2653.   inherited Create(Palette);
  2654.   FPalette := Palette;
  2655.   FColors := GetPaletteEntries(Palette, 0, 256, nil^);
  2656.   if (FColors > 0) then
  2657.   begin
  2658.     GetMem(FPaletteEntries, sizeof(TPaletteEntry) * FColors);
  2659.     FColors := GetPaletteEntries(Palette, 0, 256, FPaletteEntries^);
  2660.   end;
  2661. end;
  2662. destructor TSlowColorLookup.Destroy;
  2663. begin
  2664.   if (FPaletteEntries <> nil) then
  2665.     FreeMem(FPaletteEntries);
  2666.   inherited Destroy;
  2667. end;
  2668. // Map color to arbitrary palette
  2669. function TSlowColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2670. begin
  2671.   Result := char(GetNearestPaletteIndex(FPalette, Red OR (Green SHL 8) OR (Blue SHL 16)));
  2672.   if (FPaletteEntries <> nil) then
  2673.     with FPaletteEntries^[ord(Result)] do
  2674.     begin
  2675.       R := peRed;
  2676.       G := peGreen;
  2677.       B := peBlue;
  2678.     end;
  2679. end;
  2680. constructor TNetscapeColorLookup.Create(Palette: hPalette);
  2681. begin
  2682.   inherited Create(Palette);
  2683.   FColors := 6*6*6; // This better be true or something is wrong
  2684. end;
  2685. // Map color to netscape 6*6*6 color cube
  2686. function TNetscapeColorLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2687. begin
  2688.   R := (Red+3) DIV 51;
  2689.   G := (Green+3) DIV 51;
  2690.   B := (Blue+3) DIV 51;
  2691.   Result := char(B + 6*G + 36*R);
  2692.   R := R * 51;
  2693.   G := G * 51;
  2694.   B := B * 51;
  2695. end;
  2696. constructor TGrayWindowsLookup.Create(Palette: hPalette);
  2697. begin
  2698.   inherited Create(Palette);
  2699.   FColors := 4;
  2700. end;
  2701. // Convert color to windows grays
  2702. function TGrayWindowsLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2703. begin
  2704.   Result := inherited Lookup(MulDiv(Red, 77, 256),
  2705.     MulDiv(Green, 150, 256), MulDiv(Blue, 29, 256), R, G, B);
  2706. end;
  2707. constructor TGrayScaleLookup.Create(Palette: hPalette);
  2708. begin
  2709.   inherited Create(Palette);
  2710.   FColors := 256;
  2711. end;
  2712. // Convert color to grayscale
  2713. function TGrayScaleLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2714. begin
  2715.   Result := char((Blue*29 + Green*150 + Red*77) DIV 256);
  2716.   R := ord(Result);
  2717.   G := ord(Result);
  2718.   B := ord(Result);
  2719. end;
  2720. constructor TMonochromeLookup.Create(Palette: hPalette);
  2721. begin
  2722.   inherited Create(Palette);
  2723.   FColors := 2;
  2724. end;
  2725. // Convert color to black/white
  2726. function TMonochromeLookup.Lookup(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2727. begin
  2728.   if ((Blue*29 + Green*150 + Red*77) > 32512) then
  2729.   begin
  2730.     Result := #1;
  2731.     R := 255;
  2732.     G := 255;
  2733.     B := 255;
  2734.   end else
  2735.   begin
  2736.     Result := #0;
  2737.     R := 0;
  2738.     G := 0;
  2739.     B := 0;
  2740.   end;
  2741. end;
  2742. ////////////////////////////////////////////////////////////////////////////////
  2743. //
  2744. // Dithering engine
  2745. //
  2746. ////////////////////////////////////////////////////////////////////////////////
  2747. type
  2748.   TDitherEngine = class
  2749.   private
  2750.   protected
  2751.     FDirection : integer;
  2752.     FColumn : integer;
  2753.     FLookup : TColorLookup;
  2754.     Width : integer;
  2755.   public
  2756.     constructor Create(AWidth: integer; Lookup: TColorLookup); virtual;
  2757.     function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; virtual;
  2758.     procedure NextLine; virtual;
  2759.     procedure NextColumn;
  2760.     property Direction: integer read FDirection;
  2761.     property Column: integer read FColumn;
  2762.   end;
  2763.   // Note: TErrorTerm does only *need* to be 16 bits wide, but since
  2764.   // it is *much* faster to use native machine words (32 bit), we sacrifice
  2765.   // some bytes (a lot actually) to improve performance.
  2766.   TErrorTerm = Integer;
  2767.   TErrors = array[0..0] of TErrorTerm;
  2768.   PErrors = ^TErrors;
  2769.   TFloydSteinbergDitherer = class(TDitherEngine)
  2770.   private
  2771.     ErrorsR ,
  2772.     ErrorsG ,
  2773.     ErrorsB : PErrors;
  2774.     ErrorR ,
  2775.     ErrorG ,
  2776.     ErrorB : PErrors;
  2777.     CurrentErrorR , // Current error or pixel value
  2778.     CurrentErrorG ,
  2779.     CurrentErrorB ,
  2780.     BelowErrorR , // Error for pixel below current
  2781.     BelowErrorG ,
  2782.     BelowErrorB ,
  2783.     BelowPrevErrorR , // Error for pixel below previous pixel
  2784.     BelowPrevErrorG ,
  2785.     BelowPrevErrorB : TErrorTerm;
  2786.   public
  2787.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2788.     destructor Destroy; override;
  2789.     function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2790.     procedure NextLine; override;
  2791.   end;
  2792.   T5by3Ditherer = class(TDitherEngine)
  2793.   private
  2794.     ErrorsR0 ,
  2795.     ErrorsG0 ,
  2796.     ErrorsB0 ,
  2797.     ErrorsR1 ,
  2798.     ErrorsG1 ,
  2799.     ErrorsB1 ,
  2800.     ErrorsR2 ,
  2801.     ErrorsG2 ,
  2802.     ErrorsB2 : PErrors;
  2803.     ErrorR0 ,
  2804.     ErrorG0 ,
  2805.     ErrorB0 ,
  2806.     ErrorR1 ,
  2807.     ErrorG1 ,
  2808.     ErrorB1 ,
  2809.     ErrorR2 ,
  2810.     ErrorG2 ,
  2811.     ErrorB2 : PErrors;
  2812.     FDirection2 : integer;
  2813.   protected
  2814.     FDivisor : integer;
  2815.     procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); virtual; abstract;
  2816.   public
  2817.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2818.     destructor Destroy; override;
  2819.     function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2820.     procedure NextLine; override;
  2821.   end;
  2822.   TStuckiDitherer = class(T5by3Ditherer)
  2823.   protected
  2824.     procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2825.   public
  2826.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2827.   end;
  2828.   TSierraDitherer = class(T5by3Ditherer)
  2829.   protected
  2830.     procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2831.   public
  2832.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2833.   end;
  2834.   TJaJuNiDitherer = class(T5by3Ditherer)
  2835.   protected
  2836.     procedure Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer); override;
  2837.   public
  2838.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2839.   end;
  2840.   TSteveArcheDitherer = class(TDitherEngine)
  2841.   private
  2842.     ErrorsR0 ,
  2843.     ErrorsG0 ,
  2844.     ErrorsB0 ,
  2845.     ErrorsR1 ,
  2846.     ErrorsG1 ,
  2847.     ErrorsB1 ,
  2848.     ErrorsR2 ,
  2849.     ErrorsG2 ,
  2850.     ErrorsB2 ,
  2851.     ErrorsR3 ,
  2852.     ErrorsG3 ,
  2853.     ErrorsB3 : PErrors;
  2854.     ErrorR0 ,
  2855.     ErrorG0 ,
  2856.     ErrorB0 ,
  2857.     ErrorR1 ,
  2858.     ErrorG1 ,
  2859.     ErrorB1 ,
  2860.     ErrorR2 ,
  2861.     ErrorG2 ,
  2862.     ErrorB2 ,
  2863.     ErrorR3 ,
  2864.     ErrorG3 ,
  2865.     ErrorB3 : PErrors;
  2866.     FDirection2 ,
  2867.     FDirection3 : integer;
  2868.   public
  2869.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2870.     destructor Destroy; override;
  2871.     function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2872.     procedure NextLine; override;
  2873.   end;
  2874.   TBurkesDitherer = class(TDitherEngine)
  2875.   private
  2876.     ErrorsR0 ,
  2877.     ErrorsG0 ,
  2878.     ErrorsB0 ,
  2879.     ErrorsR1 ,
  2880.     ErrorsG1 ,
  2881.     ErrorsB1 : PErrors;
  2882.     ErrorR0 ,
  2883.     ErrorG0 ,
  2884.     ErrorB0 ,
  2885.     ErrorR1 ,
  2886.     ErrorG1 ,
  2887.     ErrorB1 : PErrors;
  2888.     FDirection2 : integer;
  2889.   public
  2890.     constructor Create(AWidth: integer; Lookup: TColorLookup); override;
  2891.     destructor Destroy; override;
  2892.     function Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char; override;
  2893.     procedure NextLine; override;
  2894.   end;
  2895. ////////////////////////////////////////////////////////////////////////////////
  2896. // TDitherEngine
  2897. constructor TDitherEngine.Create(AWidth: integer; Lookup: TColorLookup);
  2898. begin
  2899.   inherited Create;
  2900.   FLookup := Lookup;
  2901.   Width := AWidth;
  2902.   FDirection := 1;
  2903.   FColumn := 0;
  2904. end;
  2905. function TDitherEngine.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2906. begin
  2907.   // Map color to palette
  2908.   Result := FLookup.Lookup(Red, Green, Blue, R, G, B);
  2909.   NextColumn;
  2910. end;
  2911. procedure TDitherEngine.NextLine;
  2912. begin
  2913.   FDirection := -FDirection;
  2914.   if (FDirection = 1) then
  2915.     FColumn := 0
  2916.   else
  2917.     FColumn := Width-1;
  2918. end;
  2919. procedure TDitherEngine.NextColumn;
  2920. begin
  2921.   inc(FColumn, FDirection);
  2922. end;
  2923. ////////////////////////////////////////////////////////////////////////////////
  2924. // TFloydSteinbergDitherer
  2925. constructor TFloydSteinbergDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  2926. begin
  2927.   inherited Create(AWidth, Lookup);
  2928.   // The Error arrays has (columns + 2) entries; the extra entry at
  2929.   // each end saves us from special-casing the first and last pixels.
  2930.   // We can get away with a single array (holding one row's worth of errors)
  2931.   // by using it to store the current row's errors at pixel columns not yet
  2932.   // processed, but the next row's errors at columns already processed.  We
  2933.   // need only a few extra variables to hold the errors immediately around the
  2934.   // current column.  (If we are lucky, those variables are in registers, but
  2935.   // even if not, they're probably cheaper to access than array elements are.)
  2936.   GetMem(ErrorsR, sizeof(TErrorTerm)*(Width+2));
  2937.   GetMem(ErrorsG, sizeof(TErrorTerm)*(Width+2));
  2938.   GetMem(ErrorsB, sizeof(TErrorTerm)*(Width+2));
  2939.   FillChar(ErrorsR^, sizeof(TErrorTerm)*(Width+2), 0);
  2940.   FillChar(ErrorsG^, sizeof(TErrorTerm)*(Width+2), 0);
  2941.   FillChar(ErrorsB^, sizeof(TErrorTerm)*(Width+2), 0);
  2942.   ErrorR := ErrorsR;
  2943.   ErrorG := ErrorsG;
  2944.   ErrorB := ErrorsB;
  2945.   CurrentErrorR := 0;
  2946.   CurrentErrorG := CurrentErrorR;
  2947.   CurrentErrorB := CurrentErrorR;
  2948.   BelowErrorR := CurrentErrorR;
  2949.   BelowErrorG := CurrentErrorR;
  2950.   BelowErrorB := CurrentErrorR;
  2951.   BelowPrevErrorR := CurrentErrorR;
  2952.   BelowPrevErrorG := CurrentErrorR;
  2953.   BelowPrevErrorB := CurrentErrorR;
  2954. end;
  2955. destructor TFloydSteinbergDitherer.Destroy;
  2956. begin
  2957.   FreeMem(ErrorsR);
  2958.   FreeMem(ErrorsG);
  2959.   FreeMem(ErrorsB);
  2960.   inherited Destroy;
  2961. end;
  2962. {$IFOPT R+}
  2963.   {$DEFINE R_PLUS}
  2964.   {$RANGECHECKS OFF}
  2965. {$ENDIF}
  2966. function TFloydSteinbergDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  2967. var
  2968.   BelowNextError : TErrorTerm;
  2969.   Delta : TErrorTerm;
  2970. begin
  2971.   CurrentErrorR := Red + (CurrentErrorR + ErrorR[0] + 8) DIV 16;
  2972. //  CurrentErrorR := Red + (CurrentErrorR + ErrorR[Direction] + 8) DIV 16;
  2973.   if (CurrentErrorR < 0) then
  2974.     CurrentErrorR := 0
  2975.   else if (CurrentErrorR > 255) then
  2976.     CurrentErrorR := 255;
  2977.   CurrentErrorG := Green + (CurrentErrorG + ErrorG[0] + 8) DIV 16;
  2978. //  CurrentErrorG := Green + (CurrentErrorG + ErrorG[Direction] + 8) DIV 16;
  2979.   if (CurrentErrorG < 0) then
  2980.     CurrentErrorG := 0
  2981.   else if (CurrentErrorG > 255) then
  2982.     CurrentErrorG := 255;
  2983.   CurrentErrorB := Blue + (CurrentErrorB + ErrorB[0] + 8) DIV 16;
  2984. //  CurrentErrorB := Blue + (CurrentErrorB + ErrorB[Direction] + 8) DIV 16;
  2985.   if (CurrentErrorB < 0) then
  2986.     CurrentErrorB := 0
  2987.   else if (CurrentErrorB > 255) then
  2988.     CurrentErrorB := 255;
  2989.   // Map color to palette
  2990.   Result := inherited Dither(CurrentErrorR, CurrentErrorG, CurrentErrorB, R, G, B);
  2991.   // Propagate Floyd-Steinberg error terms.
  2992.   // Errors are accumulated into the error arrays, at a resolution of
  2993.   // 1/16th of a pixel count.  The error at a given pixel is propagated
  2994.   // to its not-yet-processed neighbors using the standard F-S fractions,
  2995.   // ... (here) 7/16
  2996.   // 3/16 5/16 1/16
  2997.   // We work left-to-right on even rows, right-to-left on odd rows.
  2998.   // Red component
  2999.   CurrentErrorR := CurrentErrorR - R;
  3000.   if (CurrentErrorR <> 0) then
  3001.   begin
  3002.     BelowNextError := CurrentErrorR; // Error * 1
  3003.     Delta := CurrentErrorR * 2;
  3004.     inc(CurrentErrorR, Delta);
  3005.     ErrorR[0] := BelowPrevErrorR + CurrentErrorR; // Error * 3
  3006.     inc(CurrentErrorR, Delta);
  3007.     BelowPrevErrorR := BelowErrorR + CurrentErrorR; // Error * 5
  3008.     BelowErrorR := BelowNextError; // Error * 1
  3009.     inc(CurrentErrorR, Delta); // Error * 7
  3010.   end;
  3011.   // Green component
  3012.   CurrentErrorG := CurrentErrorG - G;
  3013.   if (CurrentErrorG <> 0) then
  3014.   begin
  3015.     BelowNextError := CurrentErrorG; // Error * 1
  3016.     Delta := CurrentErrorG * 2;
  3017.     inc(CurrentErrorG, Delta);
  3018.     ErrorG[0] := BelowPrevErrorG + CurrentErrorG; // Error * 3
  3019.     inc(CurrentErrorG, Delta);
  3020.     BelowPrevErrorG := BelowErrorG + CurrentErrorG; // Error * 5
  3021.     BelowErrorG := BelowNextError; // Error * 1
  3022.     inc(CurrentErrorG, Delta); // Error * 7
  3023.   end;
  3024.   // Blue component
  3025.   CurrentErrorB := CurrentErrorB - B;
  3026.   if (CurrentErrorB <> 0) then
  3027.   begin
  3028.     BelowNextError := CurrentErrorB; // Error * 1
  3029.     Delta := CurrentErrorB * 2;
  3030.     inc(CurrentErrorB, Delta);
  3031.     ErrorB[0] := BelowPrevErrorB + CurrentErrorB; // Error * 3
  3032.     inc(CurrentErrorB, Delta);
  3033.     BelowPrevErrorB := BelowErrorB + CurrentErrorB; // Error * 5
  3034.     BelowErrorB := BelowNextError; // Error * 1
  3035.     inc(CurrentErrorB, Delta); // Error * 7
  3036.   end;
  3037.   // Move on to next column
  3038.   if (Direction = 1) then
  3039.   begin
  3040.     inc(longInt(ErrorR), sizeof(TErrorTerm));
  3041.     inc(longInt(ErrorG), sizeof(TErrorTerm));
  3042.     inc(longInt(ErrorB), sizeof(TErrorTerm));
  3043.   end else
  3044.   begin
  3045.     dec(longInt(ErrorR), sizeof(TErrorTerm));
  3046.     dec(longInt(ErrorG), sizeof(TErrorTerm));
  3047.     dec(longInt(ErrorB), sizeof(TErrorTerm));
  3048.   end;
  3049. end;
  3050. {$IFDEF R_PLUS}
  3051.   {$RANGECHECKS ON}
  3052.   {$UNDEF R_PLUS}
  3053. {$ENDIF}
  3054. {$IFOPT R+}
  3055.   {$DEFINE R_PLUS}
  3056.   {$RANGECHECKS OFF}
  3057. {$ENDIF}
  3058. procedure TFloydSteinbergDitherer.NextLine;
  3059. begin
  3060.   ErrorR[0] := BelowPrevErrorR;
  3061.   ErrorG[0] := BelowPrevErrorG;
  3062.   ErrorB[0] := BelowPrevErrorB;
  3063.   // Note: The optimizer produces better code for this construct:
  3064.   //   a := 0; b := a; c := a;
  3065.   // compared to this construct:
  3066.   //   a := 0; b := 0; c := 0;
  3067.   CurrentErrorR := 0;
  3068.   CurrentErrorG := CurrentErrorR;
  3069.   CurrentErrorB := CurrentErrorG;
  3070.   BelowErrorR := CurrentErrorG;
  3071.   BelowErrorG := CurrentErrorG;
  3072.   BelowErrorB := CurrentErrorG;
  3073.   BelowPrevErrorR := CurrentErrorG;
  3074.   BelowPrevErrorG := CurrentErrorG;
  3075.   BelowPrevErrorB := CurrentErrorG;
  3076.   inherited NextLine;
  3077.   if (Direction = 1) then
  3078.   begin
  3079.     ErrorR := ErrorsR;
  3080.     ErrorG := ErrorsG;
  3081.     ErrorB := ErrorsB;
  3082.   end else
  3083.   begin
  3084.     ErrorR := @ErrorsR[Width+1];
  3085.     ErrorG := @ErrorsG[Width+1];
  3086.     ErrorB := @ErrorsB[Width+1];
  3087.   end;
  3088. end;
  3089. {$IFDEF R_PLUS}
  3090.   {$RANGECHECKS ON}
  3091.   {$UNDEF R_PLUS}
  3092. {$ENDIF}
  3093. ////////////////////////////////////////////////////////////////////////////////
  3094. // T5by3Ditherer
  3095. constructor T5by3Ditherer.Create(AWidth: integer; Lookup: TColorLookup);
  3096. begin
  3097.   inherited Create(AWidth, Lookup);
  3098.   GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+4));
  3099.   GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+4));
  3100.   GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+4));
  3101.   GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+4));
  3102.   GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+4));
  3103.   GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+4));
  3104.   GetMem(ErrorsR2, sizeof(TErrorTerm)*(Width+4));
  3105.   GetMem(ErrorsG2, sizeof(TErrorTerm)*(Width+4));
  3106.   GetMem(ErrorsB2, sizeof(TErrorTerm)*(Width+4));
  3107.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3108.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3109.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3110.   FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+4), 0);
  3111.   FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+4), 0);
  3112.   FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+4), 0);
  3113.   FillChar(ErrorsR2^, sizeof(TErrorTerm)*(Width+4), 0);
  3114.   FillChar(ErrorsG2^, sizeof(TErrorTerm)*(Width+4), 0);
  3115.   FillChar(ErrorsB2^, sizeof(TErrorTerm)*(Width+4), 0);
  3116.   FDivisor := 1;
  3117.   FDirection2 := 2 * Direction;
  3118.   ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3119.   ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3120.   ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3121.   ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3122.   ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3123.   ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3124.   ErrorR2 := PErrors(longInt(ErrorsR2)+2*sizeof(TErrorTerm));
  3125.   ErrorG2 := PErrors(longInt(ErrorsG2)+2*sizeof(TErrorTerm));
  3126.   ErrorB2 := PErrors(longInt(ErrorsB2)+2*sizeof(TErrorTerm));
  3127. end;
  3128. destructor T5by3Ditherer.Destroy;
  3129. begin
  3130.   FreeMem(ErrorsR0);
  3131.   FreeMem(ErrorsG0);
  3132.   FreeMem(ErrorsB0);
  3133.   FreeMem(ErrorsR1);
  3134.   FreeMem(ErrorsG1);
  3135.   FreeMem(ErrorsB1);
  3136.   FreeMem(ErrorsR2);
  3137.   FreeMem(ErrorsG2);
  3138.   FreeMem(ErrorsB2);
  3139.   inherited Destroy;
  3140. end;
  3141. {$IFOPT R+}
  3142.   {$DEFINE R_PLUS}
  3143.   {$RANGECHECKS OFF}
  3144. {$ENDIF}
  3145. function T5by3Ditherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3146. var
  3147.   ColorR ,
  3148.   ColorG ,
  3149.   ColorB : integer; // Error for current pixel
  3150. begin
  3151.   // Apply red component error correction
  3152.   ColorR := Red + (ErrorR0[0] + FDivisor DIV 2) DIV FDivisor;
  3153.   if (ColorR < 0) then
  3154.     ColorR := 0
  3155.   else if (ColorR > 255) then
  3156.     ColorR := 255;
  3157.   // Apply green component error correction
  3158.   ColorG := Green + (ErrorG0[0] + FDivisor DIV 2) DIV FDivisor;
  3159.   if (ColorG < 0) then
  3160.     ColorG := 0
  3161.   else if (ColorG > 255) then
  3162.     ColorG := 255;
  3163.   // Apply blue component error correction
  3164.   ColorB := Blue + (ErrorB0[0] + FDivisor DIV 2) DIV FDivisor;
  3165.   if (ColorB < 0) then
  3166.     ColorB := 0
  3167.   else if (ColorB > 255) then
  3168.     ColorB := 255;
  3169.   // Map color to palette
  3170.   Result := inherited Dither(ColorR, ColorG, ColorB, R, G, B);
  3171.   // Propagate red component error
  3172.   Propagate(ErrorR0, ErrorR1, ErrorR2, ColorR - R);
  3173.   // Propagate green component error
  3174.   Propagate(ErrorG0, ErrorG1, ErrorG2, ColorG - G);
  3175.   // Propagate blue component error
  3176.   Propagate(ErrorB0, ErrorB1, ErrorB2, ColorB - B);
  3177.   // Move on to next column
  3178.   if (Direction = 1) then
  3179.   begin
  3180.     inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3181.     inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3182.     inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3183.     inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3184.     inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3185.     inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3186.     inc(longInt(ErrorR2), sizeof(TErrorTerm));
  3187.     inc(longInt(ErrorG2), sizeof(TErrorTerm));
  3188.     inc(longInt(ErrorB2), sizeof(TErrorTerm));
  3189.   end else
  3190.   begin
  3191.     dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3192.     dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3193.     dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3194.     dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3195.     dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3196.     dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3197.     dec(longInt(ErrorR2), sizeof(TErrorTerm));
  3198.     dec(longInt(ErrorG2), sizeof(TErrorTerm));
  3199.     dec(longInt(ErrorB2), sizeof(TErrorTerm));
  3200.   end;
  3201. end;
  3202. {$IFDEF R_PLUS}
  3203.   {$RANGECHECKS ON}
  3204.   {$UNDEF R_PLUS}
  3205. {$ENDIF}
  3206. {$IFOPT R+}
  3207.   {$DEFINE R_PLUS}
  3208.   {$RANGECHECKS OFF}
  3209. {$ENDIF}
  3210. procedure T5by3Ditherer.NextLine;
  3211. var
  3212.   TempErrors : PErrors;
  3213. begin
  3214.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3215.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3216.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3217.   // Swap lines
  3218.   TempErrors := ErrorsR0;
  3219.   ErrorsR0 := ErrorsR1;
  3220.   ErrorsR1 := ErrorsR2;
  3221.   ErrorsR2 := TempErrors;
  3222.   TempErrors := ErrorsG0;
  3223.   ErrorsG0 := ErrorsG1;
  3224.   ErrorsG1 := ErrorsG2;
  3225.   ErrorsG2 := TempErrors;
  3226.   TempErrors := ErrorsB0;
  3227.   ErrorsB0 := ErrorsB1;
  3228.   ErrorsB1 := ErrorsB2;
  3229.   ErrorsB2 := TempErrors;
  3230.   inherited NextLine;
  3231.   FDirection2 := 2 * Direction;
  3232.   if (Direction = 1) then
  3233.   begin
  3234.     // ErrorsR0[1] gives compiler error, so we
  3235.     // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3236.     ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3237.     ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3238.     ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3239.     ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3240.     ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3241.     ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3242.     ErrorR2 := PErrors(longInt(ErrorsR2)+2*sizeof(TErrorTerm));
  3243.     ErrorG2 := PErrors(longInt(ErrorsG2)+2*sizeof(TErrorTerm));
  3244.     ErrorB2 := PErrors(longInt(ErrorsB2)+2*sizeof(TErrorTerm));
  3245.   end else
  3246.   begin
  3247.     ErrorR0 := @ErrorsR0[Width+1];
  3248.     ErrorG0 := @ErrorsG0[Width+1];
  3249.     ErrorB0 := @ErrorsB0[Width+1];
  3250.     ErrorR1 := @ErrorsR1[Width+1];
  3251.     ErrorG1 := @ErrorsG1[Width+1];
  3252.     ErrorB1 := @ErrorsB1[Width+1];
  3253.     ErrorR2 := @ErrorsR2[Width+1];
  3254.     ErrorG2 := @ErrorsG2[Width+1];
  3255.     ErrorB2 := @ErrorsB2[Width+1];
  3256.   end;
  3257. end;
  3258. {$IFDEF R_PLUS}
  3259.   {$RANGECHECKS ON}
  3260.   {$UNDEF R_PLUS}
  3261. {$ENDIF}
  3262. ////////////////////////////////////////////////////////////////////////////////
  3263. // TStuckiDitherer
  3264. constructor TStuckiDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3265. begin
  3266.   inherited Create(AWidth, Lookup);
  3267.   FDivisor := 42;
  3268. end;
  3269. {$IFOPT R+}
  3270.   {$DEFINE R_PLUS}
  3271.   {$RANGECHECKS OFF}
  3272. {$ENDIF}
  3273. procedure TStuckiDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3274. begin
  3275.   if (Error = 0) then
  3276.     exit;
  3277.   // Propagate Stucki error terms:
  3278.   // ... ... (here) 8/42 4/42
  3279.   // 2/42 4/42 8/42 4/42 2/42
  3280.   // 1/42 2/42 4/42 2/42 1/42
  3281.   inc(Errors2[FDirection2], Error); // Error * 1
  3282.   inc(Errors2[-FDirection2], Error); // Error * 1
  3283.   Error := Error + Error;
  3284.   inc(Errors1[FDirection2], Error); // Error * 2
  3285.   inc(Errors1[-FDirection2], Error); // Error * 2
  3286.   inc(Errors2[Direction], Error); // Error * 2
  3287.   inc(Errors2[-Direction], Error); // Error * 2
  3288.   Error := Error + Error;
  3289.   inc(Errors0[FDirection2], Error); // Error * 4
  3290.   inc(Errors1[-Direction], Error); // Error * 4
  3291.   inc(Errors1[Direction], Error); // Error * 4
  3292.   inc(Errors2[0], Error); // Error * 4
  3293.   Error := Error + Error;
  3294.   inc(Errors0[Direction], Error); // Error * 8
  3295.   inc(Errors1[0], Error); // Error * 8
  3296. end;
  3297. {$IFDEF R_PLUS}
  3298.   {$RANGECHECKS ON}
  3299.   {$UNDEF R_PLUS}
  3300. {$ENDIF}
  3301. ////////////////////////////////////////////////////////////////////////////////
  3302. // TSierraDitherer
  3303. constructor TSierraDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3304. begin
  3305.   inherited Create(AWidth, Lookup);
  3306.   FDivisor := 32;
  3307. end;
  3308. {$IFOPT R+}
  3309.   {$DEFINE R_PLUS}
  3310.   {$RANGECHECKS OFF}
  3311. {$ENDIF}
  3312. procedure TSierraDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3313. var
  3314.   TempError : integer;
  3315. begin
  3316.   if (Error = 0) then
  3317.     exit;
  3318.   // Propagate Sierra error terms:
  3319.   // ... ... (here) 5/32 3/32
  3320.   // 2/32 4/32 5/32 4/32 2/32
  3321.   // ... 2/32 3/32 2/32 ...
  3322.   TempError := Error + Error;
  3323.   inc(Errors1[FDirection2], TempError); // Error * 2
  3324.   inc(Errors1[-FDirection2], TempError);// Error * 2
  3325.   inc(Errors2[Direction], TempError); // Error * 2
  3326.   inc(Errors2[-Direction], TempError); // Error * 2
  3327.   inc(TempError, Error);
  3328.   inc(Errors0[FDirection2], TempError); // Error * 3
  3329.   inc(Errors2[0], TempError); // Error * 3
  3330.   inc(TempError, Error);
  3331.   inc(Errors1[-Direction], TempError); // Error * 4
  3332.   inc(Errors1[Direction], TempError); // Error * 4
  3333.   inc(TempError, Error);
  3334.   inc(Errors0[Direction], TempError); // Error * 5
  3335.   inc(Errors1[0], TempError); // Error * 5
  3336. end;
  3337. {$IFDEF R_PLUS}
  3338.   {$RANGECHECKS ON}
  3339.   {$UNDEF R_PLUS}
  3340. {$ENDIF}
  3341. ////////////////////////////////////////////////////////////////////////////////
  3342. // TJaJuNiDitherer
  3343. constructor TJaJuNiDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3344. begin
  3345.   inherited Create(AWidth, Lookup);
  3346.   FDivisor := 38;
  3347. end;
  3348. {$IFOPT R+}
  3349.   {$DEFINE R_PLUS}
  3350.   {$RANGECHECKS OFF}
  3351. {$ENDIF}
  3352. procedure TJaJuNiDitherer.Propagate(Errors0, Errors1, Errors2: PErrors; Error: integer);
  3353. var
  3354.   TempError : integer;
  3355. begin
  3356.   if (Error = 0) then
  3357.     exit;
  3358.   // Propagate Jarvis, Judice and Ninke error terms:
  3359.   // ... ... (here) 8/38 4/38
  3360.   // 2/38 4/38 8/38 4/38 2/38
  3361.   // 1/38 2/38 4/38 2/38 1/38
  3362.   inc(Errors2[FDirection2], Error); // Error * 1
  3363.   inc(Errors2[-FDirection2], Error); // Error * 1
  3364.   TempError := Error + Error;
  3365.   inc(Error, TempError);
  3366.   inc(Errors1[FDirection2], Error); // Error * 3
  3367.   inc(Errors1[-FDirection2], Error); // Error * 3
  3368.   inc(Errors2[Direction], Error); // Error * 3
  3369.   inc(Errors2[-Direction], Error); // Error * 3
  3370.   inc(Error, TempError);
  3371.   inc(Errors0[FDirection2], Error); // Error * 5
  3372.   inc(Errors1[-Direction], Error); // Error * 5
  3373.   inc(Errors1[Direction], Error); // Error * 5
  3374.   inc(Errors2[0], Error); // Error * 5
  3375.   inc(Error, TempError);
  3376.   inc(Errors0[Direction], Error); // Error * 7
  3377.   inc(Errors1[0], Error); // Error * 7
  3378. end;
  3379. {$IFDEF R_PLUS}
  3380.   {$RANGECHECKS ON}
  3381.   {$UNDEF R_PLUS}
  3382. {$ENDIF}
  3383. ////////////////////////////////////////////////////////////////////////////////
  3384. // TSteveArcheDitherer
  3385. constructor TSteveArcheDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3386. begin
  3387.   inherited Create(AWidth, Lookup);
  3388.   GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+6));
  3389.   GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+6));
  3390.   GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+6));
  3391.   GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+6));
  3392.   GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+6));
  3393.   GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+6));
  3394.   GetMem(ErrorsR2, sizeof(TErrorTerm)*(Width+6));
  3395.   GetMem(ErrorsG2, sizeof(TErrorTerm)*(Width+6));
  3396.   GetMem(ErrorsB2, sizeof(TErrorTerm)*(Width+6));
  3397.   GetMem(ErrorsR3, sizeof(TErrorTerm)*(Width+6));
  3398.   GetMem(ErrorsG3, sizeof(TErrorTerm)*(Width+6));
  3399.   GetMem(ErrorsB3, sizeof(TErrorTerm)*(Width+6));
  3400.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+6), 0);
  3401.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+6), 0);
  3402.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+6), 0);
  3403.   FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+6), 0);
  3404.   FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+6), 0);
  3405.   FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+6), 0);
  3406.   FillChar(ErrorsR2^, sizeof(TErrorTerm)*(Width+6), 0);
  3407.   FillChar(ErrorsG2^, sizeof(TErrorTerm)*(Width+6), 0);
  3408.   FillChar(ErrorsB2^, sizeof(TErrorTerm)*(Width+6), 0);
  3409.   FillChar(ErrorsR3^, sizeof(TErrorTerm)*(Width+6), 0);
  3410.   FillChar(ErrorsG3^, sizeof(TErrorTerm)*(Width+6), 0);
  3411.   FillChar(ErrorsB3^, sizeof(TErrorTerm)*(Width+6), 0);
  3412.   FDirection2 := 2 * Direction;
  3413.   FDirection3 := 3 * Direction;
  3414.   ErrorR0 := PErrors(longInt(ErrorsR0)+3*sizeof(TErrorTerm));
  3415.   ErrorG0 := PErrors(longInt(ErrorsG0)+3*sizeof(TErrorTerm));
  3416.   ErrorB0 := PErrors(longInt(ErrorsB0)+3*sizeof(TErrorTerm));
  3417.   ErrorR1 := PErrors(longInt(ErrorsR1)+3*sizeof(TErrorTerm));
  3418.   ErrorG1 := PErrors(longInt(ErrorsG1)+3*sizeof(TErrorTerm));
  3419.   ErrorB1 := PErrors(longInt(ErrorsB1)+3*sizeof(TErrorTerm));
  3420.   ErrorR2 := PErrors(longInt(ErrorsR2)+3*sizeof(TErrorTerm));
  3421.   ErrorG2 := PErrors(longInt(ErrorsG2)+3*sizeof(TErrorTerm));
  3422.   ErrorB2 := PErrors(longInt(ErrorsB2)+3*sizeof(TErrorTerm));
  3423.   ErrorR3 := PErrors(longInt(ErrorsR3)+3*sizeof(TErrorTerm));
  3424.   ErrorG3 := PErrors(longInt(ErrorsG3)+3*sizeof(TErrorTerm));
  3425.   ErrorB3 := PErrors(longInt(ErrorsB3)+3*sizeof(TErrorTerm));
  3426. end;
  3427. destructor TSteveArcheDitherer.Destroy;
  3428. begin
  3429.   FreeMem(ErrorsR0);
  3430.   FreeMem(ErrorsG0);
  3431.   FreeMem(ErrorsB0);
  3432.   FreeMem(ErrorsR1);
  3433.   FreeMem(ErrorsG1);
  3434.   FreeMem(ErrorsB1);
  3435.   FreeMem(ErrorsR2);
  3436.   FreeMem(ErrorsG2);
  3437.   FreeMem(ErrorsB2);
  3438.   FreeMem(ErrorsR3);
  3439.   FreeMem(ErrorsG3);
  3440.   FreeMem(ErrorsB3);
  3441.   inherited Destroy;
  3442. end;
  3443. {$IFOPT R+}
  3444.   {$DEFINE R_PLUS}
  3445.   {$RANGECHECKS OFF}
  3446. {$ENDIF}
  3447. function TSteveArcheDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3448. var
  3449.   ColorR ,
  3450.   ColorG ,
  3451.   ColorB : integer; // Error for current pixel
  3452.   // Propagate Stevenson & Arche error terms:
  3453.   // ... ... ... (here) ... 32/200 ...
  3454.   //    12/200 ... 26/200 ... 30/200 ... 16/200
  3455.   // ... 12/200 ... 26/200 ... 12/200 ...
  3456.   // 5/200 ... 12/200 ... 12/200 ... 5/200
  3457.   procedure Propagate(Errors0, Errors1, Errors2, Errors3: PErrors; Error: integer);
  3458.   var
  3459.     TempError : integer;
  3460.   begin
  3461.     if (Error = 0) then
  3462.       exit;
  3463.     TempError := 5 * Error;
  3464.     inc(Errors3[FDirection3], TempError); // Error * 5
  3465.     inc(Errors3[-FDirection3], TempError); // Error * 5
  3466.     TempError := 12 * Error;
  3467.     inc(Errors1[-FDirection3], TempError); // Error * 12
  3468.     inc(Errors2[-FDirection2], TempError); // Error * 12
  3469.     inc(Errors2[FDirection2], TempError); // Error * 12
  3470.     inc(Errors3[-Direction], TempError); // Error * 12
  3471.     inc(Errors3[Direction], TempError); // Error * 12
  3472.     inc(Errors1[FDirection3], 16 * TempError); // Error * 16
  3473.     TempError := 26 * Error;
  3474.     inc(Errors1[-Direction], TempError); // Error * 26
  3475.     inc(Errors2[0], TempError); // Error * 26
  3476.     inc(Errors1[Direction], 30 * Error); // Error * 30
  3477.     inc(Errors0[FDirection2], 32 * Error); // Error * 32
  3478.   end;
  3479. begin
  3480.   // Apply red component error correction
  3481.   ColorR := Red + (ErrorR0[0] + 100) DIV 200;
  3482.   if (ColorR < 0) then
  3483.     ColorR := 0
  3484.   else if (ColorR > 255) then
  3485.     ColorR := 255;
  3486.   // Apply green component error correction
  3487.   ColorG := Green + (ErrorG0[0] + 100) DIV 200;
  3488.   if (ColorG < 0) then
  3489.     ColorG := 0
  3490.   else if (ColorG > 255) then
  3491.     ColorG := 255;
  3492.   // Apply blue component error correction
  3493.   ColorB := Blue + (ErrorB0[0] + 100) DIV 200;
  3494.   if (ColorB < 0) then
  3495.     ColorB := 0
  3496.   else if (ColorB > 255) then
  3497.     ColorB := 255;
  3498.   // Map color to palette
  3499.   Result := inherited Dither(ColorR, ColorG, ColorB, R, G, B);
  3500.   // Propagate red component error
  3501.   Propagate(ErrorR0, ErrorR1, ErrorR2, ErrorR3, ColorR - R);
  3502.   // Propagate green component error
  3503.   Propagate(ErrorG0, ErrorG1, ErrorG2, ErrorG3, ColorG - G);
  3504.   // Propagate blue component error
  3505.   Propagate(ErrorB0, ErrorB1, ErrorB2, ErrorB3, ColorB - B);
  3506.   // Move on to next column
  3507.   if (Direction = 1) then
  3508.   begin
  3509.     inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3510.     inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3511.     inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3512.     inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3513.     inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3514.     inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3515.     inc(longInt(ErrorR2), sizeof(TErrorTerm));
  3516.     inc(longInt(ErrorG2), sizeof(TErrorTerm));
  3517.     inc(longInt(ErrorB2), sizeof(TErrorTerm));
  3518.     inc(longInt(ErrorR3), sizeof(TErrorTerm));
  3519.     inc(longInt(ErrorG3), sizeof(TErrorTerm));
  3520.     inc(longInt(ErrorB3), sizeof(TErrorTerm));
  3521.   end else
  3522.   begin
  3523.     dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3524.     dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3525.     dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3526.     dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3527.     dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3528.     dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3529.     dec(longInt(ErrorR2), sizeof(TErrorTerm));
  3530.     dec(longInt(ErrorG2), sizeof(TErrorTerm));
  3531.     dec(longInt(ErrorB2), sizeof(TErrorTerm));
  3532.     dec(longInt(ErrorR3), sizeof(TErrorTerm));
  3533.     dec(longInt(ErrorG3), sizeof(TErrorTerm));
  3534.     dec(longInt(ErrorB3), sizeof(TErrorTerm));
  3535.   end;
  3536. end;
  3537. {$IFDEF R_PLUS}
  3538.   {$RANGECHECKS ON}
  3539.   {$UNDEF R_PLUS}
  3540. {$ENDIF}
  3541. {$IFOPT R+}
  3542.   {$DEFINE R_PLUS}
  3543.   {$RANGECHECKS OFF}
  3544. {$ENDIF}
  3545. procedure TSteveArcheDitherer.NextLine;
  3546. var
  3547.   TempErrors : PErrors;
  3548. begin
  3549.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+6), 0);
  3550.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+6), 0);
  3551.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+6), 0);
  3552.   // Swap lines
  3553.   TempErrors := ErrorsR0;
  3554.   ErrorsR0 := ErrorsR1;
  3555.   ErrorsR1 := ErrorsR2;
  3556.   ErrorsR2 := ErrorsR3;
  3557.   ErrorsR3 := TempErrors;
  3558.   TempErrors := ErrorsG0;
  3559.   ErrorsG0 := ErrorsG1;
  3560.   ErrorsG1 := ErrorsG2;
  3561.   ErrorsG2 := ErrorsG3;
  3562.   ErrorsG3 := TempErrors;
  3563.   TempErrors := ErrorsB0;
  3564.   ErrorsB0 := ErrorsB1;
  3565.   ErrorsB1 := ErrorsB2;
  3566.   ErrorsB2 := ErrorsB3;
  3567.   ErrorsB3 := TempErrors;
  3568.   inherited NextLine;
  3569.   FDirection2 := 2 * Direction;
  3570.   FDirection3 := 3 * Direction;
  3571.   if (Direction = 1) then
  3572.   begin
  3573.     // ErrorsR0[1] gives compiler error, so we
  3574.     // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3575.     ErrorR0 := PErrors(longInt(ErrorsR0)+3*sizeof(TErrorTerm));
  3576.     ErrorG0 := PErrors(longInt(ErrorsG0)+3*sizeof(TErrorTerm));
  3577.     ErrorB0 := PErrors(longInt(ErrorsB0)+3*sizeof(TErrorTerm));
  3578.     ErrorR1 := PErrors(longInt(ErrorsR1)+3*sizeof(TErrorTerm));
  3579.     ErrorG1 := PErrors(longInt(ErrorsG1)+3*sizeof(TErrorTerm));
  3580.     ErrorB1 := PErrors(longInt(ErrorsB1)+3*sizeof(TErrorTerm));
  3581.     ErrorR2 := PErrors(longInt(ErrorsR2)+3*sizeof(TErrorTerm));
  3582.     ErrorG2 := PErrors(longInt(ErrorsG2)+3*sizeof(TErrorTerm));
  3583.     ErrorB2 := PErrors(longInt(ErrorsB2)+3*sizeof(TErrorTerm));
  3584.     ErrorR3 := PErrors(longInt(ErrorsR3)+3*sizeof(TErrorTerm));
  3585.     ErrorG3 := PErrors(longInt(ErrorsG3)+3*sizeof(TErrorTerm));
  3586.     ErrorB3 := PErrors(longInt(ErrorsB3)+3*sizeof(TErrorTerm));
  3587.   end else
  3588.   begin
  3589.     ErrorR0 := @ErrorsR0[Width+2];
  3590.     ErrorG0 := @ErrorsG0[Width+2];
  3591.     ErrorB0 := @ErrorsB0[Width+2];
  3592.     ErrorR1 := @ErrorsR1[Width+2];
  3593.     ErrorG1 := @ErrorsG1[Width+2];
  3594.     ErrorB1 := @ErrorsB1[Width+2];
  3595.     ErrorR2 := @ErrorsR2[Width+2];
  3596.     ErrorG2 := @ErrorsG2[Width+2];
  3597.     ErrorB2 := @ErrorsB2[Width+2];
  3598.     ErrorR3 := @ErrorsR2[Width+2];
  3599.     ErrorG3 := @ErrorsG2[Width+2];
  3600.     ErrorB3 := @ErrorsB2[Width+2];
  3601.   end;
  3602. end;
  3603. {$IFDEF R_PLUS}
  3604.   {$RANGECHECKS ON}
  3605.   {$UNDEF R_PLUS}
  3606. {$ENDIF}
  3607. ////////////////////////////////////////////////////////////////////////////////
  3608. // TBurkesDitherer
  3609. constructor TBurkesDitherer.Create(AWidth: integer; Lookup: TColorLookup);
  3610. begin
  3611.   inherited Create(AWidth, Lookup);
  3612.   GetMem(ErrorsR0, sizeof(TErrorTerm)*(Width+4));
  3613.   GetMem(ErrorsG0, sizeof(TErrorTerm)*(Width+4));
  3614.   GetMem(ErrorsB0, sizeof(TErrorTerm)*(Width+4));
  3615.   GetMem(ErrorsR1, sizeof(TErrorTerm)*(Width+4));
  3616.   GetMem(ErrorsG1, sizeof(TErrorTerm)*(Width+4));
  3617.   GetMem(ErrorsB1, sizeof(TErrorTerm)*(Width+4));
  3618.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3619.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3620.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3621.   FillChar(ErrorsR1^, sizeof(TErrorTerm)*(Width+4), 0);
  3622.   FillChar(ErrorsG1^, sizeof(TErrorTerm)*(Width+4), 0);
  3623.   FillChar(ErrorsB1^, sizeof(TErrorTerm)*(Width+4), 0);
  3624.   FDirection2 := 2 * Direction;
  3625.   ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3626.   ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3627.   ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3628.   ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3629.   ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3630.   ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3631. end;
  3632. destructor TBurkesDitherer.Destroy;
  3633. begin
  3634.   FreeMem(ErrorsR0);
  3635.   FreeMem(ErrorsG0);
  3636.   FreeMem(ErrorsB0);
  3637.   FreeMem(ErrorsR1);
  3638.   FreeMem(ErrorsG1);
  3639.   FreeMem(ErrorsB1);
  3640.   inherited Destroy;
  3641. end;
  3642. {$IFOPT R+}
  3643.   {$DEFINE R_PLUS}
  3644.   {$RANGECHECKS OFF}
  3645. {$ENDIF}
  3646. function TBurkesDitherer.Dither(Red, Green, Blue: BYTE; var R, G, B: BYTE): char;
  3647. var
  3648.   ErrorR ,
  3649.   ErrorG ,
  3650.   ErrorB : integer; // Error for current pixel
  3651.   // Propagate Burkes error terms:
  3652.   // ... ... (here) 8/32 4/32
  3653.   // 2/32 4/32 8/32 4/32 2/32
  3654.   procedure Propagate(Errors0, Errors1: PErrors; Error: integer);
  3655.   begin
  3656.     if (Error = 0) then
  3657.       exit;
  3658.     inc(Error, Error);
  3659.     inc(Errors1[FDirection2], Error); // Error * 2
  3660.     inc(Errors1[-FDirection2], Error); // Error * 2
  3661.     inc(Error, Error);
  3662.     inc(Errors0[FDirection2], Error); // Error * 4
  3663.     inc(Errors1[-Direction], Error); // Error * 4
  3664.     inc(Errors1[Direction], Error); // Error * 4
  3665.     inc(Error, Error);
  3666.     inc(Errors0[Direction], Error); // Error * 8
  3667.     inc(Errors1[0], Error); // Error * 8
  3668.   end;
  3669. begin
  3670.   // Apply red component error correction
  3671.   ErrorR := Red + (ErrorR0[0] + 16) DIV 32;
  3672.   if (ErrorR < 0) then
  3673.     ErrorR := 0
  3674.   else if (ErrorR > 255) then
  3675.     ErrorR := 255;
  3676.   // Apply green component error correction
  3677.   ErrorG := Green + (ErrorG0[0] + 16) DIV 32;
  3678.   if (ErrorG < 0) then
  3679.     ErrorG := 0
  3680.   else if (ErrorG > 255) then
  3681.     ErrorG := 255;
  3682.   // Apply blue component error correction
  3683.   ErrorB := Blue + (ErrorB0[0] + 16) DIV 32;
  3684.   if (ErrorB < 0) then
  3685.     ErrorB := 0
  3686.   else if (ErrorB > 255) then
  3687.     ErrorB := 255;
  3688.   // Map color to palette
  3689.   Result := inherited Dither(ErrorR, ErrorG, ErrorB, R, G, B);
  3690.   // Propagate red component error
  3691.   Propagate(ErrorR0, ErrorR1, ErrorR - R);
  3692.   // Propagate green component error
  3693.   Propagate(ErrorG0, ErrorG1, ErrorG - G);
  3694.   // Propagate blue component error
  3695.   Propagate(ErrorB0, ErrorB1, ErrorB - B);
  3696.   // Move on to next column
  3697.   if (Direction = 1) then
  3698.   begin
  3699.     inc(longInt(ErrorR0), sizeof(TErrorTerm));
  3700.     inc(longInt(ErrorG0), sizeof(TErrorTerm));
  3701.     inc(longInt(ErrorB0), sizeof(TErrorTerm));
  3702.     inc(longInt(ErrorR1), sizeof(TErrorTerm));
  3703.     inc(longInt(ErrorG1), sizeof(TErrorTerm));
  3704.     inc(longInt(ErrorB1), sizeof(TErrorTerm));
  3705.   end else
  3706.   begin
  3707.     dec(longInt(ErrorR0), sizeof(TErrorTerm));
  3708.     dec(longInt(ErrorG0), sizeof(TErrorTerm));
  3709.     dec(longInt(ErrorB0), sizeof(TErrorTerm));
  3710.     dec(longInt(ErrorR1), sizeof(TErrorTerm));
  3711.     dec(longInt(ErrorG1), sizeof(TErrorTerm));
  3712.     dec(longInt(ErrorB1), sizeof(TErrorTerm));
  3713.   end;
  3714. end;
  3715. {$IFDEF R_PLUS}
  3716.   {$RANGECHECKS ON}
  3717.   {$UNDEF R_PLUS}
  3718. {$ENDIF}
  3719. {$IFOPT R+}
  3720.   {$DEFINE R_PLUS}
  3721.   {$RANGECHECKS OFF}
  3722. {$ENDIF}
  3723. procedure TBurkesDitherer.NextLine;
  3724. var
  3725.   TempErrors : PErrors;
  3726. begin
  3727.   FillChar(ErrorsR0^, sizeof(TErrorTerm)*(Width+4), 0);
  3728.   FillChar(ErrorsG0^, sizeof(TErrorTerm)*(Width+4), 0);
  3729.   FillChar(ErrorsB0^, sizeof(TErrorTerm)*(Width+4), 0);
  3730.   // Swap lines
  3731.   TempErrors := ErrorsR0;
  3732.   ErrorsR0 := ErrorsR1;
  3733.   ErrorsR1 := TempErrors;
  3734.   TempErrors := ErrorsG0;
  3735.   ErrorsG0 := ErrorsG1;
  3736.   ErrorsG1 := TempErrors;
  3737.   TempErrors := ErrorsB0;
  3738.   ErrorsB0 := ErrorsB1;
  3739.   ErrorsB1 := TempErrors;
  3740.   inherited NextLine;
  3741.   FDirection2 := 2 * Direction;
  3742.   if (Direction = 1) then
  3743.   begin
  3744.     // ErrorsR0[1] gives compiler error, so we
  3745.     // use PErrors(longInt(ErrorsR0)+sizeof(TErrorTerm)) instead...
  3746.     ErrorR0 := PErrors(longInt(ErrorsR0)+2*sizeof(TErrorTerm));
  3747.     ErrorG0 := PErrors(longInt(ErrorsG0)+2*sizeof(TErrorTerm));
  3748.     ErrorB0 := PErrors(longInt(ErrorsB0)+2*sizeof(TErrorTerm));
  3749.     ErrorR1 := PErrors(longInt(ErrorsR1)+2*sizeof(TErrorTerm));
  3750.     ErrorG1 := PErrors(longInt(ErrorsG1)+2*sizeof(TErrorTerm));
  3751.     ErrorB1 := PErrors(longInt(ErrorsB1)+2*sizeof(TErrorTerm));
  3752.   end else
  3753.   begin
  3754.     ErrorR0 := @ErrorsR0[Width+1];
  3755.     ErrorG0 := @ErrorsG0[Width+1];
  3756.     ErrorB0 := @ErrorsB0[Width+1];
  3757.     ErrorR1 := @ErrorsR1[Width+1];
  3758.     ErrorG1 := @ErrorsG1[Width+1];
  3759.     ErrorB1 := @ErrorsB1[Width+1];
  3760.   end;
  3761. end;
  3762. {$IFDEF R_PLUS}
  3763.   {$RANGECHECKS ON}
  3764.   {$UNDEF R_PLUS}
  3765. {$ENDIF}
  3766. ////////////////////////////////////////////////////////////////////////////////
  3767. //
  3768. // Octree Color Quantization Engine
  3769. //
  3770. ////////////////////////////////////////////////////////////////////////////////
  3771. //  Adapted from Earl F. Glynn's ColorQuantizationLibrary, March 1998
  3772. ////////////////////////////////////////////////////////////////////////////////
  3773. type
  3774.   TOctreeNode = class; // Forward definition so TReducibleNodes can be declared
  3775.   TReducibleNodes = array[0..7] of TOctreeNode;
  3776.   TOctreeNode = Class(TObject)
  3777.   public
  3778.     IsLeaf : Boolean;
  3779.     PixelCount : integer;
  3780.     RedSum : integer;
  3781.     GreenSum : integer;
  3782.     BlueSum : integer;
  3783.     Next : TOctreeNode;
  3784.     Child : TReducibleNodes;
  3785.     constructor Create(Level: integer; ColorBits: integer; var LeafCount: integer;
  3786.       var ReducibleNodes: TReducibleNodes);
  3787.     destructor Destroy; override;
  3788.   end;
  3789.   TColorQuantizer = class(TObject)
  3790.   private
  3791.     FTree : TOctreeNode;
  3792.     FLeafCount : integer;
  3793.     FReducibleNodes : TReducibleNodes;
  3794.     FMaxColors : integer;
  3795.     FColorBits : integer;
  3796.   protected
  3797.     procedure AddColor(var Node: TOctreeNode; r, g, b: byte; ColorBits: integer;
  3798.       Level: integer; var LeafCount: integer; var ReducibleNodes: TReducibleNodes);
  3799.     procedure DeleteTree(var Node: TOctreeNode);
  3800.     procedure GetPaletteColors(const Node: TOctreeNode;
  3801.       var RGBQuadArray: TRGBQuadArray; var Index: integer);
  3802.     procedure ReduceTree(ColorBits: integer; var LeafCount: integer;
  3803.       var ReducibleNodes: TReducibleNodes);
  3804.   public
  3805.     constructor Create(MaxColors: integer; ColorBits: integer);
  3806.     destructor Destroy; override;
  3807.     procedure GetColorTable(var RGBQuadArray: TRGBQuadArray);
  3808.     function ProcessImage(const DIB: TDIBReader): boolean;
  3809.     property ColorCount: integer read FLeafCount;
  3810.   end;
  3811. constructor TOctreeNode.Create(Level: integer; ColorBits: integer;
  3812.   var LeafCount: integer; var ReducibleNodes: TReducibleNodes);
  3813. var
  3814.   i : integer;
  3815. begin
  3816.   PixelCount := 0;
  3817.   RedSum := 0;
  3818.   GreenSum := 0;
  3819.   BlueSum := 0;
  3820.   for i := Low(Child) to High(Child) do
  3821.     Child[i] := nil;