Ignore:
Timestamp:
Dec 22, 2016, 8:49:19 PM (7 years ago)
Author:
chronos
Message:
  • Modified: Updated BGRABitmap package.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/Packages/bgrabitmap/bgrabitmaptypes.pas

    r472 r494  
    77
    88       --> Include BGRABitmap and BGRABitmapTypes in the 'uses' clause.
     9               If you are using LCL types, add also BGRAGraphics unit.
    910
    1011 ****************************************************************************
     
    2627
    2728{$mode objfpc}{$H+}
     29{$i bgrabitmap.inc}
    2830
    2931interface
    3032
    3133uses
    32   Classes, Types, Graphics, FPImage, FPImgCanv, GraphType;
     34  Classes, Types, BGRAGraphics,
     35  FPImage, FPImgCanv{$IFDEF BGRABITMAP_USE_LCL}, GraphType{$ENDIF},
     36  BGRAMultiFileType;
    3337
    3438type
    35   //pointer for direct pixel access
    36   PBGRAPixel = ^TBGRAPixel;
    37 
     39  TMultiFileContainer = BGRAMultiFileType.TMultiFileContainer;
    3840  Int32or64 = {$IFDEF CPU64}Int64{$ELSE}LongInt{$ENDIF};
    3941  UInt32or64 = {$IFDEF CPU64}UInt64{$ELSE}LongWord{$ENDIF};
    4042
    41   //Each pixel is a sequence of 4 bytes containing blue, green, red and alpha channel.
    42   TBGRAPixel = packed record
    43     blue, green, red, alpha: byte;
    44   end;
    45 
    46   ArrayOfTBGRAPixel = array of TBGRAPixel;
    47 
    48   //gamma expanded values
    49   TExpandedPixel = packed record
    50     red, green, blue, alpha: word;
    51   end;
    52 
    53   //pixel color defined in HSL colorspace
    54   THSLAPixel = packed record
    55     hue, saturation, lightness, alpha: word;
    56   end;
    57   TGSBAPixel = THSLAPixel;
    58 
    59   //general purpose color variable with floating point values
    60   TColorF = packed array[1..4] of single;
    61  
    62   { These types are used as parameters }
    63 
    64   TDrawMode = (dmSet,                   //replace pixels
    65                dmSetExceptTransparent,  //draw pixels with alpha=255
    66                dmLinearBlend,           //blend without gamma correction
    67                dmDrawWithTransparency,  //normal blending with gamma correction
    68                dmXor);                  //bitwise xor for all channels
    69   TChannel = (cRed, cGreen, cBlue, cAlpha);
    70   TChannels = set of TChannel;
    71                
    72   //floodfill option
    73   TFloodfillMode = (fmSet,                   //set pixels
    74                     fmDrawWithTransparency,  //draw fill color with transparency
    75                     fmProgressive);          //draw fill color with transparency according to similarity with start color
    76 
    77   TResampleMode = (rmSimpleStretch,   //low quality resample
    78                    rmFineResample);   //use resample filters and pixel-centered coordinates
    79   TResampleFilter = (rfBox,           //equivalent of stretch with high quality
    80                      rfLinear,        //linear interpolation
    81                      rfHalfCosine,    //mix of rfLinear and rfCosine
    82                      rfCosine,        //cosine-like interpolation
    83                      rfBicubic,       //simple bi-cubic filter (blur)
    84                      rfMitchell,      //downsizing interpolation
    85                      rfSpline,        //upsizing interpolation
    86                      rfLanczos2,      //Lanczos with radius 2
    87                      rfLanczos3,      //Lanczos with radius 3
    88                      rfLanczos4,      //Lanczos with radius 4
    89                      rfBestQuality);  //mix of rfMitchell and rfSpline
    90 
    91   TDitheringAlgorithm = (daNearestNeighbor, daFloydSteinberg);
    92   TAlphaChannelPaletteOption = (acIgnore, acTransparentEntry, acFullChannelInPalette);
     43{=== Miscellaneous types ===}
     44
     45type
     46  {* Options when doing a floodfill (also called bucket fill) }
     47  TFloodfillMode = (
     48    {** Pixels that are filled are replaced }
     49    fmSet,
     50    {** Pixels that are filled are drawn upon with the fill color }
     51    fmDrawWithTransparency,
     52    {** Pixels that are filled are drawn upon to the extent that the color underneath is similar to
     53        the start color. The more different the different is, the less it is drawn upon }
     54    fmProgressive);
     55
     56  {* Specifies how much smoothing is applied to the computation of the median }
     57  TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
     58  {* Specifies the shape of a predefined blur }
     59  TRadialBlurType = (
     60    {** Gaussian-like, pixel importance decreases progressively }
     61    rbNormal,
     62    {** Disk blur, pixel importance does not decrease progressively }
     63    rbDisk,
     64    {** Pixel are considered when they are at a certain distance }
     65    rbCorona,
     66    {** Gaussian-like, but 10 times smaller than ''rbNormal'' }
     67    rbPrecise,
     68    {** Gaussian-like but simplified to be computed faster }
     69    rbFast,
     70    {** Box blur, pixel importance does not decrease progressively
     71        and the pixels are included when they are in a square.
     72        This is much faster than ''rbFast'' however you may get
     73        square shapes in the resulting image }
     74    rbBox);
     75
     76  TEmbossOption = (eoTransparent, eoPreserveHue);
     77  TEmbossOptions = set of TEmbossOption;
     78
     79  TTextLayout = BGRAGraphics.TTextLayout;
    9380
    9481const
     82  tlTop = BGRAGraphics.tlTop;
     83  tlCenter = BGRAGraphics.tlCenter;
     84  tlBottom = BGRAGraphics.tlBottom;
     85
     86  // checks the bounds of an image in the given clipping rectangle
     87  function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean;
     88
     89{==== Imported from GraphType ====}
     90//if this unit is defined, otherwise
     91//define here the types used by the library.
     92{$IFDEF BGRABITMAP_USE_LCL}
     93  type
     94    { Order of the lines in an image }
     95    TRawImageLineOrder = GraphType.TRawImageLineOrder;
     96    { Order of the bits in a byte containing pixel values }
     97    TRawImageBitOrder = GraphType.TRawImageBitOrder;
     98    { Order of the bytes in a group of byte containing pixel values }
     99    TRawImageByteOrder = GraphType.TRawImageByteOrder;
     100    { Definition of a single line 3D bevel }
     101    TGraphicsBevelCut = GraphType.TGraphicsBevelCut;
     102
     103  const
     104    riloTopToBottom = GraphType.riloTopToBottom;   // The first line (line 0) is the top line
     105    riloBottomToTop = GraphType.riloBottomToTop;   // The first line (line 0) is the bottom line
     106
     107    riboBitsInOrder = GraphType.riboBitsInOrder;   // Bit 0 is pixel 0
     108    riboReversedBits = GraphType.riboReversedBits; // Bit 0 is pixel 7 (Bit 1 is pixel 6, ...)
     109
     110    riboLSBFirst = GraphType.riboLSBFirst; // least significant byte first (little endian)
     111    riboMSBFirst = GraphType.riboMSBFirst; // most significant byte first (big endian)
     112
     113    fsSurface = GraphType.fsSurface; //type is defined as Graphics.TFillStyle
     114    fsBorder = GraphType.fsBorder;
     115
     116    bvNone = GraphType.bvNone;
     117    bvLowered = GraphType.bvLowered;
     118    bvRaised = GraphType.bvRaised;
     119    bvSpace = GraphType.bvSpace;
     120{$ELSE}
     121  type
     122    {* Order of the lines in an image }
     123    TRawImageLineOrder = (
     124      {** The first line in memory (line 0) is the top line }
     125      riloTopToBottom,
     126      {** The first line in memory (line 0) is the bottom line }
     127      riloBottomToTop);
     128
     129    {* Order of the bits in a byte containing pixel values }
     130    TRawImageBitOrder = (
     131      {** The lowest bit is on the left. So with a monochrome picture, bit 0 would be pixel 0 }
     132      riboBitsInOrder,
     133      {** The lowest bit is on the right. So with a momochrome picture, bit 0 would be pixel 7 (bit 1 would be pixel 6, ...) }
     134      riboReversedBits);
     135
     136    {* Order of the bytes in a group of byte containing pixel values }
     137    TRawImageByteOrder = (
     138      {** Least significant byte first (little endian) }
     139      riboLSBFirst,
     140      {** most significant byte first (big endian) }
     141      riboMSBFirst);
     142
     143    {* Definition of a single line 3D bevel }
     144    TGraphicsBevelCut =
     145    (
     146      {** No bevel }
     147      bvNone,
     148      {** Shape is lowered, light is on the bottom-right corner }
     149      bvLowered,
     150      {** Shape is raised, light is on the top-left corner }
     151      bvRaised,
     152      {** Shape is at the same level, there is no particular lighting }
     153      bvSpace);
     154{$ENDIF}
     155
     156{$DEFINE INCLUDE_INTERFACE}
     157{$I bgrapixel.inc}
     158
     159{$DEFINE INCLUDE_INTERFACE}
     160{$I geometrytypes.inc}
     161
     162{$DEFINE INCLUDE_INTERFACE}
     163{$i csscolorconst.inc}
     164
     165{$DEFINE INCLUDE_SCANNER_INTERFACE }
     166{$I bgracustombitmap.inc}
     167
     168{==== Integer math ====}
     169
     170  {* Computes the value modulo cycle, and if the ''value'' is negative, the result
     171     is still positive }
     172  function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
     173
     174  { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
     175    They use a table to store already computed values. The return value is an integer
     176    ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is
     177    32768 instead of 1. The input has a period of 65536, so you can supply any integer
     178    without applying a modulo. }
     179
     180  { Compute all values now }
     181  procedure PrecalcSin65536;
     182
     183  {* Returns an integer approximation of the sine. Value ranges from 0 to 65535,
     184     where 65536 corresponds to the next cycle }
     185  function Sin65536(value: word): Int32or64; inline;
     186  {* Returns an integer approximation of the cosine. Value ranges from 0 to 65535,
     187     where 65536 corresponds to the next cycle }
     188  function Cos65536(value: word): Int32or64; inline;
     189
     190  {* Returns the square root of the given byte, considering that
     191     255 is equal to unity }
     192  function ByteSqrt(value: byte): byte; inline;
     193
     194{==== Types provided for fonts ====}
     195type
     196  {* Quality to be used to render text }
     197  TBGRAFontQuality = (
     198    {** Use the system capabilities. It is rather fast however it may be
     199        not be smoothed. }
     200    fqSystem,
     201    {** Use the system capabilities to render with ClearType. This quality is
     202        of course better than fqSystem however it may not be perfect.}
     203    fqSystemClearType,
     204    {** Garanties a high quality antialiasing. }
     205    fqFineAntialiasing,
     206    {** Fine antialiasing with ClearType in assuming an LCD display in red/green/blue order }
     207    fqFineClearTypeRGB,
     208    {** Fine antialiasing with ClearType in assuming an LCD display in blue/green/red order }
     209    fqFineClearTypeBGR);
     210
     211  {* Measurements of a font }
     212  TFontPixelMetric = record
     213    {** The values have been computed }
     214    Defined: boolean;
     215    {** Position of the baseline, where most letters lie }
     216    Baseline,
     217    {** Position of the top of the small letters (x being one of them) }
     218    xLine,
     219    {** Position of the top of the UPPERCASE letters }
     220    CapLine,
     221    {** Position of the bottom of letters like g and p }
     222    DescentLine,
     223    {** Total line height including line spacing defined by the font }
     224    Lineheight: integer;
     225  end;
     226
     227  {* Vertical anchoring of the font. When text is drawn, a start coordinate
     228      is necessary. Text can be positioned in different ways. This enum
     229      defines what position it is regarding the font }
     230  TFontVerticalAnchor = (
     231    {** The top of the font. Everything will be drawn below the start coordinate. }
     232    fvaTop,
     233    {** The center of the font }
     234    fvaCenter,
     235    {** The top of capital letters }
     236    fvaCapLine,
     237    {** The center of capital letters }
     238    fvaCapCenter,
     239    {** The top of small letters }
     240    fvaXLine,
     241    {** The center of small letters }
     242    fvaXCenter,
     243    {** The baseline, the bottom of most letters }
     244    fvaBaseline,
     245    {** The bottom of letters that go below the baseline }
     246    fvaDescentLine,
     247    {** The bottom of the font. Everything will be drawn above the start coordinate }
     248    fvaBottom);
     249
     250  {* Definition of a function that handles work-break }
     251  TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;
     252
     253  {* Alignment for a typewriter, that does not have any more information
     254     than a square shape containing glyphs }
     255  TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight, twaLeft, twaMiddle, twaRight, twaBottomLeft, twaBottom, twaBottomRight);
     256  {* How a typewriter must render its content on a Canvas2d }
     257  TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill);
     258
     259  { TBGRACustomFontRenderer }
     260  {* Abstract class for all font renderers }
     261  TBGRACustomFontRenderer = class
     262    {** Specifies the font to use. Unless the font renderer accept otherwise,
     263        the name is in human readable form, like 'Arial', 'Times New Roman', ...  }
     264    FontName: string;
     265
     266    {** Specifies the set of styles to be applied to the font.
     267        These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
     268        So the value [fsBold,fsItalic] means that the font must be bold and italic }
     269    FontStyle: TFontStyles;
     270
     271    {** Specifies the quality of rendering. Default value is fqSystem }
     272    FontQuality : TBGRAFontQuality;
     273
     274    {** Specifies the rotation of the text, for functions that support text rotation.
     275        It is expressed in tenth of degrees, positive values going counter-clockwise }
     276    FontOrientation: integer;
     277
     278    {** Specifies the height of the font without taking into account additional line spacing.
     279        A negative value means that it is the full height instead }
     280    FontEmHeight: integer;
     281
     282    {** Returns measurement for the current font in pixels }
     283    function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
     284
     285    {** Returns the total size of the string provided using the current font.
     286        Orientation is not taken into account, so that the width is along the text }
     287    function TextSize(sUTF8: string): TSize; virtual; abstract;
     288
     289    {** Draws the UTF8 encoded string, with color ''c''.
     290        If align is taLeftJustify, (''x'',''y'') is the top-left corner.
     291        If align is taCenter, (''x'',''y'') is at the top and middle of the text.
     292        If align is taRightJustify, (''x'',''y'') is the top-right corner.
     293        The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
     294    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
     295
     296    {** Same as above functions, except that the text is filled using texture.
     297        The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
     298    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
     299
     300    {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
     301    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
     302    {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
     303    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
     304
     305    {** Draw the UTF8 encoded string at the coordinate (''x'',''y''), clipped inside the rectangle ''ARect''.
     306        Additional style information is provided by the style parameter.
     307        The color ''c'' is used to fill the text. No rotation is applied. }
     308    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract;
     309
     310    {** Same as above except a ''texture'' is used to fill the text }
     311    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract;
     312
     313    {** Copy the path for the UTF8 encoded string into ''ADest''.
     314        If ''align'' is ''taLeftJustify'', (''x'',''y'') is the top-left corner.
     315        If ''align'' is ''taCenter'', (''x'',''y'') is at the top and middle of the text.
     316        If ''align'' is ''taRightJustify'', (''x'',''y'') is the top-right corner. }
     317    procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional
     318  end;
     319
     320  {* Output mode for the improved renderer for readability. This is used by the font renderer based on LCL in ''BGRAText'' }
     321  TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
     322
     323{** Removes line ending and tab characters from a string (for a function
     324    like ''TextOut'' that does not handle this). this works with UTF8 strings
     325    as well }
     326function CleanTextOutString(s: string): string;
     327{** Remove the line ending at the specified position or return False.
     328    This works with UTF8 strings however the index is the byte index }
     329function RemoveLineEnding(var s: string; indexByte: integer): boolean;
     330{** Remove the line ending at the specified position or return False.
     331    The index is the character index, that may be different from the
     332    byte index }
     333function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     334{** Default word break handler, that simply divide when there is a space }
     335procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
     336
     337{==== Images and resampling ====}
     338
     339type
     340  {* How the resample is to be computed }
     341  TResampleMode = (
     342    {** Low quality resample by repeating pixels, stretching them }
     343    rmSimpleStretch,
     344    {** Use resample filters. This gives high
     345        quality resampling however this the proportion changes slightly because
     346        the first and last pixel are considered to occupy only half a unit as
     347        they are considered as the border of the picture
     348        (pixel-centered coordinates) }
     349    rmFineResample);
     350
     351  {* List of resample filter to be used with ''rmFineResample'' }
     352  TResampleFilter = (
     353    {** Equivalent of simple stretch with high quality and pixel-centered coordinates }
     354    rfBox,
     355    {** Linear interpolation giving slow transition between pixels }
     356    rfLinear,
     357    {** Mix of ''rfLinear'' and ''rfCosine'' giving medium speed stransition between pixels }
     358    rfHalfCosine,
     359    {** Cosine-like interpolation giving fast transition between pixels }
     360    rfCosine,
     361    {** Simple bi-cubic filter (blurry) }
     362    rfBicubic,
     363    {** Mitchell filter, good for downsizing interpolation }
     364    rfMitchell,
     365    {** Spline filter, good for upsizing interpolation, however slightly blurry }
     366    rfSpline,
     367    {** Lanczos with radius 2, blur is corrected }
     368    rfLanczos2,
     369    {** Lanczos with radius 3, high contrast }
     370    rfLanczos3,
     371    {** Lanczos with radius 4, high contrast }
     372    rfLanczos4,
     373    {** Best quality using rfMitchell or rfSpline }
     374    rfBestQuality);
     375
     376const
     377  {** List of strings to represent resample filters }
    95378  ResampleFilterStr : array[TResampleFilter] of string =
    96379   ('Box','Linear','HalfCosine','Cosine','Bicubic','Mitchell','Spline',
    97380    'Lanczos2','Lanczos3','Lanczos4','BestQuality');
    98381
    99 function StrToResampleFilter(str: string): TResampleFilter;
     382  {** Gives the sample filter represented by a string }
     383  function StrToResampleFilter(str: string): TResampleFilter;
    100384
    101385type
    102   TBGRAImageFormat = (ifUnknown, ifJpeg, ifPng, ifGif, ifBmp, ifIco, ifPcx, ifPaintDotNet, ifLazPaint, ifOpenRaster,
    103     ifPsd, ifTarga, ifTiff, ifXwd, ifXPixMap, ifBmpMioMap);
     386  {* List of image formats }
     387  TBGRAImageFormat = (
     388    {** Unknown format }
     389    ifUnknown,
     390    {** JPEG format, opaque, lossy compression }
     391    ifJpeg,
     392    {** PNG format, transparency, lossless compression }
     393    ifPng,
     394    {** GIF format, single transparent color, lossless in theory but only low number of colors allowed }
     395    ifGif,
     396    {** BMP format, transparency, no compression. Note that transparency is
     397        not supported by all BMP readers so it is not recommended to avoid
     398        storing images with transparency in this format }
     399    ifBmp,
     400    {** ICO format, contains different sizes of the same image }
     401    ifIco,
     402    {** PCX format, opaque, rudimentary lossless compression }
     403    ifPcx,
     404    {** Paint.NET format, layers, lossless compression }
     405    ifPaintDotNet,
     406    {** LazPaint format, layers, lossless compression }
     407    ifLazPaint,
     408    {** OpenRaster format, layers, lossless compression }
     409    ifOpenRaster,
     410    {** Phoxo format, layers }
     411    ifPhoxo,
     412    {** Photoshop format, layers, rudimentary lossless compression }
     413    ifPsd,
     414    {** Targa format (TGA), transparency, rudimentary lossless compression }
     415    ifTarga,
     416    {** TIFF format, limited support }
     417    ifTiff,
     418    {** X-Window capture, limited support }
     419    ifXwd,
     420    {** X-Pixmap, text encoded image, limited support }
     421    ifXPixMap,
     422    {** iGO BMP, limited support }
     423    ifBmpMioMap);
     424
     425  {* Options when loading an image }
     426  TBGRALoadingOption = (
     427     {** Do not clear RGB channels when alpha is zero (not recommended) }
     428     loKeepTransparentRGB,
     429     {** Consider BMP to be opaque if no alpha value is provided (for compatibility) }
     430     loBmpAutoOpaque,
     431     {** Load JPEG quickly however with a lower quality }
     432     loJpegQuick);
     433  TBGRALoadingOptions = set of TBGRALoadingOption;
    104434
    105435var
     436  {** List of stream readers for images }
    106437  DefaultBGRAImageReader: array[TBGRAImageFormat] of TFPCustomImageReaderClass;
     438  {** List of stream writers for images }
    107439  DefaultBGRAImageWriter: array[TBGRAImageFormat] of TFPCustomImageWriterClass;
    108440
    109 type
    110   TBGRAFontQuality = (fqSystem, fqSystemClearType, fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR);
    111         // fqSystem: use system rendering. It is fast however it may be not be smoothed.
    112         // fqSystemClearType: use system rendering with ClearType. This quality is of course better than fqSystem however it may not be much smoother.
    113         // fqFineAntialiasing: garanties a high quality antialiasing. This is slower.
    114         // fqFineClearTypeRGB: garanties a high quality antialiasing with ClearType. The order of the color in the LCD screen is supposed to be un red/green/blue order.
    115         // fqFineClearTypeBGR: same as above, except the color of the LCD screen is supposed to be in blue/green/red order.
    116 
    117   TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
    118   TRadialBlurType = (rbNormal, rbDisk, rbCorona, rbPrecise, rbFast, rbBox);
    119   TSplineStyle = (ssInside, ssInsideWithEnds, ssCrossing, ssCrossingWithEnds,
    120     ssOutside, ssRoundOutside, ssVertexToSide);
    121  
    122   { Advanced blending modes
    123     see : http://www.brighthub.com/multimedia/photography/articles/18301.aspx
    124     and : http://www.pegtop.net/delphi/articles/blendmodes/ }
    125   TBlendOperation = (boLinearBlend, boTransparent,                                  //blending
    126     boLighten, boScreen, boAdditive, boLinearAdd, boColorDodge, boDivide, boNiceGlow, boSoftLight, boHardLight, //lighting
    127     boGlow, boReflect, boOverlay, boDarkOverlay, boDarken, boMultiply, boColorBurn, //masking
    128     boDifference, boLinearDifference, boExclusion, boLinearExclusion, boSubtract, boLinearSubtract, boSubtractInverse, boLinearSubtractInverse,
    129     boNegation, boLinearNegation, boXor);         //negative
    130 
    131 const
    132   boGlowMask = boGlow;
    133   boLinearMultiply = boMultiply;
    134   boNonLinearOverlay = boDarkOverlay;
    135   EmptyRect : TRect = (left:0; top:0; right:0; bottom: 0);
    136 
    137 const
    138   BlendOperationStr : array[TBlendOperation] of string =
    139    ('LinearBlend', 'Transparent',
    140     'Lighten', 'Screen', 'Additive', 'LinearAdd', 'ColorDodge', 'Divide', 'NiceGlow', 'SoftLight', 'HardLight',
    141     'Glow', 'Reflect', 'Overlay', 'DarkOverlay', 'Darken', 'Multiply', 'ColorBurn',
    142     'Difference', 'LinearDifference', 'Exclusion', 'LinearExclusion', 'Subtract', 'LinearSubtract', 'SubtractInverse', 'LinearSubtractInverse',
    143     'Negation', 'LinearNegation', 'Xor');
    144 
    145 function StrToBlendOperation(str: string): TBlendOperation;
    146 
    147 type
    148   TGradientType = (gtLinear, gtReflected, gtDiamond, gtRadial);
    149 const
    150   GradientTypeStr : array[TGradientType] of string =
    151   ('Linear','Reflected','Diamond','Radial');
    152 function StrToGradientType(str: string): TGradientType;
    153  
    154 type
    155   { A pen style is defined as a list of floating number. The first number is the length of the first dash,
    156     the second number is the length of the first gap, the third number is the length of the second dash...
    157     It must have an even number of values. }
    158   TBGRAPenStyle = Array Of Single;
    159   TRoundRectangleOption = (rrTopLeftSquare,rrTopRightSquare,rrBottomRightSquare,rrBottomLeftSquare,
    160                            rrTopLeftBevel,rrTopRightBevel,rrBottomRightBevel,rrBottomLeftBevel,rrDefault);
    161   TRoundRectangleOptions = set of TRoundRectangleOption;
    162   TPolygonOrder = (poNone, poFirstOnTop, poLastOnTop); //see TBGRAMultiShapeFiller in BGRAPolygon
    163  
    164 function BGRAPenStyle(dash1, space1: single; dash2: single=0; space2: single = 0; dash3: single=0; space3: single = 0; dash4 : single = 0; space4 : single = 0): TBGRAPenStyle; 
    165  
    166 { Point, polygon and curve structures }
    167 type
    168   PPointF = ^TPointF;
    169   TPointF = packed record
    170     x, y: single;
    171   end;
    172   ArrayOfTPointF = array of TPointF;
    173   TArcOption = (aoClosePath, aoPie, aoFillPath);
    174   TArcOptions = set of TArcOption;
    175 
    176   TCubicBezierCurve = record
    177     p1,c1,c2,p2: TPointF;
    178   end;
    179   TQuadraticBezierCurve = record
    180     p1,c,p2: TPointF;
    181   end;
    182 
    183   TArcDef = record
    184     center: TPointF;
    185     radius: TPointF;
    186     xAngleRadCW, startAngleRadCW, endAngleRadCW: single; //see convention in BGRAPath
    187     anticlockwise: boolean
    188   end;
    189   PArcDef = ^TArcDef;
    190 
    191   TPoint3D = record
    192     x,y,z: single;
    193   end;
    194 
    195   TBGRAArrowStyle = (asNone, asNormal, asCut, asTriangle, asHollowTriangle, asFlipped, asFlippedCut, asTail, asTailRepeat);
    196 
    197   TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight,
    198                               twaLeft, twaMiddle, twaRight,
    199                               twaBottomLeft, twaBottom, twaBottomRight);
    200   TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill);
    201 
    202 function ConcatPointsF(const APolylines: array of ArrayOfTPointF): ArrayOfTPointF;
    203 
    204 function Point3D(x,y,z: single): TPoint3D;
    205 operator = (const v1,v2: TPoint3D): boolean; inline;
    206 operator * (const v1,v2: TPoint3D): single; inline;
    207 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline;
    208 operator - (const v1,v2: TPoint3D): TPoint3D; inline;
    209 operator - (const v: TPoint3D): TPoint3D; inline;
    210 operator + (const v1,v2: TPoint3D): TPoint3D; inline;
    211 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D);
    212 procedure Normalize3D(var v: TPoint3D); inline;
    213 
    214 function BezierCurve(origin, control1, control2, destination: TPointF) : TCubicBezierCurve; overload;
    215 function BezierCurve(origin, control, destination: TPointF) : TQuadraticBezierCurve; overload;
    216 function BezierCurve(origin, destination: TPointF) : TQuadraticBezierCurve; overload;
    217 function ArcDef(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean) : TArcDef;
    218 
    219 { Useful constants }
    220 const
    221   dmFastBlend = dmLinearBlend;
    222   EmptySingle: single = -3.402823e38;                        //used as a separator in floating point lists
    223   EmptyPointF: TPointF = (x: -3.402823e38; y: -3.402823e38); //used as a separator in TPointF lists
    224   BGRAPixelTransparent: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 0);
    225   BGRAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255);
    226   BGRABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
    227 
    228   { This color is needed for drawing black shapes on the standard TCanvas, because
    229     when drawing with pure black, there is no way to know if something has been
    230     drawn or if it is transparent }
    231   clBlackOpaque = TColor($010000);
    232 
    233 {$DEFINE INCLUDE_COLOR_CONST}
    234 {$i csscolorconst.inc}
    235 
    236 type
    237   TBGRAColorDefinition = record
    238     Name: string;
    239     Color: TBGRAPixel;
    240   end;
    241 
    242   { TBGRAColorList }
    243 
    244   TBGRAColorList = class
    245   protected
    246     FFinished: boolean;
    247     FNbColors: integer;
    248     FColors: array of TBGRAColorDefinition;
    249     function GetByIndex(Index: integer): TBGRAPixel;
    250     function GetByName(Name: string): TBGRAPixel;
    251     function GetName(Index: integer): string;
    252   public
    253     constructor Create;
    254     procedure Add(Name: string; const Color: TBGRAPixel);
    255     procedure Finished;
    256     function IndexOf(Name: string): integer;
    257     function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
    258 
    259     property ByName[Name: string]: TBGRAPixel read GetByName;
    260     property ByIndex[Index: integer]: TBGRAPixel read GetByIndex; default;
    261     property Name[Index: integer]: string read GetName;
    262     property Count: integer read FNbColors;
    263   end;
    264 
    265 var
    266   VGAColors, CSSColors: TBGRAColorList;
    267 
    268 function isEmptyPointF(pt: TPointF): boolean;
    269 
    270 type
    271   TFontPixelMetric = record
    272     Defined: boolean;
    273     Baseline, xLine, CapLine, DescentLine, Lineheight: integer;
    274   end;
    275 
    276   { A scanner is like an image, but its content has no limit and can be calculated on the fly.
    277     It must not implement reference counting. }
    278   IBGRAScanner = interface
    279     procedure ScanMoveTo(X,Y: Integer);
    280     function ScanNextPixel: TBGRAPixel;
    281     function ScanAt(X,Y: Single): TBGRAPixel;
    282     function ScanAtInteger(X,Y: integer): TBGRAPixel;
    283     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode);
    284     function IsScanPutPixelsDefined: boolean;
    285   end;
    286 
    287   { A path is the ability to define a contour with moveTo, lineTo...
    288     It must not implement reference counting. }
    289   IBGRAPath = interface
    290     procedure closePath;
    291     procedure moveTo(const pt: TPointF);
    292     procedure lineTo(const pt: TPointF);
    293     procedure polylineTo(const pts: array of TPointF);
    294     procedure quadraticCurveTo(const cp,pt: TPointF);
    295     procedure bezierCurveTo(const cp1,cp2,pt: TPointF);
    296     procedure arc(const arcDef: TArcDef);
    297     procedure copyTo(dest: IBGRAPath);
    298   end;
    299 
    300   TScanAtFunction = function (X,Y: Single): TBGRAPixel of object;
    301   TScanAtIntegerFunction = function (X,Y: Integer): TBGRAPixel of object;
    302   TScanNextPixelFunction = function: TBGRAPixel of object;
    303   TBGRACustomGradient = class;
    304 
    305   TBGRACustomFillInfo = class;
    306   TBGRACustomFontRenderer = class;
    307 
    308   { TBGRACustomBitmap }
    309 
    310   TBGRACustomBitmap = class(TFPCustomImage,IBGRAScanner) // a bitmap can be used as a scanner
    311   private
    312     function GetFontAntialias: Boolean;
    313     procedure SetFontAntialias(const AValue: Boolean);
    314   protected
    315      { accessors to properies }
    316      function GetArrowEndRepeat: integer; virtual; abstract;
    317      function GetArrowStartRepeat: integer; virtual; abstract;
    318      procedure SetArrowEndRepeat(AValue: integer); virtual; abstract;
    319      procedure SetArrowStartRepeat(AValue: integer); virtual; abstract;
    320      function GetArrowEndOffset: single; virtual; abstract;
    321      function GetArrowStartOffset: single; virtual; abstract;
    322      procedure SetArrowEndOffset(AValue: single); virtual; abstract;
    323      procedure SetArrowStartOffset(AValue: single); virtual; abstract;
    324      function GetArrowEndSize: TPointF; virtual; abstract;
    325      function GetArrowStartSize: TPointF; virtual; abstract;
    326      procedure SetArrowEndSize(AValue: TPointF); virtual; abstract;
    327      procedure SetArrowStartSize(AValue: TPointF); virtual; abstract;
    328      function GetLineCap: TPenEndCap; virtual; abstract;
    329      procedure SetLineCap(AValue: TPenEndCap); virtual; abstract;
    330      function GetFontRenderer: TBGRACustomFontRenderer; virtual; abstract;
    331      procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); virtual; abstract;
    332      function GetHeight: integer; virtual; abstract;
    333      function GetWidth: integer; virtual; abstract;
    334      function GetDataPtr: PBGRAPixel; virtual; abstract;
    335      function GetNbPixels: integer; virtual; abstract;
    336      function CheckEmpty: boolean; virtual; abstract;
    337      function GetHasTransparentPixels: boolean; virtual; abstract;
    338      function GetAverageColor: TColor; virtual; abstract;
    339      function GetAveragePixel: TBGRAPixel; virtual; abstract;
    340      procedure SetCanvasOpacity(AValue: byte); virtual; abstract;
    341      function GetScanLine(y: integer): PBGRAPixel; virtual; abstract;
    342      function GetRefCount: integer; virtual; abstract;
    343      function GetBitmap: TBitmap; virtual; abstract;
    344      function GetLineOrder: TRawImageLineOrder; virtual; abstract;
    345      function GetCanvasFP: TFPImageCanvas; virtual; abstract;
    346      function GetCanvasDrawModeFP: TDrawMode; virtual; abstract;
    347      procedure SetCanvasDrawModeFP(const AValue: TDrawMode); virtual; abstract;
    348      function GetCanvas: TCanvas; virtual; abstract;
    349      function GetCanvasOpacity: byte; virtual; abstract;
    350      function GetCanvasAlphaCorrection: boolean; virtual; abstract;
    351      procedure SetCanvasAlphaCorrection(const AValue: boolean); virtual; abstract;
    352      function GetFontHeight: integer; virtual; abstract;
    353      procedure SetFontHeight(AHeight: integer); virtual; abstract;
    354      function GetFontFullHeight: integer; virtual; abstract;
    355      procedure SetFontFullHeight(AHeight: integer); virtual; abstract;
    356      function GetPenStyle: TPenStyle; virtual; abstract;
    357      procedure SetPenStyle(const AValue: TPenStyle); virtual; abstract;
    358      function GetCustomPenStyle: TBGRAPenStyle; virtual; abstract;
    359      procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); virtual; abstract;
    360      function GetClipRect: TRect; virtual; abstract;
    361      procedure SetClipRect(const AValue: TRect); virtual; abstract;
    362      function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
    363      procedure ClearTransparentPixels; virtual; abstract;
    364      procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;
    365      procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;
    366 
    367   public
    368      Caption:   string;  //user defined caption
    369 
    370      {-------------------font style------------------------}
    371      FontName: string;              //Specifies the font to use. Unless the font renderer accept otherwise,
    372                                     //the name is in human readable form, like 'Arial', 'Times New Roman', ...
    373 
    374      FontStyle: TFontStyles;        //Specifies the set of styles to be applied to the font.
    375                                     //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
    376                                     //So the value [fsBold,fsItalic] means that the font must be bold and italic.
    377 
    378      FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem.
    379 
    380      FontOrientation: integer;      //Specifies the rotation of the text, for functions that support text rotation.
    381                                     //It is expressed in tenth of degrees, positive values going counter-clockwise.
    382 
    383      //line style
    384      JoinStyle: TPenJoinStyle;
    385      JoinMiterLimit: single;
    386 
    387      FillMode:  TFillMode;  //winding or alternate
    388      LinearAntialiasing: boolean;
    389 
    390      { The resample filter is used when resizing the bitmap, and
    391        scan interpolation filter is used when the bitmap is used
    392        as a scanner (IBGRAScanner) }
    393      ResampleFilter,
    394      ScanInterpolationFilter: TResampleFilter;
    395      ScanOffset: TPoint;
    396 
    397      constructor Create; virtual; abstract; overload;
    398      constructor Create(ABitmap: TBitmap); virtual; abstract; overload;
    399      constructor Create(AWidth, AHeight: integer; Color: TColor); virtual; abstract; overload;
    400      constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); virtual; abstract; overload;
    401      constructor Create(AFilename: string); virtual; abstract; overload;
    402      constructor Create(AFilename: string; AIsUtf8Filename: boolean); virtual; abstract; overload;
    403      constructor Create(AStream: TStream); virtual; abstract; overload;
    404 
    405      function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; virtual; abstract; overload;
    406      function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; overload;
    407      function NewBitmap(Filename: string): TBGRACustomBitmap; virtual; abstract; overload;
    408      function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; virtual; abstract; overload;
    409 
    410      //there are UTF8 functions that are different from standard function as those
    411      //depend on TFPCustomImage that does not clearly handle UTF8
    412      procedure LoadFromFile(const filename: string); virtual;
    413      procedure LoadFromFileUTF8(const filenameUTF8: string); virtual;
    414      procedure LoadFromFileUTF8(const filenameUTF8: string; AHandler: TFPCustomImageReader); virtual;
    415      procedure LoadFromStream(Str: TStream); virtual; overload;
    416      procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader); virtual; overload;
    417      procedure SaveToFile(const filename: string); virtual; overload;
    418      procedure SaveToFile(const filename: string; Handler:TFPCustomImageWriter); virtual; overload;
    419      procedure SaveToFileUTF8(const filenameUTF8: string); virtual; overload;
    420      procedure SaveToFileUTF8(const filenameUTF8: string; Handler:TFPCustomImageWriter); virtual; overload;
    421      procedure SaveToStreamAsPng(Str: TStream); virtual; abstract;
    422      procedure SaveToStreamAs(Str: TStream; AFormat: TBGRAImageFormat); virtual;
    423      procedure Assign(ARaster: TRasterImage); virtual; abstract; overload;
    424      procedure Assign(MemBitmap: TBGRACustomBitmap); virtual; abstract; overload;
    425      procedure Serialize(AStream: TStream); virtual; abstract;
    426      procedure Deserialize(AStream: TStream); virtual; abstract;
    427 
    428      {Pixel functions}
    429      procedure SetPixel(x, y: int32or64; c: TColor); virtual; abstract; overload;
    430      procedure XorPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    431      procedure SetPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    432      procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    433      procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
    434      procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
    435      procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract;
    436      procedure ErasePixel(x, y: int32or64; alpha: byte); virtual; abstract;
    437      procedure AlphaPixel(x, y: int32or64; alpha: byte); virtual; abstract;
    438      function GetPixel(x, y: int32or64): TBGRAPixel; virtual; abstract; overload;
    439      function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract;
    440      function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract; overload;
    441      function GetPixelCycle(x, y: int32or64): TBGRAPixel; virtual; overload;
    442      function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    443      function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;
    444      function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    445      function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;
    446 
    447      {Line primitives}
    448      procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    449      procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    450      procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    451      procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
    452      procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload;
    453      procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    454      procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); virtual; abstract;
    455      procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    456      procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    457      procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    458      procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); virtual; abstract;
    459      procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    460      procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; maxDiff: byte); virtual; abstract;
    461      procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
    462      procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode);
    463      procedure HorizLine(x,y,x2: Int32or64; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
    464 
    465      {Shapes}
    466      procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); virtual; abstract;
    467      procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); virtual; abstract;
    468 
    469      procedure ArrowStartAsNone;
    470      procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
    471      procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
    472      procedure ArrowStartAsTail;
    473 
    474      procedure ArrowEndAsNone;
    475      procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
    476      procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
    477      procedure ArrowEndAsTail;
    478 
    479      procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode= dmDrawWithTransparency); virtual; abstract;
    480      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract; overload;
    481      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; abstract; overload;
    482      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); virtual; abstract; overload;
    483      procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); virtual; abstract; overload;
    484      procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    485      procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
    486      procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); virtual; abstract; overload;
    487 
    488      procedure DrawPolyLine(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode=dmDrawWithTransparency);
    489      procedure DrawPolyLineAntialias(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean); virtual; overload;
    490      procedure DrawPolyLineAntialias(const points: array of TPoint; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; overload;
    491      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
    492      procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    493      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
    494      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;
    495      procedure DrawPolygon(const points: array of TPoint; c: TBGRAPixel; ADrawMode: TDrawMode=dmDrawWithTransparency);
    496      procedure DrawPolygonAntialias(const points: array of TPoint; c: TBGRAPixel); overload;
    497      procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
    498      procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    499      procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;
    500 
    501      procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract;
    502      procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract; overload;
    503      procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); virtual; abstract; overload;
    504      procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); virtual; abstract; overload;
    505      procedure ErasePolyLine(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
    506      procedure ErasePolyLineAntialias(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); overload;
    507      procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); virtual; abstract; overload;
    508      procedure ErasePolygonOutline(const points: array of TPoint; alpha: byte);
    509      procedure ErasePolygonOutlineAntialias(const points: array of TPoint; alpha: byte);
    510 
    511      procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); virtual; abstract;
    512      procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); virtual; abstract;
    513 
    514      procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
    515      procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
    516      procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    517      procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    518      procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); virtual; abstract; overload;
    519 
    520      procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
    521      procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
    522      procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True);  virtual; abstract; overload;
    523      procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    524      procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    525      procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    526      procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;
    527      procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    528      procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;
    529 
    530      procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel);  virtual; abstract; overload;
    531      procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); virtual; abstract; overload;
    532      procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); virtual; abstract; overload;
    533      procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload;
    534      procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload;
    535 
    536      procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
    537      procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
    538      procedure FillPolyAntialias(const points: array of TPointF; c: TBGRAPixel); virtual; abstract;
    539      procedure FillPolyAntialias(const points: array of TPointF; texture: IBGRAScanner); virtual; abstract;
    540      procedure ErasePoly(const points: array of TPointF; alpha: byte); virtual; abstract;
    541      procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); virtual; abstract;
    542 
    543      procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
    544      procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
    545      procedure FillShapeAntialias(shape: TBGRACustomFillInfo; c: TBGRAPixel); virtual; abstract;
    546      procedure FillShapeAntialias(shape: TBGRACustomFillInfo; texture: IBGRAScanner); virtual; abstract;
    547      procedure EraseShape(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;
    548      procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;
    549 
    550      procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); virtual; abstract;
    551      procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); virtual; abstract;
    552      procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract;
    553      procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); virtual; abstract;
    554      procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); virtual; abstract;
    555      procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); virtual; abstract;
    556      procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); virtual; abstract;
    557 
    558      procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    559      procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    560      procedure Rectangle(x, y, x2, y2: integer; c: TColor); virtual; overload;
    561      procedure Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
    562      procedure Rectangle(r: TRect; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual;overload;
    563      procedure Rectangle(r: TRect; c: TColor); virtual; overload;
    564      procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single); virtual; overload;
    565      procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract; overload;
    566      procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    567 
    568      procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
    569      procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
    570      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
    571      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    572      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    573      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
    574      procedure FillRoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;
    575      procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    576      procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    577      procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); virtual; abstract;
    578 
    579      procedure EllipseInRect(r: TRect; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
    580      procedure EllipseInRect(r: TRect; BorderColor,FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
    581      procedure FillEllipseInRect(r: TRect; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;
    582 
    583      procedure FillRect(r: TRect; c: TColor); virtual; overload;
    584      procedure FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
    585      procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode); virtual; overload;
    586      procedure FillRect(x, y, x2, y2: integer; c: TColor); virtual; overload;
    587      procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    588      procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
    589      procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); virtual; abstract;
    590      procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); virtual; abstract;
    591      procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); virtual; abstract;
    592      procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); virtual; abstract;
    593 
    594      procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; overload;
    595      procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; overload;
    596      procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    597      procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    598      procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; overload;
    599      procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; overload;
    600      function TextSize(sUTF8: string): TSize; virtual; abstract;
    601 
    602      { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c or texture is used to fill the text.
    603        The value of FontOrientation is taken into account, so that the text may be rotated. }
    604      procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel); virtual; overload;
    605      procedure TextOut(x, y: single; sUTF8: string; c: TColor); virtual; overload;
    606      procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner); virtual; overload;
    607 
    608      { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    609        The position depends on the specified horizontal alignment halign and vertical alignement valign.
    610        The color c or texture is used to fill the text. No rotation is applied. }
    611      procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); virtual; overload;
    612      procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); virtual; overload;
    613 
    614      {Spline}
    615      function ComputeClosedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
    616      function ComputeOpenedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
    617      function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
    618      function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;
    619      function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
    620      function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;
    621 
    622      function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;
    623      function ComputeWidePolyline(const points: array of TPointF; w: single; Closed: boolean): ArrayOfTPointF; virtual; abstract;
    624      function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;
    625 
    626      function ComputeEllipse(x,y,rx,ry: single): ArrayOfTPointF; deprecated;
    627      function ComputeEllipse(x,y,rx,ry,w: single): ArrayOfTPointF; deprecated;
    628      function ComputeEllipseContour(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    629      function ComputeEllipseBorder(x,y,rx,ry,w: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    630      function ComputeArc65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    631      function ComputeArcRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    632      function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    633      function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    634      function ComputePie65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    635      function ComputePieRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    636 
    637      {Filling}
    638      procedure FillTransparent; virtual;
    639      procedure NoClip; virtual; abstract;
    640      procedure ApplyGlobalOpacity(alpha: byte); virtual; abstract;
    641      procedure Fill(c: TColor); virtual; overload;
    642      procedure Fill(c: TBGRAPixel); virtual; overload;
    643      procedure Fill(texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
    644      procedure Fill(texture: IBGRAScanner); virtual; abstract; overload;
    645      procedure Fill(c: TBGRAPixel; start, Count: integer); virtual; abstract; overload;
    646      procedure DrawPixels(c: TBGRAPixel; start, Count: integer); virtual; abstract;
    647      procedure AlphaFill(alpha: byte); virtual; overload;
    648      procedure AlphaFill(alpha: byte; start, Count: integer); virtual; abstract; overload;
    649      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); virtual; overload;
    650      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); virtual; overload;
    651      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); virtual; abstract; overload;
    652      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
    653      procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); virtual; abstract; overload;
    654      procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); virtual; abstract; overload;
    655      procedure ReplaceColor(before, after: TColor); virtual; abstract; overload;
    656      procedure ReplaceColor(before, after: TBGRAPixel); virtual; abstract; overload;
    657      procedure ReplaceTransparent(after: TBGRAPixel); virtual; abstract; overload;
    658      procedure FloodFill(X, Y: integer; Color: TBGRAPixel;
    659        mode: TFloodfillMode; Tolerance: byte = 0); virtual;
    660      procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel;
    661        mode: TFloodfillMode; Tolerance: byte = 0); virtual; abstract;
    662      procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel;
    663        gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    664        gammaColorCorrection: boolean = True; Sinus: Boolean=False); virtual; abstract;
    665      procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
    666        gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    667        Sinus: Boolean=False); virtual; abstract;
    668      function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
    669                 AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; virtual; abstract;
    670 
    671      {Canvas drawing functions}
    672      procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
    673        AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
    674      procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
    675        ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
    676      procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); virtual; abstract;
    677      procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); virtual; abstract;
    678      procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); virtual; abstract;
    679      procedure DrawPart(ARect: TRect; Canvas: TCanvas; x, y: integer; Opaque: boolean); virtual;
    680      function GetPart(ARect: TRect): TBGRACustomBitmap; virtual; abstract;
    681      function GetPtrBitmap(Top,Bottom: Integer): TBGRACustomBitmap; virtual; abstract;
    682      procedure InvalidateBitmap; virtual; abstract;         //call if you modify with Scanline
    683      procedure LoadFromBitmapIfNeeded; virtual; abstract;   //call to ensure that bitmap data is up to date
    684 
    685      {BGRA bitmap functions}
    686      procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
    687      procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
    688      procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
    689      procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
    690      procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
    691      procedure PutImagePart(x,y: integer; Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte = 255);
    692      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
    693      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte=255); overload;
    694      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); virtual; abstract; overload;
    695      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
    696      function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap): TRect;
    697      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload;
    698      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload;
    699      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload;
    700      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload;
    701      procedure ComputeImageAngleAxes(x,y,w,h,angle: single; imageCenterX,imageCenterY: single; ARestoreOffsetAfterRotation: boolean;
    702        out Origin,HAxis,VAxis: TPointF);
    703      function GetImageAngleBounds(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; ARestoreOffsetAfterRotation: boolean = false): TRect;
    704      procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); virtual; abstract;
    705      procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255;
    706          ALinearBlend: boolean = false); virtual; abstract;
    707      function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; virtual; abstract;
    708      function Equals(comp: TBGRACustomBitmap): boolean; virtual; abstract;
    709      function Equals(comp: TBGRAPixel): boolean; virtual; abstract;
    710      function Resample(newWidth, newHeight: integer;
    711        mode: TResampleMode = rmFineResample): TBGRACustomBitmap; virtual; abstract;
    712      procedure VerticalFlip; virtual; overload;
    713      procedure VerticalFlip(ARect: TRect); virtual; abstract; overload;
    714      procedure HorizontalFlip; virtual; overload;
    715      procedure HorizontalFlip(ARect: TRect); virtual; abstract; overload;
    716      function RotateCW: TBGRACustomBitmap; virtual; abstract;
    717      function RotateCCW: TBGRACustomBitmap; virtual; abstract;
    718      procedure Negative; virtual; abstract;
    719      procedure NegativeRect(ABounds: TRect); virtual; abstract;
    720      procedure LinearNegative; virtual; abstract;
    721      procedure LinearNegativeRect(ABounds: TRect); virtual; abstract;
    722      procedure InplaceGrayscale; virtual; abstract;
    723      procedure InplaceGrayscale(ABounds: TRect); virtual; abstract;
    724      procedure ConvertToLinearRGB; virtual; abstract;
    725      procedure ConvertFromLinearRGB; virtual; abstract;
    726      procedure SwapRedBlue; virtual; abstract;
    727      procedure GrayscaleToAlpha; virtual; abstract;
    728      procedure AlphaToGrayscale; virtual; abstract;
    729      procedure ApplyMask(mask: TBGRACustomBitmap); overload;
    730      procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect); overload;
    731      procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); virtual; abstract; overload;
    732      function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; virtual; abstract;
    733      function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; virtual; abstract;
    734      function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; virtual; abstract;
    735      function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; virtual; abstract;
    736 
    737      {Filters}
    738      function FilterSmartZoom3(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
    739      function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
    740      function FilterSmooth: TBGRACustomBitmap; virtual; abstract;
    741      function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
    742      function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
    743      function FilterContour: TBGRACustomBitmap; virtual; abstract;
    744      function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract;
    745      function FilterBlurRadial(radius: integer;
    746        blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    747      function FilterBlurRadial(ABounds: TRect; radius: integer;
    748        blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    749      function FilterBlurMotion(distance: integer; angle: single;
    750        oriented: boolean): TBGRACustomBitmap; virtual; abstract;
    751      function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
    752        oriented: boolean): TBGRACustomBitmap; virtual; abstract;
    753      function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
    754      function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
    755      function FilterEmboss(angle: single): TBGRACustomBitmap; virtual; abstract;
    756      function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    757      function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; virtual; abstract;
    758      function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; virtual; abstract;
    759      function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; virtual; abstract;
    760      function FilterGrayscale: TBGRACustomBitmap; virtual; abstract;
    761      function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    762      function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
    763      function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
    764      function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; virtual; abstract;
    765      function FilterSphere: TBGRACustomBitmap; virtual; abstract;
    766      function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
    767      function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
    768      function FilterCylinder: TBGRACustomBitmap; virtual; abstract;
    769      function FilterPlane: TBGRACustomBitmap; virtual; abstract;
    770 
    771      property Width: integer Read GetWidth;        //width of the image in pixels
    772      property Height: integer Read GetHeight;      //height of the image in pixels
    773      property NbPixels: integer Read GetNbPixels;  //total number of pixels. It is always true that NbPixels = Width * Height
    774 
    775      property ScanLine[y: integer]: PBGRAPixel Read GetScanLine;   //Returns the address of the left-most pixel of any line.
    776                                                                    //The parameter y ranges from 0 to Height-1.
    777 
    778      property LineOrder: TRawImageLineOrder Read GetLineOrder;     //Indicates the order in which lines are stored in memory.
    779                                                                    //If it is equal to riloTopToBottom, the first line is the top line.
    780                                                                    //If it is equal to riloBottomToTop, the first line is the bottom line.
    781 
    782      property Data: PBGRAPixel Read GetDataPtr;  //Provides a pointer to the first pixel in memory.
    783                                                  //Depending on the LineOrder property, this can be the top-left pixel or the bottom-left pixel.
    784                                                  //There is no padding between scanlines, so the start of the next line is at the address Data + Width.
    785 
    786      property Empty: boolean Read CheckEmpty;    //Returns True if the bitmap only contains transparent pixels or has a size of zero.
    787 
    788      property HasTransparentPixels: boolean Read GetHasTransparentPixels; //Returns True if there are transparent or semitransparent pixels,
    789                                                                           //and so if the image would be stored with an alpha channel.
    790 
    791      property RefCount: integer Read GetRefCount;
    792      property Bitmap: TBitmap Read GetBitmap; //don't forget to call InvalidateBitmap before if you changed something with Scanline
    793      property AverageColor: TColor Read GetAverageColor;
    794      property AveragePixel: TBGRAPixel Read GetAveragePixel;
    795      property CanvasFP: TFPImageCanvas read GetCanvasFP;
    796      property CanvasDrawModeFP: TDrawMode read GetCanvasDrawModeFP write SetCanvasDrawModeFP;
    797      property Canvas: TCanvas Read GetCanvas;
    798      property CanvasOpacity: byte Read GetCanvasOpacity Write SetCanvasOpacity;
    799      property CanvasAlphaCorrection: boolean
    800        Read GetCanvasAlphaCorrection Write SetCanvasAlphaCorrection;
    801 
    802      property PenStyle: TPenStyle read GetPenStyle Write SetPenStyle;
    803      property CustomPenStyle: TBGRAPenStyle read GetCustomPenStyle write SetCustomPenStyle;
    804      property ClipRect: TRect read GetClipRect write SetClipRect;
    805 
    806      { Specifies the height of the font without taking into account additional line spacing.
    807        A negative value means that it is the full height instead (see below). }
    808      property FontHeight: integer Read GetFontHeight Write SetFontHeight;
    809 
    810      { Specifies the height of the font, taking into account the additional line spacing defined for the font. }
    811      property FontFullHeight: integer read GetFontFullHeight write SetFontFullHeight;
    812 
    813      property FontAntialias: Boolean read GetFontAntialias write SetFontAntialias;    //Simplified property to specify the quality.
    814      property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;   //Returns measurement for the current font in pixels.
    815 
    816      { Specifies the font renderer. By default it is an instance of TLCLFontRenderer of unit BGRAText.
    817        Other renderers are provided in BGRATextFX unit and BGRAVectorize unit.
    818        Once you assign a renderer, it will automatically be freed.
    819        The renderers may provide additional styling for the font. }
    820      property FontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;
    821 
    822      property LineCap: TPenEndCap read GetLineCap write SetLineCap;
    823      property ArrowStartSize: TPointF read GetArrowStartSize write SetArrowStartSize;
    824      property ArrowEndSize: TPointF read GetArrowEndSize write SetArrowEndSize;
    825      property ArrowStartOffset: single read GetArrowStartOffset write SetArrowStartOffset;
    826      property ArrowEndOffset: single read GetArrowEndOffset write SetArrowEndOffset;
    827      property ArrowStartRepeat: integer read GetArrowStartRepeat write SetArrowStartRepeat;
    828      property ArrowEndRepeat: integer read GetArrowEndRepeat write SetArrowEndRepeat;
    829 
    830      //IBGRAScanner
    831      function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual; abstract;
    832      procedure ScanMoveTo(X,Y: Integer); virtual; abstract;
    833      function ScanNextPixel: TBGRAPixel; virtual; abstract;
    834      function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
    835      procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    836      function IsScanPutPixelsDefined: boolean; virtual;
    837 
    838   protected
    839      //interface
    840      function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    841      function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    842      function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    843 
    844   end;
    845 
    846   { TBGRACustomScanner }
    847 
    848   TBGRACustomScanner = class(IBGRAScanner)
    849   private
    850     FCurX,FCurY: integer;
    851   public
    852     function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual;
    853     procedure ScanMoveTo(X,Y: Integer); virtual;
    854     function ScanNextPixel: TBGRAPixel; virtual;
    855     function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
    856     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    857     function IsScanPutPixelsDefined: boolean; virtual;
    858   protected
    859     function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    860     function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    861     function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    862   end;
    863 
    864   { TBGRACustomGradient }
    865 
    866   TBGRACustomGradient = class
    867   public
    868     function GetColorAt(position: integer): TBGRAPixel; virtual; abstract;
    869     function GetColorAtF(position: single): TBGRAPixel; virtual;
    870     function GetAverageColor: TBGRAPixel; virtual; abstract;
    871     function GetMonochrome: boolean; virtual; abstract;
    872     property Monochrome: boolean read GetMonochrome;
    873   end;
    874 
    875   { TIntersectionInfo }
    876 
    877   TIntersectionInfo = class
    878     interX: single;
    879     winding: integer;
    880     numSegment: integer;
    881     procedure SetValues(AInterX: Single; AWinding, ANumSegment: integer);
    882   end;
    883   ArrayOfTIntersectionInfo = array of TIntersectionInfo;
    884 
    885   TBGRACustomFillInfo = class
    886     public
    887       //returns true if the same segment number can be curved
    888       function SegmentsCurved: boolean; virtual; abstract;
    889 
    890       //returns integer bounds
    891       function GetBounds: TRect; virtual; abstract;
    892 
    893       //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if
    894       //there is nothing to draw
    895       function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; virtual; abstract;
    896 
    897       //check if the point is inside the filling zone
    898       function IsPointInside(x,y: single; windingMode: boolean): boolean; virtual; abstract;
    899 
    900       //create an array that will contain computed intersections.
    901       //you may augment, in this case, use CreateIntersectionInfo for new items
    902       function CreateIntersectionArray: ArrayOfTIntersectionInfo; virtual; abstract;
    903       function CreateIntersectionInfo: TIntersectionInfo; virtual; abstract; //creates a single info
    904       procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo); virtual; abstract;
    905 
    906       //fill a previously created array of intersections with actual intersections at the current y coordinate.
    907       //nbInter gets the number of computed intersections
    908       procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); virtual; abstract;
    909   end;
    910 
    911   { TBGRACustomFontRenderer }
    912 
    913   TBGRACustomFontRenderer = class
    914     FontName: string;              //Specifies the font to use. Unless the font renderer accept otherwise,
    915                                    //the name is in human readable form, like 'Arial', 'Times New Roman', ...
    916 
    917     FontStyle: TFontStyles;        //Specifies the set of styles to be applied to the font.
    918                                    //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
    919                                    //So the value [fsBold,fsItalic] means that the font must be bold and italic.
    920 
    921     FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem.
    922 
    923     FontOrientation: integer;      //Specifies the rotation of the text, for functions that support text rotation.
    924                                    //It is expressed in tenth of degrees, positive values going counter-clockwise.
    925 
    926     FontEmHeight: integer;         // Specifies the height of the font without taking into account additional line spacing.
    927                                    // A negative value means that it is the full height instead.
    928 
    929     { Returns measurement for the current font in pixels. }
    930     function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
    931 
    932     { Returns the total size of the string provided using the current font.
    933       Orientation is not taken into account, so that the width is along the text.  }
    934     function TextSize(sUTF8: string): TSize; virtual; abstract;
    935 
    936     { Draws the UTF8 encoded string, with color c.
    937       If align is taLeftJustify, (x,y) is the top-left corner.
    938       If align is taCenter, (x,y) is at the top and middle of the text.
    939       If align is taRightJustify, (x,y) is the top-right corner.
    940       The value of FontOrientation is taken into account, so that the text may be rotated. }
    941     procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    942 
    943     { Same as above functions, except that the text is filled using texture.
    944       The value of FontOrientation is taken into account, so that the text may be rotated. }
    945     procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    946 
    947     { Same as above, except that the orientation is specified, overriding the value of the property FontOrientation. }
    948     procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    949     procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    950 
    951     { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect.
    952       Additional style information is provided by the style parameter.
    953       The color c or texture is used to fill the text. No rotation is applied. }
    954     procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract;
    955     procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract;
    956 
    957     { Copy the path for the UTF8 encoded string into ADest.
    958       If align is taLeftJustify, (x,y) is the top-left corner.
    959       If align is taCenter, (x,y) is at the top and middle of the text.
    960       If align is taRightJustify, (x,y) is the top-right corner. }
    961     procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional
    962   end;
    963 
    964 type
    965   TBGRABitmapAny = class of TBGRACustomBitmap;  //used to create instances of the same type (see NewBitmap)
    966   TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
    967 
    968 var
    969   BGRABitmapFactory : TBGRABitmapAny;
    970   BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
    971 
    972 function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean; inline;
    973 
    974 { Color functions }
    975 function GetIntensity(const c: TExpandedPixel): word; inline;
    976 function GetIntensity(c: TBGRAPixel): word; inline;
    977 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
    978 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
    979 function GetLightness(c: TBGRAPixel): word;
    980 function GetLightness(const c: TExpandedPixel): word; inline;
    981 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
    982 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
    983 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel; //if you already know the current lightness of the color
    984 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel; inline;
    985 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
    986 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    987 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;
    988 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
    989 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
    990 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
    991 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
    992 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    993 function GtoH(ghue: word): word;
    994 function HtoG(hue: word): word;
    995 function HueDiff(h1, h2: word): word;
    996 function GetHue(ec: TExpandedPixel): word;
    997 function ColorImportance(ec: TExpandedPixel): word;
    998 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
    999 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
    1000 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    1001 function GammaExpansion(c: TBGRAPixel): TExpandedPixel; inline;
    1002 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel; inline;
    1003 function GammaCompression(red,green,blue,alpha: word): TBGRAPixel; inline;
    1004 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
    1005 function GrayscaleToBGRA(lightness: word): TBGRAPixel;
    1006 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel; overload;
    1007 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel; weight2: byte): TBGRAPixel;
    1008 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel; overload;
    1009 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel; weight2: integer): TBGRAPixel; overload;
    1010 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel; overload;
    1011 function BGRA(red, green, blue, alpha: byte): TBGRAPixel; overload; inline;
    1012 function BGRA(red, green, blue: byte): TBGRAPixel; overload; inline;
    1013 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel; overload; inline;
    1014 function HSLA(hue, saturation, lightness: word): THSLAPixel; overload; inline;
    1015 function ColorToBGRA(color: TColor): TBGRAPixel; overload;
    1016 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
    1017 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
    1018 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
    1019 function BGRAToColor(c: TBGRAPixel): TColor;
    1020 operator = (const c1, c2: TBGRAPixel): boolean; inline;
    1021 function ExpandedDiff(ec1, ec2: TExpandedPixel): word;
    1022 function BGRAWordDiff(c1, c2: TBGRAPixel): word;
    1023 function BGRADiff(c1, c2: TBGRAPixel): byte;
    1024 operator - (const c1, c2: TColorF): TColorF; inline;
    1025 operator + (const c1, c2: TColorF): TColorF; inline;
    1026 operator * (const c1, c2: TColorF): TColorF; inline;
    1027 operator * (const c1: TColorF; factor: single): TColorF; inline;
    1028 function ColorF(red,green,blue,alpha: single): TColorF;
    1029 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
    1030 function StrToBGRA(str: string): TBGRAPixel; //full parse
    1031 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; //full parse with default when error or missing values
    1032 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel; //partial parse allowed
    1033 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    1034 
    1035 { Get height [0..1] stored in a TBGRAPixel }
    1036 function MapHeight(Color: TBGRAPixel): Single;
    1037 
    1038 { Get TBGRAPixel to store height [0..1] }
    1039 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
    1040 
    1041 
    1042 { Gamma conversion arrays. Should be used as readonly }
    1043 var
    1044   // TBGRAPixel -> TExpandedPixel
    1045   GammaExpansionTab:   packed array[0..255] of word;
    1046  
    1047   // TExpandedPixel -> TBGRAPixel
    1048   GammaCompressionTab: packed array[0..65535] of byte;
    1049 
    1050 { Point functions }
    1051 function PointF(x, y: single): TPointF;
    1052 function PointsF(const pts: array of TPointF): ArrayOfTPointF;
    1053 operator = (const pt1, pt2: TPointF): boolean; inline;
    1054 operator - (const pt1, pt2: TPointF): TPointF; inline;
    1055 operator - (const pt2: TPointF): TPointF; inline;
    1056 operator + (const pt1, pt2: TPointF): TPointF; inline;
    1057 operator * (const pt1, pt2: TPointF): single; inline; //scalar product
    1058 operator * (const pt1: TPointF; factor: single): TPointF; inline;
    1059 operator * (factor: single; const pt1: TPointF): TPointF; inline;
    1060 function PtInRect(const pt: TPoint; r: TRect): boolean; overload;
    1061 function RectWithSize(left,top,width,height: integer): TRect;
    1062 function VectLen(dx,dy: single): single; overload;
    1063 function VectLen(v: TPointF): single; overload;
    1064 
    1065 { Line and polygon functions }
    1066 type
    1067     TLineDef = record
    1068        origin, dir: TPointF;
    1069     end;
    1070 
    1071 function IntersectLine(line1, line2: TLineDef): TPointF;
    1072 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;
    1073 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean;
    1074 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    1075 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    1076 
    1077 { Cyclic functions }
    1078 function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
    1079 
    1080 { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
    1081   They use a table to store already computed values. The return value is an integer
    1082   ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is
    1083   32768 instead of 1. The input has a period of 65536, so you can supply any integer
    1084   without applying a modulo. }
    1085 procedure PrecalcSin65536; // compute all values now
    1086 function Sin65536(value: word): Int32or64; inline;
    1087 function Cos65536(value: word): Int32or64; inline;
    1088 function ByteSqrt(value: byte): byte; inline;
    1089 
    1090 function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
    1091 function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
    1092 function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
    1093 function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
    1094 function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     441  {** Detect the file format of a given file }
     442  function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
     443  {** Detect the file format of a given stream. ''ASuggestedExtensionUTF8'' can
     444      be provided to guess the format }
     445  function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
     446  {** Returns the file format that is most likely to be stored in the
     447      given filename (according to its extension) }
     448  function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
     449  {** Returns a likely image extension for the format }
     450  function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
     451  {** Create an image reader for the given format }
     452  function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
     453  {** Create an image writer for the given format. ''AHasTransparentPixels''
     454      specifies if alpha channel must be supported }
     455  function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     456
     457{$DEFINE INCLUDE_INTERFACE}
     458{$I bgracustombitmap.inc}
    1095459
    1096460implementation
    1097461
    1098 uses Math, SysUtils, FileUtil, lazutf8classes, LCLProc,
     462uses Math, SysUtils, BGRAUTF8,
    1099463  FPReadTiff, FPReadXwd, FPReadXPM,
    1100   FPWriteTiff, FPWriteJPEG, FPWritePNG, FPWriteBMP, FPWritePCX,
     464  FPWriteTiff, FPWriteJPEG, BGRAWritePNG, FPWriteBMP, FPWritePCX,
    1101465  FPWriteTGA, FPWriteXPM;
     466
     467{$DEFINE INCLUDE_IMPLEMENTATION}
     468{$I geometrytypes.inc}
     469
     470{$DEFINE INCLUDE_IMPLEMENTATION}
     471{$I csscolorconst.inc}
     472
     473{$DEFINE INCLUDE_IMPLEMENTATION}
     474{$I bgracustombitmap.inc}
     475
     476{$DEFINE INCLUDE_IMPLEMENTATION}
     477{$I bgrapixel.inc}
     478
     479function CleanTextOutString(s: string): string;
     480var idxIn, idxOut: integer;
     481begin
     482  setlength(result, length(s));
     483  idxIn := 1;
     484  idxOut := 1;
     485  while IdxIn <= length(s) do
     486  begin
     487    if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8
     488    begin
     489      result[idxOut] := s[idxIn];
     490      inc(idxOut);
     491    end;
     492    inc(idxIn);
     493  end;
     494  setlength(result, idxOut-1);
     495end;
     496
     497function RemoveLineEnding(var s: string; indexByte: integer): boolean;
     498begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long
     499      //so this function can be applied to UTF8 strings as well
     500  result := false;
     501  if length(s) >= indexByte then
     502  begin
     503    if s[indexByte] in[#13,#10] then
     504    begin
     505      result := true;
     506      if length(s) >= indexByte+1 then
     507      begin
     508        if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then
     509          delete(s,indexByte,2)
     510        else
     511          delete(s,indexByte,1);
     512      end
     513        else
     514          delete(s,indexByte,1);
     515    end;
     516  end;
     517end;
     518
     519function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     520var indexByte: integer;
     521    pIndex: PChar;
     522begin
     523  pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8);
     524  if pIndex = nil then
     525  begin
     526    result := false;
     527    exit;
     528  end;
     529  indexByte := pIndex - @sUTF8[1];
     530  result := RemoveLineEnding(sUTF8, indexByte);
     531end;
     532
     533procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
     534var p: integer;
     535begin
     536  if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then
     537  begin
     538    p := length(ABefore);
     539    while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);
     540    if p > 1 then //can put the word after
     541    begin
     542      AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
     543      ABefore := copy(ABefore,1,p-1);
     544    end else
     545    begin //cannot put the word after, so before
     546
     547    end;
     548  end;
     549  while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);
     550  while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);
     551end;
     552
    1102553
    1103554function StrToResampleFilter(str: string): TResampleFilter;
     
    1114565end;
    1115566
    1116 function StrToBlendOperation(str: string): TBlendOperation;
    1117 var op: TBlendOperation;
    1118 begin
    1119   result := boTransparent;
    1120   str := LowerCase(str);
    1121   for op := low(TBlendOperation) to high(TBlendOperation) do
    1122     if str = LowerCase(BlendOperationStr[op]) then
    1123     begin
    1124       result := op;
    1125       exit;
    1126     end;
    1127 end;
    1128 
    1129 function StrToGradientType(str: string): TGradientType;
    1130 var gt: TGradientType;
    1131 begin
    1132   result := gtLinear;
    1133   str := LowerCase(str);
    1134   for gt := low(TGradientType) to high(TGradientType) do
    1135     if str = LowerCase(GradientTypeStr[gt]) then
    1136     begin
    1137       result := gt;
    1138       exit;
    1139     end;
    1140 end;
    1141 
    1142 { Make a pen style. Need an even number of values. See TBGRAPenStyle }
    1143 function BGRAPenStyle(dash1, space1: single; dash2: single; space2: single;
    1144   dash3: single; space3: single; dash4: single; space4: single): TBGRAPenStyle;
    1145 var
    1146   i: Integer;
    1147 begin
    1148   if dash4 <> 0 then
    1149   begin
    1150     setlength(result,8);
    1151     result[6] := dash4;
    1152     result[7] := space4;
    1153     result[4] := dash3;
    1154     result[5] := space3;
    1155     result[2] := dash2;
    1156     result[3] := space2;
    1157   end else
    1158   if dash3 <> 0 then
    1159   begin
    1160     setlength(result,6);
    1161     result[4] := dash3;
    1162     result[5] := space3;
    1163     result[2] := dash2;
    1164     result[3] := space2;
    1165   end else
    1166   if dash2 <> 0 then
    1167   begin
    1168     setlength(result,4);
    1169     result[2] := dash2;
    1170     result[3] := space2;
    1171   end else
    1172   begin
    1173     setlength(result,2);
    1174   end;
    1175   result[0] := dash1;
    1176   result[1] := space1;
    1177   for i := 0 to high(result) do
    1178     if result[i]=0 then
    1179       raise exception.Create('Zero is not a valid value');
    1180 end;
    1181 
    1182 { Bézier curves definitions. See : http://en.wikipedia.org/wiki/B%C3%A9zier_curve }
    1183 
    1184 function ConcatPointsF(const APolylines: array of ArrayOfTPointF
    1185   ): ArrayOfTPointF;
    1186 var
    1187   i,pos,count:integer;
    1188   j: Integer;
    1189 begin
    1190   count := 0;
    1191   for i := 0 to high(APolylines) do
    1192     inc(count,length(APolylines[i]));
    1193   setlength(result,count);
    1194   pos := 0;
    1195   for i := 0 to high(APolylines) do
    1196     for j := 0 to high(APolylines[i]) do
    1197     begin
    1198       result[pos] := APolylines[i][j];
    1199       inc(pos);
    1200     end;
    1201 end;
    1202 
    1203 operator-(const v: TPoint3D): TPoint3D;
    1204 begin
    1205   result.x := -v.x;
    1206   result.y := -v.y;
    1207   result.z := -v.z;
    1208 end;
    1209 
    1210 operator + (const v1,v2: TPoint3D): TPoint3D; inline;
    1211 begin
    1212   result.x := v1.x+v2.x;
    1213   result.y := v1.y+v2.y;
    1214   result.z := v1.z+v2.z;
    1215 end;
    1216 
    1217 operator - (const v1,v2: TPoint3D): TPoint3D; inline;
    1218 begin
    1219   result.x := v1.x-v2.x;
    1220   result.y := v1.y-v2.y;
    1221   result.z := v1.z-v2.z;
    1222 end;
    1223 
    1224 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline;
    1225 begin
    1226   result.x := v1.x*factor;
    1227   result.y := v1.y*factor;
    1228   result.z := v1.z*factor;
    1229 end;
    1230 
    1231 function Point3D(x, y, z: single): TPoint3D;
    1232 begin
    1233   result.x := x;
    1234   result.y := y;
    1235   result.z := z;
    1236 end;
    1237 
    1238 operator=(const v1, v2: TPoint3D): boolean;
    1239 begin
    1240   result := (v1.x=v2.x) and (v1.y=v2.y) and (v1.z=v2.z);
    1241 end;
    1242 
    1243 operator * (const v1,v2: TPoint3D): single; inline;
    1244 begin
    1245   result := v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
    1246 end;
    1247 
    1248 procedure Normalize3D(var v: TPoint3D); inline;
    1249 var len: double;
    1250 begin
    1251   len := v*v;
    1252   if len = 0 then exit;
    1253   len := sqrt(len);
    1254   v.x /= len;
    1255   v.y /= len;
    1256   v.z /= len;
    1257 end;
    1258 
    1259 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D);
    1260 begin
    1261   w.x := u.y*v.z-u.z*v.y;
    1262   w.y := u.z*v.x-u.x*v.z;
    1263   w.z := u.x*v.Y-u.y*v.x;
    1264 end;
    1265 
    1266 // Define a Bézier curve with two control points.
    1267 function BezierCurve(origin, control1, control2, destination: TPointF): TCubicBezierCurve;
    1268 begin
    1269   result.p1 := origin;
    1270   result.c1 := control1;
    1271   result.c2 := control2;
    1272   result.p2 := destination;
    1273 end;
    1274 
    1275 // Define a Bézier curve with one control point.
    1276 function BezierCurve(origin, control, destination: TPointF
    1277   ): TQuadraticBezierCurve;
    1278 begin
    1279   result.p1 := origin;
    1280   result.c := control;
    1281   result.p2 := destination;
    1282 end;
    1283 
    1284 //straight line
    1285 function BezierCurve(origin, destination: TPointF): TQuadraticBezierCurve;
    1286 begin
    1287   result.p1 := origin;
    1288   result.c := (origin+destination)*0.5;
    1289   result.p2 := destination;
    1290 end;
    1291 
    1292 function ArcDef(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
    1293   anticlockwise: boolean): TArcDef;
    1294 begin
    1295   result.center := PointF(cx,cy);
    1296   result.radius := PointF(rx,ry);
    1297   result.xAngleRadCW:= xAngleRadCW;
    1298   result.startAngleRadCW := startAngleRadCW;
    1299   result.endAngleRadCW:= endAngleRadCW;
    1300   result.anticlockwise:= anticlockwise;
    1301 end;
    1302 
    1303 { Check if a PointF structure is empty or should be treated as a list separator }
    1304 function isEmptyPointF(pt: TPointF): boolean;
    1305 begin
    1306   Result := (pt.x = EmptySingle) and (pt.y = EmptySingle);
    1307 end;
    1308 
    1309567{ TBGRACustomFontRenderer }
    1310568
    1311569procedure TBGRACustomFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
    1312 begin
    1313 end;
    1314 
    1315 { TIntersectionInfo }
    1316 
    1317 procedure TIntersectionInfo.SetValues(AInterX: Single; AWinding,
    1318   ANumSegment: integer);
    1319 begin
    1320   interX := AInterX;
    1321   winding := AWinding;
    1322   numSegment := ANumSegment;
    1323 end;
    1324 
    1325 { TBGRACustomGradient }
    1326 
    1327 function TBGRACustomGradient.GetColorAtF(position: single): TBGRAPixel;
    1328 begin
    1329   position *= 65536;
    1330   if position < low(integer) then
    1331     result := GetColorAt(low(Integer))
    1332   else if position > high(integer) then
    1333     result := GetColorAt(high(Integer))
    1334   else
    1335     result := GetColorAt(round(position));
    1336 end;
    1337 
    1338 { TBGRAColorList }
    1339 
    1340 function TBGRAColorList.GetByIndex(Index: integer): TBGRAPixel;
    1341 begin
    1342   if (Index < 0) or (Index >= FNbColors) then
    1343     result := BGRAPixelTransparent
    1344   else
    1345     result := FColors[Index].Color;
    1346 end;
    1347 
    1348 function TBGRAColorList.GetByName(Name: string): TBGRAPixel;
    1349 var i: integer;
    1350 begin
    1351   i := IndexOf(Name);
    1352   if i = -1 then
    1353     result := BGRAPixelTransparent
    1354   else
    1355     result := FColors[i].Color;
    1356 end;
    1357 
    1358 function TBGRAColorList.GetName(Index: integer): string;
    1359 begin
    1360   if (Index < 0) or (Index >= FNbColors) then
    1361     result := ''
    1362   else
    1363     result := FColors[Index].Name;
    1364 end;
    1365 
    1366 constructor TBGRAColorList.Create;
    1367 begin
    1368   FNbColors:= 0;
    1369   FColors := nil;
    1370   FFinished:= false;
    1371 end;
    1372 
    1373 procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel);
    1374 begin
    1375   if FFinished then
    1376     raise Exception.Create('This list is already finished');
    1377   if length(FColors) = FNbColors then
    1378     SetLength(FColors, FNbColors*2+1);
    1379   FColors[FNbColors].Name := Name;
    1380   FColors[FNbColors].Color := Color;
    1381   inc(FNbColors);
    1382 end;
    1383 
    1384 procedure TBGRAColorList.Finished;
    1385 begin
    1386   if FFinished then exit;
    1387   FFinished := true;
    1388   SetLength(FColors, FNbColors);
    1389 end;
    1390 
    1391 function TBGRAColorList.IndexOf(Name: string): integer;
    1392 var i: integer;
    1393 begin
    1394   for i := 0 to FNbColors-1 do
    1395     if CompareText(Name, FColors[i].Name) = 0 then
    1396     begin
    1397       result := i;
    1398       exit;
    1399     end;
    1400   result := -1;
    1401 end;
    1402 
    1403 function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
    1404 var i: integer;
    1405   MinDiff,CurDiff: Word;
    1406 begin
    1407   if AMaxDiff = 0 then
    1408   begin
    1409     for i := 0 to FNbColors-1 do
    1410       if AColor = FColors[i].Color then
    1411       begin
    1412         result := i;
    1413         exit;
    1414       end;
    1415     result := -1;
    1416   end else
    1417   begin
    1418     MinDiff := AMaxDiff;
    1419     result := -1;
    1420     for i := 0 to FNbColors-1 do
    1421     begin
    1422       CurDiff := BGRAWordDiff(AColor,FColors[i].Color);
    1423       if CurDiff <= MinDiff then
    1424       begin
    1425         result := i;
    1426         MinDiff := CurDiff;
    1427         if MinDiff = 0 then exit;
    1428       end;
    1429     end;
    1430   end;
    1431 end;
    1432 
    1433 { TBGRACustomBitmap }
    1434 
    1435 function TBGRACustomBitmap.GetFontAntialias: Boolean;
    1436 begin
    1437   result := FontQuality <> fqSystem;
    1438 end;
    1439 
    1440 procedure TBGRACustomBitmap.SetFontAntialias(const AValue: Boolean);
    1441 begin
    1442   if AValue and not FontAntialias then
    1443     FontQuality := fqFineAntialiasing
    1444   else if not AValue and (FontQuality <> fqSystem) then
    1445     FontQuality := fqSystem;
    1446 end;
    1447 
    1448 { These declaration make sure that these methods are virtual }
    1449 procedure TBGRACustomBitmap.LoadFromFile(const filename: string);
    1450 begin
    1451   LoadFromFileUTF8(SysToUtf8(filename));
    1452 end;
    1453 
    1454 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string);
    1455 var
    1456   Stream: TStream;
    1457   format: TBGRAImageFormat;
    1458   reader: TFPCustomImageReader;
    1459 begin
    1460   stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
    1461   try
    1462     format := DetectFileFormat(Stream, ExtractFileExt(filenameUTF8));
    1463     reader := CreateBGRAImageReader(format);
    1464     try
    1465       LoadFromStream(stream, reader);
    1466     finally
    1467       reader.Free;
    1468     end;
    1469   finally
    1470     ClearTransparentPixels;
    1471     stream.Free;
    1472   end;
    1473 end;
    1474 
    1475 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string;
    1476   AHandler: TFPCustomImageReader);
    1477 var
    1478   Stream: TStream;
    1479 begin
    1480   stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
    1481   try
    1482     LoadFromStream(stream, AHandler);
    1483   finally
    1484     ClearTransparentPixels;
    1485     stream.Free;
    1486   end;
    1487 end;
    1488 
    1489 procedure TBGRACustomBitmap.SaveToFile(const filename: string);
    1490 begin
    1491   SaveToFileUTF8(SysToUtf8(filename));
    1492 end;
    1493 
    1494 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string);
    1495 var
    1496   writer: TFPCustomImageWriter;
    1497   format: TBGRAImageFormat;
    1498 begin
    1499   format := SuggestImageFormat(filenameUTF8);
    1500   writer := CreateBGRAImageWriter(Format, HasTransparentPixels);
    1501   try
    1502     SaveToFileUTF8(filenameUTF8, writer);
    1503   finally
    1504     writer.free;
    1505   end;
    1506 end;
    1507 
    1508 procedure TBGRACustomBitmap.SaveToFile(const filename: string;
    1509   Handler: TFPCustomImageWriter);
    1510 begin
    1511   SaveToFileUTF8(SysToUtf8(filename),Handler);
    1512 end;
    1513 
    1514 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string;
    1515   Handler: TFPCustomImageWriter);
    1516 var
    1517   stream: TFileStreamUTF8;
    1518 begin
    1519    stream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
    1520    try
    1521      SaveToStream(stream, Handler);
    1522    finally
    1523      stream.Free;
    1524    end;
    1525 end;
    1526 
    1527 procedure TBGRACustomBitmap.SaveToStreamAs(Str: TStream;
    1528   AFormat: TBGRAImageFormat);
    1529 var handler: TFPCustomImageWriter;
    1530 begin
    1531   handler := CreateBGRAImageWriter(AFormat, HasTransparentPixels);
    1532   try
    1533     SaveToStream(Str, handler)
    1534   finally
    1535     handler.Free;
    1536   end;
    1537 end;
    1538 
    1539 procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel;
    1540   ADrawMode: TDrawMode);
    1541 begin
    1542   case ADrawMode of
    1543   dmSet: SetPixel(x,y,c);
    1544   dmSetExceptTransparent: if c.alpha = 255 then SetPixel(x,y,c);
    1545   dmLinearBlend: FastBlendPixel(x,y,c);
    1546   dmDrawWithTransparency: DrawPixel(x,y,c);
    1547   dmXor: XorPixel(x,y,c);
    1548   end;
    1549 end;
    1550 
    1551 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream);
    1552 var
    1553   format: TBGRAImageFormat;
    1554   reader: TFPCustomImageReader;
    1555 begin
    1556   format := DetectFileFormat(Str);
    1557   reader := CreateBGRAImageReader(format);
    1558   try
    1559     LoadFromStream(Str,reader);
    1560   finally
    1561     reader.Free;
    1562   end;
    1563 end;
    1564 
    1565 { LoadFromStream uses TFPCustomImage routine, which uses
    1566   Colors property to access pixels. That's why the
    1567   FP drawing mode is temporarily changed to load
    1568   bitmaps properly }
    1569 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream;
    1570   Handler: TFPCustomImageReader);
    1571 var
    1572   OldDrawMode: TDrawMode;
    1573 begin
    1574   OldDrawMode := CanvasDrawModeFP;
    1575   CanvasDrawModeFP := dmSet;
    1576   try
    1577     inherited LoadFromStream(Str, Handler);
    1578   finally
    1579     CanvasDrawModeFP := OldDrawMode;
    1580   end;
    1581 end;
    1582 
    1583 { Look for a pixel considering the bitmap is repeated in both directions }
    1584 function TBGRACustomBitmap.GetPixelCycle(x, y: int32or64): TBGRAPixel;
    1585 begin
    1586   if (Width = 0) or (Height = 0) then
    1587     Result := BGRAPixelTransparent
    1588   else
    1589     Result := (Scanline[PositiveMod(y,Height)] + PositiveMod(x,Width))^;
    1590 end;
    1591 
    1592 procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64;
    1593   texture: IBGRAScanner);
    1594 begin
    1595   HorizLine(x,y,x2,texture,dmDrawWithTransparency);
    1596 end;
    1597 
    1598 procedure TBGRACustomBitmap.HorizLine(x, y, x2: Int32or64; c: TBGRAPixel;
    1599   ADrawMode: TDrawMode);
    1600 begin
    1601   case ADrawMode of
    1602     dmSet: SetHorizLine(x,y,x2,c);
    1603     dmSetExceptTransparent: if c.alpha = 255 then SetHorizLine(x,y,x2,c);
    1604     dmXor: XorHorizLine(x,y,x2,c);
    1605     dmLinearBlend: FastBlendHorizLine(x,y,x2,c);
    1606     dmDrawWithTransparency: DrawHorizLine(x,y,x2,c);
    1607   end;
    1608 end;
    1609 
    1610 procedure TBGRACustomBitmap.VertLine(x, y, y2: Int32or64; c: TBGRAPixel;
    1611   ADrawMode: TDrawMode);
    1612 begin
    1613   case ADrawMode of
    1614     dmSet: SetVertLine(x,y,y2,c);
    1615     dmSetExceptTransparent: if c.alpha = 255 then SetVertLine(x,y,y2,c);
    1616     dmXor: XorVertLine(x,y,y2,c);
    1617     dmLinearBlend: FastBlendVertLine(x,y,y2,c);
    1618     dmDrawWithTransparency: DrawVertLine(x,y,y2,c);
    1619   end;
    1620 end;
    1621 
    1622 procedure TBGRACustomBitmap.ArrowStartAsNone;
    1623 begin
    1624   SetArrowStart(asNone);
    1625 end;
    1626 
    1627 procedure TBGRACustomBitmap.ArrowStartAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
    1628 var join: TPenJoinStyle;
    1629 begin
    1630   if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
    1631   if ACut then
    1632   begin
    1633     if AFlipped then
    1634       SetArrowStart(asFlippedCut,join,ARelativePenWidth)
    1635     else
    1636       SetArrowStart(asCut,join,ARelativePenWidth)
    1637   end
    1638   else
    1639   begin
    1640     if AFlipped then
    1641       SetArrowStart(asFlipped,join,ARelativePenWidth)
    1642     else
    1643       SetArrowStart(asNormal,join,ARelativePenWidth)
    1644   end;
    1645 end;
    1646 
    1647 procedure TBGRACustomBitmap.ArrowStartAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
    1648   AHollowPenWidth: single);
    1649 var join: TPenJoinStyle;
    1650 begin
    1651   if ARounded then join := pjsRound else join := pjsMiter;
    1652   if AHollow then
    1653     SetArrowStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
    1654   else
    1655     SetArrowStart(asTriangle, join,1,ABackOffset);
    1656 end;
    1657 
    1658 procedure TBGRACustomBitmap.ArrowStartAsTail;
    1659 begin
    1660   SetArrowStart(asTail);
    1661 end;
    1662 
    1663 procedure TBGRACustomBitmap.ArrowEndAsNone;
    1664 begin
    1665   SetArrowEnd(asNone);
    1666 end;
    1667 
    1668 procedure TBGRACustomBitmap.ArrowEndAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
    1669 var join: TPenJoinStyle;
    1670 begin
    1671   if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
    1672   if ACut then
    1673   begin
    1674     if AFlipped then
    1675       SetArrowEnd(asFlippedCut,join,ARelativePenWidth)
    1676     else
    1677       SetArrowEnd(asCut,join,ARelativePenWidth)
    1678   end
    1679   else
    1680   begin
    1681     if AFlipped then
    1682       SetArrowEnd(asFlipped,join,ARelativePenWidth)
    1683     else
    1684       SetArrowEnd(asNormal,join,ARelativePenWidth)
    1685   end;
    1686 end;
    1687 
    1688 procedure TBGRACustomBitmap.ArrowEndAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
    1689   AHollowPenWidth: single);
    1690 var join: TPenJoinStyle;
    1691 begin
    1692   if ARounded then join := pjsRound else join := pjsMiter;
    1693   if AHollow then
    1694     SetArrowEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
    1695   else
    1696     SetArrowEnd(asTriangle, join,1, ABackOffset);
    1697 end;
    1698 
    1699 procedure TBGRACustomBitmap.ArrowEndAsTail;
    1700 begin
    1701   SetArrowEnd(asTail);
    1702 end;
    1703 
    1704 procedure TBGRACustomBitmap.DrawPolyLine(const points: array of TPoint;
    1705   c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
    1706 var i: integer;
    1707 begin
    1708    if length(points) = 1 then
    1709    begin
    1710      if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c,ADrawMode);
    1711    end
    1712    else
    1713      for i := 0 to high(points)-1 do
    1714        DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1),ADrawMode);
    1715 end;
    1716 
    1717 { Pixel polylines are constructed by concatenation }
    1718 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint;
    1719   c: TBGRAPixel; DrawLastPixel: boolean);
    1720 var i: integer;
    1721 begin
    1722    if length(points) = 1 then
    1723    begin
    1724      if DrawLastPixel then DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
    1725    end
    1726    else
    1727      for i := 0 to high(points)-1 do
    1728        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1));
    1729 end;
    1730 
    1731 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint; c1,
    1732   c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean);
    1733 var i: integer;
    1734   DashPos: integer;
    1735 begin
    1736    DashPos := 0;
    1737    if length(points) = 1 then
    1738    begin
    1739      if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c1);
    1740    end
    1741    else
    1742      for i := 0 to high(points)-1 do
    1743        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c1,c2,dashLen,DrawLastPixel and (i=high(points)-1),DashPos);
    1744 end;
    1745 
    1746 procedure TBGRACustomBitmap.DrawPolygon(const points: array of TPoint;
    1747   c: TBGRAPixel; ADrawMode: TDrawMode);
    1748 var i: integer;
    1749 begin
    1750    if length(points) = 1 then
    1751    begin
    1752      DrawPixel(points[0].x,points[0].y,c,ADrawMode);
    1753    end
    1754    else
    1755    begin
    1756      for i := 0 to high(points)-1 do
    1757        DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false,ADrawMode);
    1758      DrawLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false,ADrawMode);
    1759    end;
    1760 end;
    1761 
    1762 procedure TBGRACustomBitmap.DrawPolygonAntialias(const points: array of TPoint;
    1763   c: TBGRAPixel);
    1764 var i: integer;
    1765 begin
    1766    if length(points) = 1 then
    1767    begin
    1768      DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
    1769    end
    1770    else
    1771    begin
    1772      for i := 0 to high(points)-1 do
    1773        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false);
    1774      DrawLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false);
    1775    end;
    1776 end;
    1777 
    1778 procedure TBGRACustomBitmap.ErasePolyLine(const points: array of TPoint; alpha: byte;
    1779   DrawLastPixel: boolean);
    1780 var i: integer;
    1781 begin
    1782    if length(points) = 1 then
    1783    begin
    1784      if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
    1785    end
    1786    else
    1787      for i := 0 to high(points)-1 do
    1788        EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
    1789 end;
    1790 
    1791 procedure TBGRACustomBitmap.ErasePolyLineAntialias(
    1792   const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
    1793 var i: integer;
    1794 begin
    1795    if length(points) = 1 then
    1796    begin
    1797      if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
    1798    end
    1799    else
    1800      for i := 0 to high(points)-1 do
    1801        EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
    1802 end;
    1803 
    1804 procedure TBGRACustomBitmap.ErasePolygonOutline(const points: array of TPoint;
    1805   alpha: byte);
    1806 var i: integer;
    1807 begin
    1808    if length(points) = 1 then
    1809    begin
    1810      ErasePixel(points[0].x,points[0].y,alpha);
    1811    end
    1812    else
    1813    begin
    1814      for i := 0 to high(points)-1 do
    1815        EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
    1816      EraseLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
    1817    end;
    1818 end;
    1819 
    1820 procedure TBGRACustomBitmap.ErasePolygonOutlineAntialias(
    1821   const points: array of TPoint; alpha: byte);
    1822 var i: integer;
    1823 begin
    1824    if length(points) = 1 then
    1825    begin
    1826      ErasePixel(points[0].x,points[0].y,alpha);
    1827    end
    1828    else
    1829    begin
    1830      for i := 0 to high(points)-1 do
    1831        EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
    1832      EraseLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
    1833    end;
    1834 end;
    1835 
    1836 { Following functions are defined for convenience }
    1837 procedure TBGRACustomBitmap.Rectangle(x, y, x2, y2: integer; c: TColor);
    1838 begin
    1839   Rectangle(x, y, x2, y2, ColorToBGRA(c), dmSet);
    1840 end;
    1841 
    1842 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode
    1843   );
    1844 begin
    1845   Rectangle(r.left, r.top, r.right, r.bottom, c, mode);
    1846 end;
    1847 
    1848 procedure TBGRACustomBitmap.Rectangle(r: TRect; BorderColor,
    1849   FillColor: TBGRAPixel; mode: TDrawMode);
    1850 begin
    1851   Rectangle(r.left, r.top, r.right, r.bottom, BorderColor, FillColor, mode);
    1852 end;
    1853 
    1854 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TColor);
    1855 begin
    1856   Rectangle(r.left, r.top, r.right, r.bottom, c);
    1857 end;
    1858 
    1859 procedure TBGRACustomBitmap.RectangleAntialias(x, y, x2, y2: single;
    1860   c: TBGRAPixel; w: single);
    1861 begin
    1862   RectangleAntialias(x, y, x2, y2, c, w, BGRAPixelTransparent);
    1863 end;
    1864 
    1865 procedure TBGRACustomBitmap.FillRoundRect(X1, Y1, X2, Y2: integer; DX,
    1866   DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode);
    1867 begin
    1868   RoundRect(X1,Y1,X2,Y2,DX,DY,FillColor,FillColor,ADrawMode);
    1869 end;
    1870 
    1871 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor: TBGRAPixel;
    1872   ADrawMode: TDrawMode);
    1873 begin
    1874   RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,ADrawMode);
    1875 end;
    1876 
    1877 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor,
    1878   FillColor: TBGRAPixel; ADrawMode: TDrawMode);
    1879 begin
    1880   RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,FillColor,ADrawMode);
    1881 end;
    1882 
    1883 procedure TBGRACustomBitmap.FillEllipseInRect(r: TRect; FillColor: TBGRAPixel;
    1884   ADrawMode: TDrawMode);
    1885 begin
    1886   FillRoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),FillColor,ADrawMode);
    1887 end;
    1888 
    1889 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TColor);
    1890 begin
    1891   FillRect(r.Left, r.top, r.right, r.bottom, c);
    1892 end;
    1893 
    1894 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode);
    1895 begin
    1896   FillRect(r.Left, r.top, r.right, r.bottom, c, mode);
    1897 end;
    1898 
    1899 procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
    1900   mode: TDrawMode);
    1901 begin
    1902   FillRect(r.Left, r.top, r.right, r.bottom, texture, mode);
    1903 end;
    1904 
    1905 procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer; c: TColor);
    1906 begin
    1907   FillRect(x, y, x2, y2, ColorToBGRA(c), dmSet);
    1908 end;
    1909 
    1910 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
    1911   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1912 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TBGRAPixel);
    1913 begin
    1914   TextOut(x, y, sUTF8, c, taLeftJustify);
    1915 end;
    1916 
    1917 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
    1918   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1919 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TColor);
    1920 begin
    1921   TextOut(x, y, sUTF8, ColorToBGRA(c));
    1922 end;
    1923 
    1924 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The texture is used to fill the text.
    1925   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1926 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string;
    1927   texture: IBGRAScanner);
    1928 begin
    1929   TextOut(x, y, sUTF8, texture, taLeftJustify);
    1930 end;
    1931 
    1932 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    1933   The position depends on the specified horizontal alignment halign and vertical alignement valign.
    1934   The color c is used to fill the text. No rotation is applied. }
    1935 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    1936   halign: TAlignment; valign: TTextLayout; c: TBGRAPixel);
    1937 var
    1938   style: TTextStyle;
    1939 begin
    1940   {$hints off}
    1941   FillChar(style,sizeof(style),0);
    1942   {$hints on}
    1943   style.Alignment := halign;
    1944   style.Layout := valign;
    1945   style.Wordbreak := true;
    1946   style.ShowPrefix := false;
    1947   style.Clipping := false;
    1948   TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,c);
    1949 end;
    1950 
    1951 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    1952   The position depends on the specified horizontal alignment halign and vertical alignement valign.
    1953   The texture is used to fill the text. No rotation is applied. }
    1954 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    1955   halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner);
    1956 var
    1957   style: TTextStyle;
    1958 begin
    1959   {$hints off}
    1960   FillChar(style,sizeof(style),0);
    1961   {$hints on}
    1962   style.Alignment := halign;
    1963   style.Layout := valign;
    1964   style.Wordbreak := true;
    1965   style.ShowPrefix := false;
    1966   style.Clipping := false;
    1967   TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,texture);
    1968 end;
    1969 
    1970 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry: single): ArrayOfTPointF;
    1971 begin
    1972   result := ComputeEllipseContour(x,y,rx,ry);
    1973 end;
    1974 
    1975 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry, w: single
    1976   ): ArrayOfTPointF;
    1977 begin
    1978   result := ComputeEllipseBorder(x,y,rx,ry,w);
    1979 end;
    1980 
    1981 procedure TBGRACustomBitmap.FillTransparent;
    1982 begin
    1983   Fill(BGRAPixelTransparent);
    1984 end;
    1985 
    1986 procedure TBGRACustomBitmap.Fill(c: TColor);
    1987 begin
    1988   Fill(ColorToBGRA(c));
    1989 end;
    1990 
    1991 procedure TBGRACustomBitmap.Fill(c: TBGRAPixel);
    1992 begin
    1993   Fill(c, 0, NbPixels);
    1994 end;
    1995 
    1996 procedure TBGRACustomBitmap.AlphaFill(alpha: byte);
    1997 begin
    1998   AlphaFill(alpha, 0, NbPixels);
    1999 end;
    2000 
    2001 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    2002   color: TBGRAPixel);
    2003 begin
    2004   FillMask(x,y, AMask, color, dmDrawWithTransparency);
    2005 end;
    2006 
    2007 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    2008   texture: IBGRAScanner);
    2009 begin
    2010   FillMask(x,y, AMask, texture, dmDrawWithTransparency);
    2011 end;
    2012 
    2013 procedure TBGRACustomBitmap.FloodFill(X, Y: integer; Color: TBGRAPixel;
    2014   mode: TFloodfillMode; Tolerance: byte);
    2015 begin
    2016   ParallelFloodFill(X,Y,Self,Color,mode,Tolerance);
    2017 end;
    2018 
    2019 procedure TBGRACustomBitmap.DrawPart(ARect: TRect; Canvas: TCanvas; x,
    2020   y: integer; Opaque: boolean);
    2021 var
    2022   partial: TBGRACustomBitmap;
    2023 begin
    2024   partial := GetPart(ARect);
    2025   if partial <> nil then
    2026   begin
    2027     partial.Draw(Canvas, x, y, Opaque);
    2028     partial.Free;
    2029   end;
    2030 end;
    2031 
    2032 procedure TBGRACustomBitmap.PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
    2033 begin
    2034   PutImageAngle(x,y,source,0);
    2035 end;
    2036 
    2037 procedure TBGRACustomBitmap.PutImagePart(x, y: integer;
    2038   Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte);
    2039 var w,h,sourcex,sourcey,nx,ny,xb,yb,destx,desty: integer;
    2040     oldClip,newClip: TRect;
    2041 begin
    2042   if (Source = nil) or (AOpacity = 0) then exit;
    2043   w := SourceRect.Right-SourceRect.Left;
    2044   h := SourceRect.Bottom-SourceRect.Top;
    2045   if (w <= 0) or (h <= 0) or (Source.Width = 0) or (Source.Height = 0) then exit;
    2046   sourcex := PositiveMod(SourceRect.Left, Source.Width);
    2047   sourcey := PositiveMod(SourceRect.Top, Source.Height);
    2048   nx := (sourceX+w + Source.Width-1) div Source.Width;
    2049   ny := (sourceY+h + Source.Height-1) div Source.Height;
    2050 
    2051   oldClip := ClipRect;
    2052   newClip := rect(x,y,x+w,y+h);
    2053   if not IntersectRect(newClip,newClip,oldClip) then exit;
    2054 
    2055   ClipRect := newClip;
    2056 
    2057   desty := y-sourcey;
    2058   for yb := 0 to ny-1 do
    2059   begin
    2060     destx := x-sourcex;
    2061     for xb := 0 to nx-1 do
    2062     begin
    2063       self.PutImage(destx,desty,Source,mode,AOpacity);
    2064       inc(destx,Source.Width);
    2065     end;
    2066     inc(desty,Source.Height);
    2067   end;
    2068 
    2069   ClipRect := oldClip;
    2070 end;
    2071 
    2072 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2073   Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean);
    2074 begin
    2075   if ACorrectBlur then
    2076     PutImageAffine(Origin,HAxis,VAxis,Source,rfCosine,AOpacity)
    2077   else
    2078     PutImageAffine(Origin,HAxis,VAxis,Source,rfLinear,AOpacity);
    2079 end;
    2080 
    2081 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2082   Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte);
    2083 var outputBounds: TRect;
    2084 begin
    2085   if (Source = nil) or (AOpacity = 0) then exit;
    2086   if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    2087      (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    2088      (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    2089   begin
    2090     PutImage(round(origin.x),round(origin.y),Source,dmDrawWithTransparency,AOpacity);
    2091     exit;
    2092   end;
    2093   outputBounds := GetImageAffineBounds(Origin,HAxis,VAxis,Source);
    2094   PutImageAffine(Origin,HAxis,VAxis,Source,outputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
    2095 end;
    2096 
    2097 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2098   Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte;
    2099   ACorrectBlur: Boolean);
    2100 begin
    2101   if ACorrectBlur then
    2102     PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfCosine,dmDrawWithTransparency, AOpacity)
    2103   else
    2104     PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfLinear,dmDrawWithTransparency,AOpacity);
    2105 end;
    2106 
    2107 { Returns the area that contains the affine transformed image }
    2108 function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
    2109   Source: TBGRACustomBitmap): TRect;
    2110 var minx,miny,maxx,maxy: integer;
    2111     vx,vy,pt1: TPointF;
    2112     sourceBounds: TRect;
    2113 
    2114   //include specified point in the bounds
    2115   procedure Include(pt: TPointF);
    2116   begin
    2117     if floor(pt.X) < minx then minx := floor(pt.X);
    2118     if floor(pt.Y) < miny then miny := floor(pt.Y);
    2119     if ceil(pt.X) > maxx then maxx := ceil(pt.X);
    2120     if ceil(pt.Y) > maxy then maxy := ceil(pt.Y);
    2121   end;
    2122 
    2123 begin
    2124   result := EmptyRect;
    2125   if (Source = nil) then exit;
    2126   sourceBounds := source.GetImageBounds;
    2127   if IsRectEmpty(sourceBounds) then exit;
    2128 
    2129   if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    2130      (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    2131      (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    2132   begin
    2133     result := sourceBounds;
    2134     OffsetRect(result,round(origin.x),round(origin.y));
    2135     IntersectRect(result,result,ClipRect);
    2136     exit;
    2137   end;
    2138 
    2139   { Compute bounds }
    2140   vx := (HAxis-Origin)*(1/source.Width);
    2141   vy := (VAxis-Origin)*(1/source.Height);
    2142   pt1 := Origin+vx*sourceBounds.Left+vy*sourceBounds.Top;
    2143   minx := floor(pt1.X);
    2144   miny := floor(pt1.Y);
    2145   maxx := ceil(pt1.X);
    2146   maxy := ceil(pt1.Y);
    2147   Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Top);
    2148   Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Bottom);
    2149   Include(Origin+vx*sourceBounds.Left+vy*sourceBounds.Bottom);
    2150 
    2151   result := rect(minx,miny,maxx+1,maxy+1);
    2152   IntersectRect(result,result,ClipRect);
    2153 end;
    2154 
    2155 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2156   Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
    2157   imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2158   ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
    2159 begin
    2160   if ACorrectBlur then
    2161     PutImageAngle(x,y,Source,angle,AOutputBounds,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
    2162   else
    2163     PutImageAngle(x,y,Source,angle,AOutputBounds,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
    2164 end;
    2165 
    2166 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2167   Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
    2168   imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
    2169 begin
    2170   if ACorrectBlur then
    2171     PutImageAngle(x,y,Source,angle,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
    2172   else
    2173     PutImageAngle(x,y,Source,angle,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
    2174 end;
    2175 
    2176 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2177   Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
    2178   AResampleFilter: TResampleFilter; imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2179   ARestoreOffsetAfterRotation: boolean);
    2180 var
    2181   Origin,HAxis,VAxis: TPointF;
    2182 begin
    2183   if (source = nil) or (AOpacity=0) then exit;
    2184   ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
    2185      Origin,HAxis,VAxis);
    2186   PutImageAffine(Origin,HAxis,VAxis,source,AOutputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
    2187 end;
    2188 
    2189 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2190   Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter;
    2191   imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2192   ARestoreOffsetAfterRotation: boolean);
    2193 var
    2194   Origin,HAxis,VAxis: TPointF;
    2195 begin
    2196   if (source = nil) or (AOpacity=0) then exit;
    2197   ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
    2198      Origin,HAxis,VAxis);
    2199   PutImageAffine(Origin,HAxis,VAxis,source,AResampleFilter,AOpacity);
    2200 end;
    2201 
    2202 procedure TBGRACustomBitmap.ComputeImageAngleAxes(x, y, w, h,
    2203   angle: single; imageCenterX, imageCenterY: single;
    2204   ARestoreOffsetAfterRotation: boolean; out Origin, HAxis, VAxis: TPointF);
    2205 var
    2206   cosa,sina: single;
    2207 
    2208   { Compute rotated coordinates }
    2209   function Coord(relX,relY: single): TPointF;
    2210   begin
    2211     relX -= imageCenterX;
    2212     relY -= imageCenterY;
    2213     result.x := relX*cosa-relY*sina+x;
    2214     result.y := relY*cosa+relX*sina+y;
    2215     if ARestoreOffsetAfterRotation then
    2216     begin
    2217       result.x += imageCenterX;
    2218       result.y += imageCenterY;
    2219     end;
    2220   end;
    2221 
    2222 begin
    2223   cosa := cos(-angle*Pi/180);
    2224   sina := -sin(-angle*Pi/180);
    2225   Origin := Coord(0,0);
    2226   HAxis := Coord(w,0);
    2227   VAxis := Coord(0,h);
    2228 end;
    2229 
    2230 function TBGRACustomBitmap.GetImageAngleBounds(x, y: single;
    2231   Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
    2232   imageCenterY: single; ARestoreOffsetAfterRotation: boolean): TRect;
    2233 var
    2234   cosa,sina: single;
    2235 
    2236   { Compute rotated coordinates }
    2237   function Coord(relX,relY: single): TPointF;
    2238   begin
    2239     relX -= imageCenterX;
    2240     relY -= imageCenterY;
    2241     result.x := relX*cosa-relY*sina+x;
    2242     result.y := relY*cosa+relX*sina+y;
    2243     if ARestoreOffsetAfterRotation then
    2244     begin
    2245       result.x += imageCenterX;
    2246       result.y += imageCenterY;
    2247     end;
    2248   end;
    2249 
    2250 begin
    2251   if (source = nil) then
    2252   begin
    2253     result := EmptyRect;
    2254     exit;
    2255   end;
    2256   cosa := cos(-angle*Pi/180);
    2257   sina := -sin(-angle*Pi/180);
    2258   result := GetImageAffineBounds(Coord(0,0),Coord(source.Width,0),Coord(0,source.Height),source);
    2259 end;
    2260 
    2261 procedure TBGRACustomBitmap.VerticalFlip;
    2262 begin
    2263   VerticalFlip(rect(0,0,Width,Height));
    2264 end;
    2265 
    2266 procedure TBGRACustomBitmap.HorizontalFlip;
    2267 begin
    2268   HorizontalFlip(rect(0,0,Width,Height));
    2269 end;
    2270 
    2271 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap);
    2272 begin
    2273   ApplyMask(mask, Rect(0,0,Width,Height), Point(0,0));
    2274 end;
    2275 
    2276 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect);
    2277 begin
    2278   ApplyMask(mask, ARect, ARect.TopLeft);
    2279 end;
    2280 
    2281 { Interface gateway }
    2282 function TBGRACustomBitmap.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2283 begin
    2284   if GetInterface(iid, obj) then
    2285     Result := S_OK
    2286   else
    2287     Result := longint(E_NOINTERFACE);
    2288 end;
    2289 
    2290 { There is no automatic reference counting, but it is compulsory to define these functions }
    2291 function TBGRACustomBitmap._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2292 begin
    2293   result := 0;
    2294 end;
    2295 
    2296 function TBGRACustomBitmap._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2297 begin
    2298   result := 0;
    2299 end;
    2300 
    2301 {$hints off}
    2302 procedure TBGRACustomBitmap.ScanPutPixels(pdest: PBGRAPixel; count: integer;
    2303   mode: TDrawMode);
    2304 begin
    2305   //do nothing
    2306 end;
    2307 {$hints on}
    2308 
    2309 function TBGRACustomBitmap.IsScanPutPixelsDefined: boolean;
    2310 begin
    2311   result := False;
    2312 end;
    2313 
    2314 {********************** End of TBGRACustomBitmap **************************}
    2315 
    2316 { TBGRACustomScanner }
    2317 { The abstract class record the position so that a derived class
    2318   need only to redefine ScanAt }
    2319 
    2320 function TBGRACustomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel;
    2321 begin
    2322   result := ScanAt(X,Y);
    2323 end;
    2324 
    2325 procedure TBGRACustomScanner.ScanMoveTo(X, Y: Integer);
    2326 begin
    2327   FCurX := X;
    2328   FCurY := Y;
    2329 end;
    2330 
    2331 { Call ScanAt to determine pixel value }
    2332 function TBGRACustomScanner.ScanNextPixel: TBGRAPixel;
    2333 begin
    2334   result := ScanAt(FCurX,FCurY);
    2335   Inc(FCurX);
    2336 end;
    2337 
    2338 {$hints off}
    2339 procedure TBGRACustomScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
    2340   mode: TDrawMode);
    2341 begin
    2342   //do nothing
    2343 end;
    2344 {$hints on}
    2345 
    2346 function TBGRACustomScanner.IsScanPutPixelsDefined: boolean;
    2347 begin
    2348   result := false;
    2349 end;
    2350 
    2351 { Interface gateway }
    2352 function TBGRACustomScanner.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2353 begin
    2354   if GetInterface(iid, obj) then
    2355     Result := S_OK
    2356   else
    2357     Result := longint(E_NOINTERFACE);
    2358 end;
    2359 
    2360 { There is no automatic reference counting, but it is compulsory to define these functions }
    2361 function TBGRACustomScanner._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2362 begin
    2363   result := 0;
    2364 end;
    2365 
    2366 function TBGRACustomScanner._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2367 begin
    2368   result := 0;
    2369 end;
    2370 
    2371 {********************** End of TBGRACustomScanner **************************}
    2372 
    2373 { The gamma correction is approximated here by a power function }
    2374 const
    2375   GammaExpFactor   = 1.7; //exponent
    2376   redWeightShl10   = 306; // = 0.299
    2377   greenWeightShl10 = 601; // = 0.587
    2378   blueWeightShl10  = 117; // = 0.114
    2379 
    2380 var
    2381   GammaLinearFactor: single;
    2382 
    2383 procedure InitGamma;
    2384 var
    2385   i: integer;
    2386 {$IFDEF WINCE}
    2387   j,prevpos,curpos,midpos: integer;
    2388 {$ENDIF}
    2389 begin
    2390   //the linear factor is used to normalize expanded values in the range 0..65535
    2391   GammaLinearFactor := 65535 / power(255, GammaExpFactor);
    2392 
    2393 {$IFDEF WINCE}
    2394   curpos := 0;
    2395   GammaExpansionTab[0] := 0;
    2396   GammaCompressionTab[0] := 0;
    2397   for i := 0 to 255 do
    2398   begin
    2399     prevpos := curpos;
    2400     curpos := round(power(i, GammaExpFactor) * GammaLinearFactor);
    2401     if i = 1 then curpos := 1; //to avoid information loss
    2402     GammaExpansionTab[i] := curpos;
    2403     midpos := (prevpos+1+curpos) div 2;
    2404     for j := prevpos+1 to midpos-1 do
    2405       GammaCompressionTab[j] := i-1;
    2406     for j := midpos to curpos do
    2407       GammaCompressionTab[j] := i;
    2408   end;
    2409 {$ELSE}
    2410   for i := 0 to 255 do
    2411     GammaExpansionTab[i] := round(power(i, GammaExpFactor) * GammaLinearFactor);
    2412 
    2413   for i := 0 to 65535 do
    2414     GammaCompressionTab[i] := round(power(i / GammaLinearFactor, 1 / GammaExpFactor));
    2415 
    2416   GammaExpansionTab[1]   := 1; //to avoid information loss
    2417   GammaCompressionTab[1] := 1;
    2418 {$ENDIF}
    2419 end;
    2420 
    2421 {************************** Color functions **************************}
     570begin {optional implementation} end;
     571
    2422572
    2423573function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb,
     
    2460610
    2461611  result := true;
    2462 end;
    2463 
    2464 { The intensity is defined here as the maximum value of any color component }
    2465 function GetIntensity(const c: TExpandedPixel): word; inline;
    2466 begin
    2467   Result := c.red;
    2468   if c.green > Result then
    2469     Result := c.green;
    2470   if c.blue > Result then
    2471     Result := c.blue;
    2472 end;
    2473 
    2474 function GetIntensity(c: TBGRAPixel): word;
    2475 begin
    2476   Result := c.red;
    2477   if c.green > Result then
    2478     Result := c.green;
    2479   if c.blue > Result then
    2480     Result := c.blue;
    2481   result := GammaExpansionTab[Result];
    2482 end;
    2483 
    2484 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
    2485 var
    2486   curIntensity: word;
    2487 begin
    2488   curIntensity := GetIntensity(c);
    2489   if curIntensity = 0 then //suppose it's gray if there is no color information
    2490   begin
    2491     Result.red := intensity;
    2492     Result.green := intensity;
    2493     Result.blue := intensity;
    2494     result.alpha := c.alpha;
    2495   end
    2496   else
    2497   begin
    2498     //linear interpolation to reached wanted intensity
    2499     Result.red   := (c.red * intensity + (curIntensity shr 1)) div curIntensity;
    2500     Result.green := (c.green * intensity + (curIntensity shr 1)) div curIntensity;
    2501     Result.blue  := (c.blue * intensity + (curIntensity shr 1)) div curIntensity;
    2502     Result.alpha := c.alpha;
    2503   end;
    2504 end;
    2505 
    2506 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
    2507 begin
    2508   result := GammaCompression(SetIntensity(GammaExpansion(c),intensity));
    2509 end;
    2510 
    2511 function GetLightness(c: TBGRAPixel): word;
    2512 begin
    2513   result := GetLightness(GammaExpansion(c));
    2514 end;
    2515 
    2516 { The lightness here is defined as the subjective sensation of luminosity, where
    2517   blue is the darkest component and green the lightest }
    2518 function GetLightness(const c: TExpandedPixel): word; inline;
    2519 begin
    2520   Result := (c.red * redWeightShl10 + c.green * greenWeightShl10 +
    2521     c.blue * blueWeightShl10 + 512) shr 10;
    2522 end;
    2523 
    2524 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
    2525 var
    2526   curLightness: word;
    2527 begin
    2528   curLightness := GetLightness(c);
    2529   if lightness = curLightness then
    2530   begin //no change
    2531     Result := c;
    2532     exit;
    2533   end;
    2534   result := SetLightness(c, lightness, curLightness);
    2535 end;
    2536 
    2537 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
    2538 begin
    2539   result := GammaCompression(SetLightness(GammaExpansion(c),lightness));
    2540 end;
    2541 
    2542 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel;
    2543 var
    2544   AddedWhiteness, maxBeforeWhite: word;
    2545   clip: boolean;
    2546 begin
    2547   if lightness = curLightness then
    2548   begin //no change
    2549     Result := c;
    2550     exit;
    2551   end;
    2552   if lightness = 65535 then //set to white
    2553   begin
    2554     Result.red   := 65535;
    2555     Result.green := 65535;
    2556     Result.blue  := 65535;
    2557     Result.alpha := c.alpha;
    2558     exit;
    2559   end;
    2560   if lightness = 0 then  //set to black
    2561   begin
    2562     Result.red   := 0;
    2563     Result.green := 0;
    2564     Result.blue  := 0;
    2565     Result.alpha := c.alpha;
    2566     exit;
    2567   end;
    2568   if curLightness = 0 then  //set from black
    2569   begin
    2570     Result.red   := lightness;
    2571     Result.green := lightness;
    2572     Result.blue  := lightness;
    2573     Result.alpha := c.alpha;
    2574     exit;
    2575   end;
    2576   if lightness < curLightness then //darker is easy
    2577   begin
    2578     result.alpha:= c.alpha;
    2579     result.red := (c.red * lightness + (curLightness shr 1)) div curLightness;
    2580     result.green := (c.green * lightness + (curLightness shr 1)) div curLightness;
    2581     result.blue := (c.blue * lightness + (curLightness shr 1)) div curLightness;
    2582     exit;
    2583   end;
    2584   //lighter and grayer
    2585   Result := c;
    2586   AddedWhiteness := lightness - curLightness;
    2587   maxBeforeWhite := 65535 - AddedWhiteness;
    2588   clip   := False;
    2589   if Result.red <= maxBeforeWhite then
    2590     Inc(Result.red, AddedWhiteness)
    2591   else
    2592   begin
    2593     Result.red := 65535;
    2594     clip := True;
    2595   end;
    2596   if Result.green <= maxBeforeWhite then
    2597     Inc(Result.green, AddedWhiteness)
    2598   else
    2599   begin
    2600     Result.green := 65535;
    2601     clip := True;
    2602   end;
    2603   if Result.blue <= maxBeforeWhite then
    2604     Inc(Result.blue, AddedWhiteness)
    2605   else
    2606   begin
    2607     Result.blue := 65535;
    2608     clip := True;
    2609   end;
    2610 
    2611   if clip then //light and whiter
    2612   begin
    2613     curLightness   := GetLightness(Result);
    2614     addedWhiteness := lightness - curLightness;
    2615     maxBeforeWhite := 65535 - curlightness;
    2616     Result.red     := Result.red + addedWhiteness * (65535 - Result.red) div
    2617       maxBeforeWhite;
    2618     Result.green   := Result.green + addedWhiteness * (65535 - Result.green) div
    2619       maxBeforeWhite;
    2620     Result.blue    := Result.blue + addedWhiteness * (65535 - Result.blue) div
    2621       maxBeforeWhite;
    2622   end;
    2623 end;
    2624 
    2625 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel;
    2626 var
    2627   r,g,b: word;
    2628   lightness256: byte;
    2629 begin
    2630   if lightness <= 32768 then
    2631   begin
    2632     if lightness = 32768 then
    2633       result := color else
    2634     begin
    2635       lightness256 := GammaCompressionTab[lightness shl 1];
    2636       result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
    2637                      color.blue * lightness256 shr 8, color.alpha);
    2638     end;
    2639   end else
    2640   begin
    2641     if lightness = 65535 then
    2642       result := BGRA(255,255,255,color.alpha) else
    2643     begin
    2644       lightness -= 32767;
    2645       r := GammaExpansionTab[color.red];
    2646       g := GammaExpansionTab[color.green];
    2647       b := GammaExpansionTab[color.blue];
    2648       result := BGRA(GammaCompressionTab[ r + (not r)*lightness shr 15 ],
    2649                      GammaCompressionTab[ g + (not g)*lightness shr 15 ],
    2650                      GammaCompressionTab[ b + (not b)*lightness shr 15 ],
    2651                      color.alpha);
    2652     end;
    2653   end;
    2654 end;
    2655 
    2656 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    2657 {$ifdef CPUI386} {$asmmode intel} assembler;
    2658   asm
    2659     imul edx
    2660     shl edx, 17
    2661     shr eax, 15
    2662     or edx, eax
    2663     mov result, edx
    2664   end;
    2665 {$ELSE}
    2666 begin
    2667   result := int64(lightness1)*lightness2 shr 15;
    2668 end;
    2669 {$ENDIF}
    2670 
    2671 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
    2672 var
    2673     maxValue,invMaxValue,r,g,b: longword;
    2674     lightness256: byte;
    2675 begin
    2676   if lightness <= 32768 then
    2677   begin
    2678     if lightness = 32768 then
    2679       result := color else
    2680     begin
    2681       lightness256 := GammaCompressionTab[lightness shl 1];
    2682       result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
    2683                      color.blue * lightness256 shr 8, color.alpha);
    2684     end;
    2685   end else
    2686   begin
    2687     r := CombineLightness(GammaExpansionTab[color.red], lightness);
    2688     g := CombineLightness(GammaExpansionTab[color.green], lightness);
    2689     b := CombineLightness(GammaExpansionTab[color.blue], lightness);
    2690     maxValue := r;
    2691     if g > maxValue then maxValue := g;
    2692     if b > maxValue then maxValue := b;
    2693     if maxValue <= 65535 then
    2694       result := BGRA(GammaCompressionTab[r],
    2695                      GammaCompressionTab[g],
    2696                      GammaCompressionTab[b],
    2697                      color.alpha)
    2698     else
    2699     begin
    2700       invMaxValue := (longword(2147483647)+longword(maxValue-1)) div maxValue;
    2701       maxValue := (maxValue-65535) shr 1;
    2702       r := r*invMaxValue shr 15 + maxValue;
    2703       g := g*invMaxValue shr 15 + maxValue;
    2704       b := b*invMaxValue shr 15 + maxValue;
    2705       if r >= 65535 then result.red := 255 else
    2706         result.red := GammaCompressionTab[r];
    2707       if g >= 65535 then result.green := 255 else
    2708         result.green := GammaCompressionTab[g];
    2709       if b >= 65535 then result.blue := 255 else
    2710         result.blue := GammaCompressionTab[b];
    2711       result.alpha := color.alpha;
    2712     end;
    2713   end;
    2714 end;
    2715 
    2716 { Conversion from RGB value to HSL colorspace. See : http://en.wikipedia.org/wiki/HSL_color_space }
    2717 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;
    2718 begin
    2719   result := ExpandedToHSLA(GammaExpansion(c));
    2720 end;
    2721 
    2722 procedure ExpandedToHSLAInline(r,g,b: Int32Or64; var dest: THSLAPixel); inline;
    2723 const
    2724   deg60  = 10922;
    2725   deg120 = 21845;
    2726   deg240 = 43690;
    2727 var
    2728   min, max, minMax: Int32or64;
    2729   UMinMax,UTwiceLightness: UInt32or64;
    2730 begin
    2731   if g > r then
    2732   begin
    2733     max := g;
    2734     min := r;
    2735   end
    2736   else
    2737   begin
    2738     max := r;
    2739     min := g;
    2740   end;
    2741   if b > max then
    2742     max := b
    2743   else
    2744   if b < min then
    2745     min  := b;
    2746   minMax := max - min;
    2747 
    2748   if minMax = 0 then
    2749     dest.hue := 0
    2750   else
    2751   if max = r then
    2752     {$PUSH}{$RANGECHECKS OFF}
    2753     dest.hue := ((g - b) * deg60) div minMax
    2754     {$POP}
    2755   else
    2756   if max = g then
    2757     dest.hue := ((b - r) * deg60) div minMax + deg120
    2758   else
    2759     {max = b} dest.hue := ((r - g) * deg60) div minMax + deg240;
    2760   UTwiceLightness := max + min;
    2761   if min = max then
    2762     dest.saturation := 0
    2763   else
    2764   begin
    2765     UMinMax:= minMax;
    2766     if UTwiceLightness < 65536 then
    2767       dest.saturation := (UMinMax shl 16) div (UTwiceLightness + 1)
    2768     else
    2769       dest.saturation := (UMinMax shl 16) div (131072 - UTwiceLightness);
    2770   end;
    2771   dest.lightness := UTwiceLightness shr 1;
    2772 end;
    2773 
    2774 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
    2775 begin
    2776   result.alpha := ec.alpha;
    2777   ExpandedToHSLAInline(ec.red,ec.green,ec.blue,result);
    2778 end;
    2779 
    2780 function HtoG(hue: word): word;
    2781 const
    2782   segmentDest: array[0..5] of NativeUInt =
    2783      (13653, 10923, 8192, 13653, 10923, 8192);
    2784   segmentSrc: array[0..5] of NativeUInt =
    2785      (10923, 10922, 10923, 10923, 10922, 10923);
    2786 var
    2787   h,g: NativeUInt;
    2788 begin
    2789   h := hue;
    2790   if h < segmentSrc[0] then
    2791     g := h * segmentDest[0] div segmentSrc[0]
    2792   else
    2793   begin
    2794     g := segmentDest[0];
    2795     h -= segmentSrc[0];
    2796     if h < segmentSrc[1] then
    2797       g += h * segmentDest[1] div segmentSrc[1]
    2798     else
    2799     begin
    2800       g += segmentDest[1];
    2801       h -= segmentSrc[1];
    2802       if h < segmentSrc[2] then
    2803         g += h * segmentDest[2] div segmentSrc[2]
    2804       else
    2805       begin
    2806         g += segmentDest[2];
    2807         h -= segmentSrc[2];
    2808         if h < segmentSrc[3] then
    2809           g += h * segmentDest[3] div segmentSrc[3]
    2810         else
    2811         begin
    2812           g += segmentDest[3];
    2813           h -= segmentSrc[3];
    2814           if h < segmentSrc[4] then
    2815             g += h * segmentDest[4] div segmentSrc[4]
    2816           else
    2817           begin
    2818             g += segmentDest[4];
    2819             h -= segmentSrc[4];
    2820             g += h * segmentDest[5] div segmentSrc[5];
    2821           end;
    2822         end;
    2823       end;
    2824     end;
    2825   end;
    2826   result := g;
    2827 end;
    2828 
    2829 function GtoH(ghue: word): word;
    2830 const
    2831   segment: array[0..5] of NativeUInt =
    2832      (13653, 10923, 8192, 13653, 10923, 8192);
    2833 var g: NativeUint;
    2834 begin
    2835   g := ghue;
    2836   if g < segment[0] then
    2837     result := g * 10923 div segment[0]
    2838   else
    2839   begin
    2840     g -= segment[0];
    2841     if g < segment[1] then
    2842       result := g * (21845-10923) div segment[1] + 10923
    2843     else
    2844     begin
    2845       g -= segment[1];
    2846       if g < segment[2] then
    2847         result := g * (32768-21845) div segment[2] + 21845
    2848       else
    2849       begin
    2850         g -= segment[2];
    2851         if g < segment[3] then
    2852           result := g * (43691-32768) div segment[3] + 32768
    2853         else
    2854         begin
    2855           g -= segment[3];
    2856           if g < segment[4] then
    2857             result := g * (54613-43691) div segment[4] + 43691
    2858           else
    2859           begin
    2860             g -= segment[4];
    2861             result := g * (65536-54613) div segment[5] + 54613;
    2862           end;
    2863         end;
    2864       end;
    2865     end;
    2866   end;
    2867 end;
    2868 
    2869 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
    2870 var lightness: UInt32Or64;
    2871     red,green,blue: Int32or64;
    2872 begin
    2873   red   := GammaExpansionTab[c.red];
    2874   green := GammaExpansionTab[c.green];
    2875   blue  := GammaExpansionTab[c.blue];
    2876   result.alpha := c.alpha shl 8 + c.alpha;
    2877 
    2878   lightness := (red * redWeightShl10 + green * greenWeightShl10 +
    2879     blue * blueWeightShl10 + 512) shr 10;
    2880 
    2881   ExpandedToHSLAInline(red,green,blue,result);
    2882   if result.lightness > 32768 then
    2883     result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;
    2884   result.lightness := lightness;
    2885   result.hue := HtoG(result.hue);
    2886 end;
    2887 
    2888 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
    2889 var lightness: UInt32Or64;
    2890     red,green,blue: Int32or64;
    2891 begin
    2892   red   := ec.red;
    2893   green := ec.green;
    2894   blue  := ec.blue;
    2895   result.alpha := ec.alpha;
    2896 
    2897   lightness := (red * redWeightShl10 + green * greenWeightShl10 +
    2898     blue * blueWeightShl10 + 512) shr 10;
    2899 
    2900   ExpandedToHSLAInline(red,green,blue,result);
    2901   if result.lightness > 32768 then
    2902     result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;
    2903   result.lightness := lightness;
    2904   result.hue := HtoG(result.hue);
    2905 end;
    2906 
    2907 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
    2908 const
    2909   deg30  = 4096;
    2910   deg60  = 8192;
    2911   deg120 = deg60 * 2;
    2912   deg180 = deg60 * 3;
    2913   deg240 = deg60 * 4;
    2914   deg360 = deg60 * 6;
    2915 
    2916   function ComputeColor(p, q: Int32or64; h: Int32or64): Int32or64; inline;
    2917   begin
    2918     if h < deg180 then
    2919     begin
    2920       if h < deg60 then
    2921         Result := p + ((q - p) * h + deg30) div deg60
    2922       else
    2923         Result := q
    2924     end else
    2925     begin
    2926       if h < deg240 then
    2927         Result := p + ((q - p) * (deg240 - h) + deg30) div deg60
    2928       else
    2929         Result := p;
    2930     end;
    2931   end;
    2932 
    2933 var
    2934   q, p, L, S, H: Int32or64;
    2935 begin
    2936   L := c.lightness;
    2937   S := c.saturation;
    2938   if S = 0 then  //gray
    2939   begin
    2940     result.red   := L;
    2941     result.green := L;
    2942     result.blue  := L;
    2943     result.alpha := c.alpha;
    2944     exit;
    2945   end;
    2946   {$hints off}
    2947   if L < 32768 then
    2948     q := (L shr 1) * ((65535 + S) shr 1) shr 14
    2949   else
    2950     q := L + S - ((L shr 1) *
    2951       (S shr 1) shr 14);
    2952   {$hints on}
    2953   if q > 65535 then q := 65535;
    2954   p   := (L shl 1) - q;
    2955   if p > 65535 then p := 65535;
    2956   H := c.hue * deg360 shr 16;
    2957   result.green := ComputeColor(p, q, H);
    2958   inc(H, deg120);
    2959   if H > deg360 then Dec(H, deg360);
    2960   result.red   := ComputeColor(p, q, H);
    2961   inc(H, deg120);
    2962   if H > deg360 then Dec(H, deg360);
    2963   result.blue  := ComputeColor(p, q, H);
    2964   result.alpha := c.alpha;
    2965 end;
    2966 
    2967 { Conversion from HSL colorspace to RGB. See : http://en.wikipedia.org/wiki/HSL_color_space }
    2968 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    2969 var ec: TExpandedPixel;
    2970 begin
    2971   ec := HSLAToExpanded(c);
    2972   Result := GammaCompression(ec);
    2973 end;
    2974 
    2975 function HueDiff(h1, h2: word): word;
    2976 begin
    2977   result := abs(integer(h1)-integer(h2));
    2978   if result > 32768 then result := 65536-result;
    2979 end;
    2980 
    2981 function GetHue(ec: TExpandedPixel): word;
    2982 const
    2983   deg60  = 8192;
    2984   deg120 = deg60 * 2;
    2985   deg240 = deg60 * 4;
    2986   deg360 = deg60 * 6;
    2987 var
    2988   min, max, minMax: integer;
    2989   r,g,b: integer;
    2990 begin
    2991   r := ec.red;
    2992   g := ec.green;
    2993   b := ec.blue;
    2994   min := r;
    2995   max := r;
    2996   if g > max then
    2997     max := g
    2998   else
    2999   if g < min then
    3000     min := g;
    3001   if b > max then
    3002     max := b
    3003   else
    3004   if b < min then
    3005     min  := b;
    3006   minMax := max - min;
    3007 
    3008   if minMax = 0 then
    3009     Result := 0
    3010   else
    3011   if max = r then
    3012     Result := (((g - b) * deg60) div
    3013       minMax + deg360) mod deg360
    3014   else
    3015   if max = g then
    3016     Result := ((b - r) * deg60) div minMax + deg120
    3017   else
    3018     {max = b} Result :=
    3019       ((r - g) * deg60) div minMax + deg240;
    3020 
    3021   Result   := (Result shl 16) div deg360; //normalize
    3022 end;
    3023 
    3024 function ColorImportance(ec: TExpandedPixel): word;
    3025 var min,max: word;
    3026 begin
    3027   min := ec.red;
    3028   max := ec.red;
    3029   if ec.green > max then
    3030     max := ec.green
    3031   else
    3032   if ec.green < min then
    3033     min := ec.green;
    3034   if ec.blue > max then
    3035     max := ec.blue
    3036   else
    3037   if ec.blue < min then
    3038     min  := ec.blue;
    3039   result := max - min;
    3040 end;
    3041 
    3042 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
    3043 var ec: TExpandedPixel;
    3044     lightness: word;
    3045 begin
    3046   c.hue := GtoH(c.hue);
    3047   lightness := c.lightness;
    3048   c.lightness := 32768;
    3049   ec := HSLAToExpanded(c);
    3050   result := GammaCompression(SetLightness(ec, lightness));
    3051 end;
    3052 
    3053 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
    3054 var lightness: word;
    3055 begin
    3056   c.hue := GtoH(c.hue);
    3057   lightness := c.lightness;
    3058   c.lightness := 32768;
    3059   result := SetLightness(HSLAToExpanded(c),lightness);
    3060 end;
    3061 
    3062 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    3063 begin
    3064   result := BGRAToHSLA(GSBAToBGRA(c));
    3065 end;
    3066 
    3067 { Apply gamma correction using conversion tables }
    3068 function GammaExpansion(c: TBGRAPixel): TExpandedPixel;
    3069 begin
    3070   Result.red   := GammaExpansionTab[c.red];
    3071   Result.green := GammaExpansionTab[c.green];
    3072   Result.blue  := GammaExpansionTab[c.blue];
    3073   Result.alpha := c.alpha shl 8 + c.alpha;
    3074 end;
    3075 
    3076 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel;
    3077 begin
    3078   Result.red   := GammaCompressionTab[ec.red];
    3079   Result.green := GammaCompressionTab[ec.green];
    3080   Result.blue  := GammaCompressionTab[ec.blue];
    3081   Result.alpha := ec.alpha shr 8;
    3082 end;
    3083 
    3084 function GammaCompression(red, green, blue, alpha: word): TBGRAPixel;
    3085 begin
    3086   Result.red   := GammaCompressionTab[red];
    3087   Result.green := GammaCompressionTab[green];
    3088   Result.blue  := GammaCompressionTab[blue];
    3089   Result.alpha := alpha shr 8;
    3090 end;
    3091 
    3092 // Conversion to grayscale by taking into account
    3093 // different color weights
    3094 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
    3095 var
    3096   ec:    TExpandedPixel;
    3097   gray:  word;
    3098   cgray: byte;
    3099 begin
    3100   if c.alpha = 0 then
    3101   begin
    3102     result := BGRAPixelTransparent;
    3103     exit;
    3104   end;
    3105   //gamma expansion
    3106   ec    := GammaExpansion(c);
    3107   //gray composition
    3108   gray  := (ec.red * redWeightShl10 + ec.green * greenWeightShl10 +
    3109     ec.blue * blueWeightShl10 + 512) shr 10;
    3110   //gamma compression
    3111   cgray := GammaCompressionTab[gray];
    3112   Result.red := cgray;
    3113   Result.green := cgray;
    3114   Result.blue := cgray;
    3115   Result.alpha := c.alpha;
    3116 end;
    3117 
    3118 function GrayscaleToBGRA(lightness: word): TBGRAPixel;
    3119 begin
    3120   result.red := GammaCompressionTab[lightness];
    3121   result.green := result.red;
    3122   result.blue := result.red;
    3123   result.alpha := $ff;
    3124 end;
    3125 
    3126 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel;
    3127 var
    3128   sumR,sumG,sumB,sumA: NativeUInt;
    3129   i: integer;
    3130 begin
    3131   if length(colors)<=0 then
    3132   begin
    3133     result := BGRAPixelTransparent;
    3134     exit;
    3135   end;
    3136   sumR := 0;
    3137   sumG := 0;
    3138   sumB := 0;
    3139   sumA := 0;
    3140   for i := 0 to high(colors) do
    3141   with colors[i] do
    3142   begin
    3143     sumR += red*alpha;
    3144     sumG += green*alpha;
    3145     sumB += blue*alpha;
    3146     sumA += alpha;
    3147   end;
    3148   if sumA > 0 then
    3149   begin
    3150     result.red := (sumR + sumA shr 1) div sumA;
    3151     result.green := (sumG + sumA shr 1) div sumA;
    3152     result.blue := (sumB + sumA shr 1) div sumA;
    3153     result.alpha := sumA div longword(length(colors));
    3154   end
    3155   else
    3156     result := BGRAPixelTransparent;
    3157 end;
    3158 
    3159 { Merge linearly two colors of same importance }
    3160 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel;
    3161 var c12: cardinal;
    3162 begin
    3163   if (c1.alpha = 0) then
    3164     Result := c2
    3165   else
    3166   if (c2.alpha = 0) then
    3167     Result := c1
    3168   else
    3169   begin
    3170     c12 := c1.alpha + c2.alpha;
    3171     Result.red   := (c1.red * c1.alpha + c2.red * c2.alpha + c12 shr 1) div c12;
    3172     Result.green := (c1.green * c1.alpha + c2.green * c2.alpha + c12 shr 1) div c12;
    3173     Result.blue  := (c1.blue * c1.alpha + c2.blue * c2.alpha + c12 shr 1) div c12;
    3174     Result.alpha := (c12 + 1) shr 1;
    3175   end;
    3176 end;
    3177 
    3178 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel;
    3179   weight2: integer): TBGRAPixel;
    3180 var
    3181     f1,f2,f12: int64;
    3182 begin
    3183   if (weight1 = 0) then
    3184   begin
    3185     if (weight2 = 0) then
    3186       result := BGRAPixelTransparent
    3187     else
    3188       Result := c2
    3189   end
    3190   else
    3191   if (weight2 = 0) then
    3192     Result := c1
    3193   else
    3194   if (weight1+weight2 = 0) then
    3195     Result := BGRAPixelTransparent
    3196   else
    3197   begin
    3198     f1 := int64(c1.alpha)*weight1;
    3199     f2 := int64(c2.alpha)*weight2;
    3200     f12 := f1+f2;
    3201     if f12 = 0 then
    3202       result := BGRAPixelTransparent
    3203     else
    3204     begin
    3205       Result.red   := (c1.red * f1 + c2.red * f2 + f12 shr 1) div f12;
    3206       Result.green := (c1.green * f1 + c2.green * f2 + f12 shr 1) div f12;
    3207       Result.blue  := (c1.blue * f1 + c2.blue * f2 + f12 shr 1) div f12;
    3208       {$hints off}
    3209       Result.alpha := (f12 + ((weight1+weight2) shr 1)) div (weight1+weight2);
    3210       {$hints on}
    3211     end;
    3212   end;
    3213 end;
    3214 
    3215 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel;
    3216   weight2: byte): TBGRAPixel;
    3217 var
    3218     w1,w2,f1,f2,f12,a: UInt32or64;
    3219 begin
    3220   w1 := weight1;
    3221   w2 := weight2;
    3222   if (w1 = 0) then
    3223   begin
    3224     if (w2 = 0) then
    3225       result := BGRAPixelTransparent
    3226     else
    3227       Result := c2
    3228   end
    3229   else
    3230   if (w2 = 0) then
    3231     Result := c1
    3232   else
    3233   begin
    3234     f1 := c1.alpha*w1;
    3235     f2 := c2.alpha*w2;
    3236     a := (f1+f2 + ((w1+w2) shr 1)) div (w1+w2);
    3237     if a = 0 then
    3238     begin
    3239       result := BGRAPixelTransparent;
    3240       exit;
    3241     end else
    3242       Result.alpha := a;
    3243     {$IFNDEF CPU64}
    3244     if (f1 >= 32768) or (f2 >= 32768) then
    3245     begin
    3246       f1 := f1 shr 1;
    3247       f2 := f2 shr 1;
    3248     end;
    3249     {$ENDIF}
    3250     f12 := f1+f2;
    3251     Result.red   := GammaCompressionTab[(GammaExpansionTab[c1.red] * f1 + GammaExpansionTab[c2.red] * f2) div f12];
    3252     Result.green := GammaCompressionTab[(GammaExpansionTab[c1.green] * f1 + GammaExpansionTab[c2.green] * f2) div f12];
    3253     Result.blue  := GammaCompressionTab[(GammaExpansionTab[c1.blue] * f1 + GammaExpansionTab[c2.blue] * f2) div f12];
    3254   end;
    3255 end;
    3256 
    3257 { Merge two colors of same importance }
    3258 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel;
    3259 var c12: cardinal;
    3260 begin
    3261   if (ec1.alpha = 0) then
    3262     Result := ec2
    3263   else
    3264   if (ec2.alpha = 0) then
    3265     Result := ec1
    3266   else
    3267   begin
    3268     c12 := ec1.alpha + ec2.alpha;
    3269     Result.red   := (int64(ec1.red) * ec1.alpha + int64(ec2.red) * ec2.alpha + c12 shr 1) div c12;
    3270     Result.green := (int64(ec1.green) * ec1.alpha + int64(ec2.green) * ec2.alpha + c12 shr 1) div c12;
    3271     Result.blue  := (int64(ec1.blue) * ec1.alpha + int64(ec2.blue) * ec2.alpha + c12 shr 1) div c12;
    3272     Result.alpha := (c12 + 1) shr 1;
    3273   end;
    3274 end;
    3275 
    3276 function BGRA(red, green, blue, alpha: byte): TBGRAPixel;
    3277 begin
    3278   Result.red   := red;
    3279   Result.green := green;
    3280   Result.blue  := blue;
    3281   Result.alpha := alpha;
    3282 end;
    3283 
    3284 function BGRA(red, green, blue: byte): TBGRAPixel; overload;
    3285 begin
    3286   Result.red   := red;
    3287   Result.green := green;
    3288   Result.blue  := blue;
    3289   Result.alpha := 255;
    3290 end;
    3291 
    3292 { Convert a TColor value to a TBGRAPixel value. Note that
    3293   you need to call ColorToRGB first if you use a system
    3294   color identifier like clWindow. }
    3295 {$PUSH}{$R-}
    3296 
    3297 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel;
    3298 begin
    3299   Result.hue   := hue;
    3300   Result.saturation := saturation;
    3301   Result.lightness  := lightness;
    3302   Result.alpha := alpha;
    3303 end;
    3304 
    3305 function HSLA(hue, saturation, lightness: word): THSLAPixel;
    3306 begin
    3307   Result.hue   := hue;
    3308   Result.saturation := saturation;
    3309   Result.lightness  := lightness;
    3310   Result.alpha := $ffff;
    3311 end;
    3312 
    3313 function ColorToBGRA(color: TColor): TBGRAPixel; overload;
    3314 begin
    3315   Result.red   := color;
    3316   Result.green := color shr 8;
    3317   Result.blue  := color shr 16;
    3318   Result.alpha := 255;
    3319 end;
    3320 
    3321 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
    3322 begin
    3323   Result.red   := color;
    3324   Result.green := color shr 8;
    3325   Result.blue  := color shr 16;
    3326   Result.alpha := opacity;
    3327 end;
    3328 {$POP}
    3329 
    3330 { Conversion from TFPColor to TBGRAPixel assuming TFPColor
    3331   is already gamma compressed }
    3332 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
    3333 begin
    3334   with AValue do
    3335     Result := BGRA(red shr 8, green shr 8, blue shr 8, alpha shr 8);
    3336 end;
    3337 
    3338 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
    3339 begin
    3340   result.red := AValue.red shl 8 + AValue.red;
    3341   result.green := AValue.green shl 8 + AValue.green;
    3342   result.blue := AValue.blue shl 8 + AValue.blue;
    3343   result.alpha := AValue.alpha shl 8 + AValue.alpha;
    3344 end;
    3345 
    3346 function BGRAToColor(c: TBGRAPixel): TColor;
    3347 begin
    3348   Result := c.red + (c.green shl 8) + (c.blue shl 16);
    3349 end;
    3350 
    3351 operator = (const c1, c2: TBGRAPixel): boolean;
    3352 begin
    3353   if (c1.alpha = 0) and (c2.alpha = 0) then
    3354     Result := True
    3355   else
    3356     Result := (c1.alpha = c2.alpha) and (c1.red = c2.red) and
    3357       (c1.green = c2.green) and (c1.blue = c2.blue);
    3358 end;
    3359 
    3360 function LessStartSlope65535(value: word): word;
    3361 var factor: word;
    3362 begin
    3363   factor := 4096 - (not value)*3 shr 7;
    3364   result := value*factor shr 12;
    3365 end;
    3366 
    3367 function ExpandedDiff(ec1, ec2: TExpandedPixel): word;
    3368 var
    3369   CompRedAlpha1, CompGreenAlpha1, CompBlueAlpha1, CompRedAlpha2,
    3370   CompGreenAlpha2, CompBlueAlpha2: integer;
    3371   DiffAlpha: word;
    3372   ColorDiff: word;
    3373   TempHueDiff: word;
    3374 begin
    3375   CompRedAlpha1 := ec1.red * ec1.alpha shr 16; //gives 0..65535
    3376   CompGreenAlpha1 := ec1.green * ec1.alpha shr 16;
    3377   CompBlueAlpha1 := ec1.blue * ec1.alpha shr 16;
    3378   CompRedAlpha2 := ec2.red * ec2.alpha shr 16;
    3379   CompGreenAlpha2 := ec2.green * ec2.alpha shr 16;
    3380   CompBlueAlpha2 := ec2.blue * ec2.alpha shr 16;
    3381   Result    := (Abs(CompRedAlpha2 - CompRedAlpha1)*redWeightShl10 +
    3382     Abs(CompBlueAlpha2 - CompBlueAlpha1)*blueWeightShl10 +
    3383     Abs(CompGreenAlpha2 - CompGreenAlpha1)*greenWeightShl10) shr 10;
    3384   ColorDiff := min(ColorImportance(ec1),ColorImportance(ec2));
    3385   if ColorDiff > 0 then
    3386   begin
    3387     TempHueDiff := HueDiff(HtoG(GetHue(ec1)),HtoG(GetHue(ec2)));
    3388     if TempHueDiff < 32768 then
    3389       TempHueDiff := LessStartSlope65535(TempHueDiff shl 1) shr 4
    3390     else
    3391       TempHueDiff := TempHueDiff shr 3;
    3392     Result := ((Result shr 4)* (not ColorDiff) + TempHueDiff*ColorDiff) shr 12;
    3393   end;
    3394   DiffAlpha := Abs(integer(ec2.Alpha) - integer(ec1.Alpha));
    3395   if DiffAlpha > Result then
    3396     Result := DiffAlpha;
    3397 end;
    3398 
    3399 function BGRAWordDiff(c1, c2: TBGRAPixel): word;
    3400 begin
    3401   result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2));
    3402 end;
    3403 
    3404 function BGRADiff(c1,c2: TBGRAPixel): byte;
    3405 begin
    3406   result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2)) shr 8;
    3407 end;
    3408 
    3409 operator-(const c1, c2: TColorF): TColorF;
    3410 begin
    3411   result[1] := c1[1]-c2[1];
    3412   result[2] := c1[2]-c2[2];
    3413   result[3] := c1[3]-c2[3];
    3414   result[4] := c1[4]-c2[4];
    3415 end;
    3416 
    3417 operator+(const c1, c2: TColorF): TColorF;
    3418 begin
    3419   result[1] := c1[1]+c2[1];
    3420   result[2] := c1[2]+c2[2];
    3421   result[3] := c1[3]+c2[3];
    3422   result[4] := c1[4]+c2[4];
    3423 end;
    3424 
    3425 operator*(const c1, c2: TColorF): TColorF;
    3426 begin
    3427   result[1] := c1[1]*c2[1];
    3428   result[2] := c1[2]*c2[2];
    3429   result[3] := c1[3]*c2[3];
    3430   result[4] := c1[4]*c2[4];
    3431 end;
    3432 
    3433 operator*(const c1: TColorF; factor: single): TColorF;
    3434 begin
    3435   result[1] := c1[1]*factor;
    3436   result[2] := c1[2]*factor;
    3437   result[3] := c1[3]*factor;
    3438   result[4] := c1[4]*factor;
    3439 end;
    3440 
    3441 function ColorF(red, green, blue, alpha: single): TColorF;
    3442 begin
    3443   result[1] := red;
    3444   result[2] := green;
    3445   result[3] := blue;
    3446   result[4] := alpha;
    3447 end;
    3448 
    3449 { Write a color in hexadecimal format RRGGBBAA or using the name in a color list }
    3450 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
    3451 var idx: integer;
    3452 begin
    3453   if Assigned(AColorList) then
    3454   begin
    3455     idx := AColorList.IndexOfColor(c, AMaxDiff);
    3456     if idx<> -1 then
    3457     begin
    3458       result := AColorList.Name[idx];
    3459       exit;
    3460     end;
    3461   end;
    3462   result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2);
    3463 end;
    3464 
    3465 type
    3466     arrayOfString = array of string;
    3467 
    3468 function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString;
    3469 var idxOpen,start,cur: integer;
    3470 begin
    3471     result := nil;
    3472     idxOpen := pos('(',str);
    3473     if idxOpen = 0 then
    3474     begin
    3475       start := 1;
    3476       //find first space
    3477       while (start <= length(str)) and (str[start]<>' ') do inc(start);
    3478     end else
    3479       start := idxOpen+1;
    3480     cur := start;
    3481     while cur <= length(str) do
    3482     begin
    3483        if str[cur] in[',',')'] then
    3484        begin
    3485          setlength(result,length(result)+1);
    3486          result[high(result)] := trim(copy(str,start,cur-start));
    3487          start := cur+1;
    3488          if str[cur] = ')' then exit;
    3489        end;
    3490        inc(cur);
    3491     end;
    3492     if idxOpen <> 0 then flagError := true; //should exit on ')'
    3493     if start <= length(str) then
    3494     begin
    3495       setlength(result,length(result)+1);
    3496       result[high(result)] := copy(str,start,length(str)-start+1);
    3497     end;
    3498 end;
    3499 
    3500 function ParseColorValue(str: string; var flagError: boolean): byte;
    3501 var pourcent,unclipped,{%H-}errPos: integer;
    3502 begin
    3503   if str = '' then result := 0 else
    3504   begin
    3505     if str[length(str)]='%' then
    3506     begin
    3507       val(copy(str,1,length(str)-1),pourcent,errPos);
    3508       if errPos <> 0 then flagError := true;
    3509       if pourcent < 0 then result := 0 else
    3510       if pourcent > 100 then result := 255 else
    3511         result := pourcent*255 div 100;
    3512     end else
    3513     begin
    3514       val(str,unclipped,errPos);
    3515       if errPos <> 0 then flagError := true;
    3516       if unclipped < 0 then result := 0 else
    3517       if unclipped > 255 then result := 255 else
    3518         result := unclipped;
    3519     end;
    3520   end;
    3521 end;
    3522 
    3523 //this function returns the parsed value only if it contains no error nor missing values, otherwise
    3524 //it returns BGRAPixelTransparent
    3525 function StrToBGRA(str: string): TBGRAPixel;
    3526 var missingValues, error: boolean;
    3527 begin
    3528   result := BGRABlack;
    3529   TryStrToBGRA(str, result, missingValues, error);
    3530   if missingValues or error then result := BGRAPixelTransparent;
    3531 end;
    3532 
    3533 //this function changes the content of parsedValue depending on available and parsable information.
    3534 //set parsedValue to the fallback values before calling this function.
    3535 //missing values are expressed by empty string or by '?', for example 'rgb(255,?,?,?)' will change only the red value.
    3536 //note that if alpha is not expressed by the string format, it will be opaque. So 'rgb(255,?,?)' will change the red value and the alpha value.
    3537 //the last parameter of rgba() is a floating point number where 1 is opaque and 0 is transparent.
    3538 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    3539 var errPos: integer;
    3540     values: array of string;
    3541     alphaF: single;
    3542     idx: integer;
    3543 begin
    3544   str := Trim(str);
    3545   error := false;
    3546   if (str = '') or (str = '?') then
    3547   begin
    3548     missingValues := true;
    3549     exit;
    3550   end else
    3551     missingValues := false;
    3552   str := StringReplace(lowerCase(str),'grey','gray',[]);
    3553 
    3554   //VGA color names
    3555   idx := VGAColors.IndexOf(str);
    3556   if idx <> -1 then
    3557   begin
    3558     parsedValue := VGAColors[idx];
    3559     exit;
    3560   end;
    3561   if str='transparent' then parsedValue := BGRAPixelTransparent else
    3562   begin
    3563     //check CSS color
    3564     idx := CSSColors.IndexOf(str);
    3565     if idx <> -1 then
    3566     begin
    3567       parsedValue := CSSColors[idx];
    3568       exit;
    3569     end;
    3570 
    3571     //CSS RGB notation
    3572     if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') or
    3573       (copy(str,1,4)='rgb ') or (copy(str,1,5)='rgba ') then
    3574     begin
    3575       values := SimpleParseFuncParam(str,error);
    3576       if (length(values)=3) or (length(values)=4) then
    3577       begin
    3578         if (values[0] <> '') and (values[0] <> '?') then
    3579            parsedValue.red := ParseColorValue(values[0], error)
    3580         else
    3581            missingValues := true;
    3582         if (values[1] <> '') and (values[1] <> '?') then
    3583            parsedValue.green := ParseColorValue(values[1], error)
    3584         else
    3585            missingValues := true;
    3586         if (values[2] <> '') and (values[2] <> '?') then
    3587            parsedValue.blue := ParseColorValue(values[2], error)
    3588         else
    3589            missingValues := true;
    3590         if length(values)=4 then
    3591         begin
    3592           if (values[3] <> '') and (values[3] <> '?') then
    3593           begin
    3594             val(values[3],alphaF,errPos);
    3595             if errPos <> 0 then
    3596             begin
    3597                parsedValue.alpha := 255;
    3598                error := true;
    3599             end
    3600             else
    3601             begin
    3602               if alphaF < 0 then
    3603                 parsedValue.alpha := 0 else
    3604               if alphaF > 1 then
    3605                 parsedValue.alpha := 255
    3606               else
    3607                 parsedValue.alpha := round(alphaF*255);
    3608             end;
    3609           end else
    3610             missingValues := true;
    3611         end else
    3612           parsedValue.alpha := 255;
    3613       end else
    3614         error := true;
    3615       exit;
    3616     end;
    3617 
    3618     //remove HTML notation header
    3619     if str[1]='#' then delete(str,1,1);
    3620 
    3621     //add alpha if missing (if you want an undefined alpha use '??' or '?')
    3622     if length(str)=6 then str += 'FF';
    3623     if length(str)=3 then str += 'F';
    3624 
    3625     //hex notation
    3626     if length(str)=8 then
    3627     begin
    3628       if copy(str,1,2) <> '??' then
    3629       begin
    3630         val('$'+copy(str,1,2),parsedValue.red,errPos);
    3631         if errPos <> 0 then error := true;
    3632       end else missingValues := true;
    3633       if copy(str,3,2) <> '??' then
    3634       begin
    3635         val('$'+copy(str,3,2),parsedValue.green,errPos);
    3636         if errPos <> 0 then error := true;
    3637       end else missingValues := true;
    3638       if copy(str,5,2) <> '??' then
    3639       begin
    3640         val('$'+copy(str,5,2),parsedValue.blue,errPos);
    3641         if errPos <> 0 then error := true;
    3642       end else missingValues := true;
    3643       if copy(str,7,2) <> '??' then
    3644       begin
    3645         val('$'+copy(str,7,2),parsedValue.alpha,errPos);
    3646         if errPos <> 0 then
    3647         begin
    3648           error := true;
    3649           parsedValue.alpha := 255;
    3650         end;
    3651       end else missingValues := true;
    3652     end else
    3653     if length(str)=4 then
    3654     begin
    3655       if str[1] <> '?' then
    3656       begin
    3657         val('$'+str[1],parsedValue.red,errPos);
    3658         if errPos <> 0 then error := true;
    3659         parsedValue.red *= $11;
    3660       end else missingValues := true;
    3661       if str[2] <> '?' then
    3662       begin
    3663         val('$'+str[2],parsedValue.green,errPos);
    3664         if errPos <> 0 then error := true;
    3665         parsedValue.green *= $11;
    3666       end else missingValues := true;
    3667       if str[3] <> '?' then
    3668       begin
    3669         val('$'+str[3],parsedValue.blue,errPos);
    3670         if errPos <> 0 then error := true;
    3671         parsedValue.blue *= $11;
    3672       end else missingValues := true;
    3673       if str[4] <> '?' then
    3674       begin
    3675         val('$'+str[4],parsedValue.alpha,errPos);
    3676         if errPos <> 0 then
    3677         begin
    3678           error := true;
    3679           parsedValue.alpha := 255;
    3680         end else
    3681           parsedValue.alpha *= $11;
    3682       end else missingValues := true;
    3683     end else
    3684       error := true; //string format not recognised
    3685   end;
    3686 
    3687 end;
    3688 
    3689 //this function returns the values that can be read from the string, otherwise
    3690 //it fills the gaps with the fallback values. The error boolean is True only
    3691 //if there was invalid values, it is not set to True if there was missing values.
    3692 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out
    3693   error: boolean): TBGRAPixel;
    3694 var missingValues: boolean;
    3695 begin
    3696   result := fallbackValues;
    3697   TryStrToBGRA(str, result, missingValues, error);
    3698 end;
    3699 
    3700 { Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. }
    3701 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;
    3702 var missingValues, error: boolean;
    3703 begin
    3704   result := BGRABlack;
    3705   TryStrToBGRA(str, result, missingValues, error);
    3706   if missingValues or error then result := DefaultColor;
    3707 end;
    3708 
    3709 function MapHeight(Color: TBGRAPixel): Single;
    3710 var intval: integer;
    3711 begin
    3712   intval := color.Green shl 16 + color.red shl 8 + color.blue;
    3713   result := intval*5.960464832810452e-8;
    3714 end;
    3715 
    3716 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
    3717 var intval: integer;
    3718 begin
    3719   if Height >= 1 then result := BGRA(255,255,255,alpha) else
    3720   if Height <= 0 then result := BGRA(0,0,0,alpha) else
    3721   begin
    3722     intval := round(Height*16777215);
    3723     result := BGRA(intval shr 8,intval shr 16,intval,alpha);
    3724   end;
    3725 end;
    3726 
    3727 {********************** Point functions **************************}
    3728 
    3729 function PointF(x, y: single): TPointF;
    3730 begin
    3731   Result.x := x;
    3732   Result.y := y;
    3733 end;
    3734 
    3735 function PointsF(const pts: array of TPointF): ArrayOfTPointF;
    3736 var
    3737   i: Integer;
    3738 begin
    3739   setlength(result, length(pts));
    3740   for i := 0 to high(pts) do result[i] := pts[i];
    3741 end;
    3742 
    3743 operator =(const pt1, pt2: TPointF): boolean;
    3744 begin
    3745   result := (pt1.x = pt2.x) and (pt1.y = pt2.y);
    3746 end;
    3747 
    3748 operator-(const pt1, pt2: TPointF): TPointF;
    3749 begin
    3750   result.x := pt1.x-pt2.x;
    3751   result.y := pt1.y-pt2.y;
    3752 end;
    3753 
    3754 operator-(const pt2: TPointF): TPointF;
    3755 begin
    3756   result.x := -pt2.x;
    3757   result.y := -pt2.y;
    3758 end;
    3759 
    3760 operator+(const pt1, pt2: TPointF): TPointF;
    3761 begin
    3762   result.x := pt1.x+pt2.x;
    3763   result.y := pt1.y+pt2.y;
    3764 end;
    3765 
    3766 operator*(const pt1, pt2: TPointF): single;
    3767 begin
    3768   result := pt1.x*pt2.x + pt1.y*pt2.y;
    3769 end;
    3770 
    3771 operator*(const pt1: TPointF; factor: single): TPointF;
    3772 begin
    3773   result.x := pt1.x*factor;
    3774   result.y := pt1.y*factor;
    3775 end;
    3776 
    3777 operator*(factor: single; const pt1: TPointF): TPointF;
    3778 begin
    3779   result.x := pt1.x*factor;
    3780   result.y := pt1.y*factor;
    3781 end;
    3782 
    3783 function PtInRect(const pt: TPoint; r: TRect): boolean;
    3784 var
    3785   temp: integer;
    3786 begin
    3787   if r.right < r.left then
    3788   begin
    3789     temp    := r.left;
    3790     r.left  := r.right;
    3791     r.Right := temp;
    3792   end;
    3793   if r.bottom < r.top then
    3794   begin
    3795     temp     := r.top;
    3796     r.top    := r.bottom;
    3797     r.bottom := temp;
    3798   end;
    3799   Result := (pt.X >= r.left) and (pt.Y >= r.top) and (pt.X < r.right) and
    3800     (pt.y < r.bottom);
    3801 end;
    3802 
    3803 function RectWithSize(left, top, width, height: integer): TRect;
    3804 begin
    3805   result.left := left;
    3806   result.top := top;
    3807   result.right := left+width;
    3808   result.bottom := top+height;
    3809 end;
    3810 
    3811 function VectLen(dx, dy: single): single;
    3812 begin
    3813   result := sqrt(dx*dx+dy*dy);
    3814 end;
    3815 
    3816 function VectLen(v: TPointF): single;
    3817 begin
    3818   result := sqrt(v.x*v.x+v.y*v.y);
    3819 end;
    3820 {$OPTIMIZATION OFF}  // Modif J.P  5/2013
    3821 function IntersectLine(line1, line2: TLineDef): TPointF;
    3822 var parallel: boolean;
    3823 begin
    3824   result := IntersectLine(line1,line2,parallel);
    3825 end;
    3826 {$OPTIMIZATION ON}
    3827 
    3828 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;
    3829 var divFactor: double;
    3830 begin
    3831   parallel := false;
    3832   //if lines are parallel
    3833   if ((line1.dir.x = line2.dir.x) and (line1.dir.y = line2.dir.y)) or
    3834      ((abs(line1.dir.y) < 1e-6) and (abs(line2.dir.y) < 1e-6)) then
    3835   begin
    3836        parallel := true;
    3837        //return the center of the segment between line origins
    3838        result.x := (line1.origin.x+line2.origin.x)/2;
    3839        result.y := (line1.origin.y+line2.origin.y)/2;
    3840   end else
    3841   if abs(line1.dir.y) < 1e-6 then //line1 is horizontal
    3842   begin
    3843        result.y := line1.origin.y;
    3844        result.x := line2.origin.x + (result.y - line2.origin.y)
    3845                /line2.dir.y*line2.dir.x;
    3846   end else
    3847   if abs(line2.dir.y) < 1e-6 then //line2 is horizontal
    3848   begin
    3849        result.y := line2.origin.y;
    3850        result.x := line1.origin.x + (result.y - line1.origin.y)
    3851                /line1.dir.y*line1.dir.x;
    3852   end else
    3853   begin
    3854        divFactor := line1.dir.x/line1.dir.y - line2.dir.x/line2.dir.y;
    3855        if abs(divFactor) < 1e-6 then //almost parallel
    3856        begin
    3857             parallel := true;
    3858             //return the center of the segment between line origins
    3859             result.x := (line1.origin.x+line2.origin.x)/2;
    3860             result.y := (line1.origin.y+line2.origin.y)/2;
    3861        end else
    3862        begin
    3863          result.y := (line2.origin.x - line1.origin.x +
    3864                   line1.origin.y*line1.dir.x/line1.dir.y -
    3865                   line2.origin.y*line2.dir.x/line2.dir.y)
    3866                   / divFactor;
    3867          result.x := line1.origin.x + (result.y - line1.origin.y)
    3868                  /line1.dir.y*line1.dir.x;
    3869        end;
    3870   end;
    3871 end;
    3872 
    3873 { Check if a polygon is convex, i.e. it always turns in the same direction }
    3874 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean;
    3875 var
    3876   positive,negative,zero: boolean;
    3877   product: single;
    3878   i: Integer;
    3879 begin
    3880   positive := false;
    3881   negative := false;
    3882   zero := false;
    3883   for i := 0 to high(pts) do
    3884   begin
    3885     product := (pts[(i+1) mod length(pts)].x-pts[i].x)*(pts[(i+2) mod length(pts)].y-pts[i].y) -
    3886                (pts[(i+1) mod length(pts)].y-pts[i].y)*(pts[(i+2) mod length(pts)].x-pts[i].x);
    3887     if product > 0 then
    3888     begin
    3889       if negative then
    3890       begin
    3891         result := false;
    3892         exit;
    3893       end;
    3894       positive := true;
    3895     end else
    3896     if product < 0 then
    3897     begin
    3898       if positive then
    3899       begin
    3900         result := false;
    3901         exit;
    3902       end;
    3903       negative := true;
    3904     end else
    3905       zero := true;
    3906   end;
    3907   if not IgnoreAlign and zero then
    3908     result := false
    3909   else
    3910     result := true;
    3911 end;
    3912 
    3913 { Check if two segments intersect }
    3914 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    3915 var
    3916   seg1: TLineDef;
    3917   seg1len: single;
    3918   seg2: TLineDef;
    3919   seg2len: single;
    3920   inter: TPointF;
    3921   pos1,pos2: single;
    3922   para: boolean;
    3923 
    3924 begin
    3925   { Determine line definitions }
    3926   seg1.origin := pt1;
    3927   seg1.dir := pt2-pt1;
    3928   seg1len := sqrt(sqr(seg1.dir.X)+sqr(seg1.dir.Y));
    3929   if seg1len = 0 then
    3930   begin
    3931     result := false;
    3932     exit;
    3933   end;
    3934   seg1.dir *= 1/seg1len;
    3935 
    3936   seg2.origin := pt3;
    3937   seg2.dir := pt4-pt3;
    3938   seg2len := sqrt(sqr(seg2.dir.X)+sqr(seg2.dir.Y));
    3939   if seg2len = 0 then
    3940   begin
    3941     result := false;
    3942     exit;
    3943   end;
    3944   seg2.dir *= 1/seg2len;
    3945 
    3946   //obviously parallel
    3947   if seg1.dir = seg2.dir then
    3948     result := false
    3949   else
    3950   begin
    3951     //try to compute intersection
    3952     inter := IntersectLine(seg1,seg2,para);
    3953     if para then
    3954       result := false
    3955     else
    3956     begin
    3957       //check if intersections are inside the segments
    3958       pos1 := (inter-seg1.origin)*seg1.dir;
    3959       pos2 := (inter-seg2.origin)*seg2.dir;
    3960       if (pos1 >= 0) and (pos1 <= seg1len) and
    3961          (pos2 >= 0) and (pos2 <= seg2len) then
    3962         result := true
    3963       else
    3964         result := false;
    3965     end;
    3966   end;
    3967 end;
    3968 
    3969 { Check if a quaduadrilateral intersects itself }
    3970 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    3971 begin
    3972   result := DoesSegmentIntersect(pt1,pt2,pt3,pt4) or DoesSegmentIntersect(pt2,pt3,pt4,pt1);
    3973612end;
    3974613
     
    4110749        begin
    4111750          for i := 0 to 6 do dwords[i] := LEtoN(dwords[i]);
    4112           if (dwords[0] = 0) and (dwords[1] <= expectedFileSize) and (dwords[5] <= expectedFileSize) and
    4113              (dwords[9] <= expectedFileSize) and
     751          if (dwords[0] = 0) and (dwords[1] <= maxFileSize) and (dwords[5] <= maxFileSize) and
     752             (dwords[9] <= maxFileSize) and
    4114753            (dwords[6] = 0) then inc(scores[ifLazPaint],2);
    4115754        end;
     
    4199838    end;
    4200839
     840    if (copy(magicAsText,1,4) = 'oXo ') then
     841    begin
     842      inc(scores[ifPhoxo],1);
     843      if (magic[4] = 1) and (magic[5] = 0) and (magic[6] = 0) and (magic[7] = 0) then
     844        inc(scores[ifPhoxo],1);
     845    end;
     846
    4201847    DetectLazPaint;
    4202848
     
    4233879
    4234880  ASuggestedExtensionUTF8:= UTF8LowerCase(ASuggestedExtensionUTF8);
    4235   if (ASuggestedExtensionUTF8 <> '') and (UTF8Copy(ASuggestedExtensionUTF8,1,1) <> '.') then
     881  if (ASuggestedExtensionUTF8 <> '') and (ASuggestedExtensionUTF8[1] <> '.') then //first UTF8 char is in first pos
    4236882    ASuggestedExtensionUTF8 := '.'+ASuggestedExtensionUTF8;
    4237883
     
    4254900function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
    4255901var ext: string;
     902  posDot: integer;
    4256903begin
    4257904  result := ifUnknown;
    4258905
    4259906  ext := ExtractFileName(AFilenameOrExtensionUTF8);
    4260   if pos('.', ext) <> 0 then ext := ExtractFileExt(ext) else ext := '.'+ext;
     907  posDot := LastDelimiter('.', ext);
     908  if posDot <> 0 then ext := copy(ext,posDot,length(ext)-posDot+1)
     909  else ext := '.'+ext;
    4261910  ext := UTF8LowerCase(ext);
    4262911
     
    4274923  if (ext = '.tif') or (ext = '.tiff') then result := ifTiff else
    4275924  if (ext = '.xwd') then result := ifXwd else
    4276   if (ext = '.xpm') then result := ifXPixMap;
     925  if (ext = '.xpm') then result := ifXPixMap else
     926  if (ext = '.oxo') then result := ifPhoxo;
     927end;
     928
     929function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
     930begin
     931  case AFormat of
     932    ifJpeg: result := 'jpg';
     933    ifPng: result := 'png';
     934    ifGif: result := 'gif';
     935    ifBmp: result := 'bmp';
     936    ifIco: result := 'ico';
     937    ifPcx: result := 'pcx';
     938    ifPaintDotNet: result := 'pdn';
     939    ifLazPaint: result := 'lzp';
     940    ifOpenRaster: result := 'ora';
     941    ifPsd: result := 'psd';
     942    ifTarga: result := 'tga';
     943    ifTiff: result := 'tif';
     944    ifXwd: result := 'xwd';
     945    ifXPixMap: result := 'xpm';
     946    ifBmpMioMap: result := 'bmp';
     947    else result := '?';
     948  end;
    4277949end;
    4278950
     
    4306978  if AFormat = ifPng then
    4307979  begin
    4308     result := TFPWriterPNG.Create;
    4309     TFPWriterPNG(result).Indexed := false;
    4310     TFPWriterPNG(result).WordSized := false;
    4311     TFPWriterPNG(result).UseAlpha := AHasTransparentPixels;
     980    result := TBGRAWriterPNG.Create;
     981    TBGRAWriterPNG(result).UseAlpha := AHasTransparentPixels;
    4312982  end else
    4313983  if AFormat = ifBmp then
     
    4328998initialization
    4329999
    4330   InitGamma;
    4331   {$DEFINE INCLUDE_COLOR_LIST}
     1000  {$DEFINE INCLUDE_INIT}
     1001  {$I bgrapixel.inc}
     1002
     1003  {$DEFINE INCLUDE_INIT}
    43321004  {$I csscolorconst.inc}
     1005 
    43331006  DefaultBGRAImageWriter[ifJpeg] := TFPWriterJPEG;
    4334   DefaultBGRAImageWriter[ifPng] := TFPWriterPNG;
     1007  DefaultBGRAImageWriter[ifPng] := TBGRAWriterPNG;
    43351008  DefaultBGRAImageWriter[ifBmp] := TFPWriterBMP;
    43361009  DefaultBGRAImageWriter[ifPcx] := TFPWriterPCX;
     
    43461019finalization
    43471020
    4348   CSSColors.Free;
    4349   VGAColors.Free;
    4350 
     1021  {$DEFINE INCLUDE_FINAL}
     1022  {$I csscolorconst.inc}
     1023
     1024  {$DEFINE INCLUDE_FINAL}
     1025  {$I bgrapixel.inc}
    43511026end.
Note: See TracChangeset for help on using the changeset viewer.